Thursday, November 09, 2006

ObjectDataSource doesn't respect culture on update

I've recently run into an issue with ObjectDataSource, GridView, and alternate cultures. My present client has a Candian office and the project requires selection of a date from a dropdown. When the data binding to the list of possible dates occurs, the data source uses the current culture to format the dates, so I end up with a dropdown whose ListItems have values like "29/10/2006" and "30/10/2006" since they do their dates backwards up there. This is completely expected and is definitely how things should work.

The problem arises when the user clicks the update button and we have to turn that string (dropdown.SelectedValue) back into a DateTime. Here the ObjectDataSource messes up and converts the string back using the InvariantCulture, which throws an exception since "30/10/2006" isn't a valid date.

This Microsoft Connect link summarizes the issue a bit more.

I was unhappy with the workaround posted there so I figured out a better one. Basically we just do the DateTime conversion ourselves, instead of letting the ObjectDataSource handle it.

1. Handle the ObjectDataSource Updating event.
2. In the handler, replace the string input parameter with its culturally aware DateTime equivalent, using something like:

TypeConverter converter =
    TypeDescriptor.GetConverter(typeof(DateTime));
e.InputParameters["DateField"] =
    converter.ConvertFromString(
        e.InputParameters["DateField"] as string);


And that's it.

Monday, October 02, 2006

Enabling Sane Project and Solution Structure

One of the constant pains of Visual Studio 2005 and Visual SourceSafe is the strong tie between solution, file system, and VSS structures. If you only ever have a single large solution with all of your projects underneath it, this never becomes apparent. If, however, you have several core components that need to be shared across several distinct solutions (systems/applications/whatever), things get gross. Visual Studio likes the solution to be at a higher level in VSS and the file system than all the projects contained in it. Once you have a project that needs to be in more than one solution (i.e., have more than one parent folder), this is impossible. Ideally I'd like my file system to look something like:

c:\projects (all solution files live here)
c:\projects\sharedlib1
c:\projects\sharedlib2
...
c:\projects\websites\site1
c:\projects\websites\site2
etc.


with a similar VSS structure:
$/ (all solutions here)
$/sharedlib1
$/sharedlib2
...


Here's what I do when creating a new solution and project to make this work:

  1. Create a blank solution
    File->New Project
    Other Project Types/Visual Studio Solutions/Blank Solution

    Don't check "Add to Source Control"
    Location: c:\projects
    Name: whatever (application name)

  2. File System Surgery
    File->Save As...
    c:\projects\whatever.sln
    Delete the subdirectory under projects (should be c:\projects\whatever\)

  3. Add solution to VSS
    Right click on sln, Add to Source Control
    Leave name alone (will be whatever.root)
    Location $/

  4. VSS Surgery
    Start the VSS Client
    Drag and drop Whatever.sln and Whatever.vssscc from c:\projects into $\ in VSS
    In Visual Studio, go to File->Source Control->Change Source Control
    There should be line line listed that says "Solution: Whatever.sln"
    Click the elipsis ("...") in the Server Binding column
    Pick $/
    Hit Ok twice
    In VSS, right click on $/whatever.root and pick delete (check the box to destroy permanently)
    Close VSS



When adding new projects, always choose "C:\projects" for Location
When adding new websites, always choose "C:\projects\websites" for Location
and they will automatically be added to the right place in VSS

And that's it. Basically I change the binding root of the solution from the automatically created "whatever.root" folder to its parent, allowing multiple solutions to live in the same directory and in effect share the same binding root.

Monday, September 18, 2006

Lightweight Promotable Transactions and Connection Pooling

We're moving an existing system from a SQL 2000 backend to SQL 2005. The system uses a custom persistence framework and System.Transactions.TransactionScope for all transactions. The framework was developed with the best practices for connection pooling in mind. Basically, connections are cheap; open them when you need them and close them when you're done. This results in a connection for each type being fetched during a request. For example, a Customer with an Address would require 2 connections, one to fetch the Customer and one to fetch the dependent Address.

When SQL 2005 enters the picture, however, we have a problem. TransactionScope and SQL 2005 are smart enough to realize that not all transactions are distributed, so they start out as local ADO transactions and are supposed to be seamlessly promoted when necessary. There are 2 problems we've run into so far. The first is that it's way too easy to trigger a promotion. Although I guess it makes sense, opening a second connection to the same data source always triggers a promotion. The second problem arises if the first connection is busy (say, reading a Customer object from a DataReader) when the second connection is open. This causes an immediate failure since the first connection is blocked, preventing a promotion.

Although you can work around the blocked connection problem with a new SQL 2005 feature, Multiple Active Result Sets (MARS), it still seems a bit gross. I may end up rewriting bits of the persistence framework to properly share a single connection, queuing the objects to be loaded and then working through them rather than having multiple connections with multiple DataReaders going at once.

Monday, September 04, 2006

HttpListenerContext is not serializable

Today I started working on my own subclass of HttpWorkerRequest so that my webserver could handle POSTs and binary data like rails.png on the Welcome Aboard page. After writing all the code to handle the necessary overrides I went to actually run the thing and discovered the troubling fact in my subject line. HttpListenerContext is not serializable. Since my host runs in a separate AppDomain, this is a big problem.

I thought about trying to make some serializable dummy class to shuttle data back and forth, but then I came across Aaron Skonnard's Service Station article in MSDN Magazine here where he hit the same thing. His solution was just to move the HttpListener into the host's AppDomain, which is exactly the right thing to do. After doing that, everything works great. I successfully served the whole Rails Welcome Aboard page including the png (and the AJAX link for environment info), as well as a few test aspx pages with postbacks. There are still some rough edges (no default page handling, untested auth, etc.), but I think this is good enough to move on to working with a pool of Ruby processes for handling Rails.

Sunday, September 03, 2006

More Http.sys and Rails

Magically I found some time to work on my web server for Rails based on Http.sys. In the interest of having something definitely working, I went down the path of creating a basic CGI server. Given a virtual directory and a physical path to a Rails public folder, I can now successfully serve pages, with a few caveats.

One minor thing is that I've only tested a few pages in my first ever Rails app, so I'm sure there are things I've missed. I haven't attempted to support things like auth or https yet, but those should definitely be possible.

More importantly, I'm using HttpRuntime.ProcessRequest for serving static content, but I'm only using SimpleWorkerRequest at the moment. This means binary files as well as POSTs and things don't work. It looks like I'm going to have to subclass HttpWorkerRequest myself to make things work. I've looked at the Cassini source for how they do it (and to see if I could just use that), so this shouldn't be too bad. Not exactly fun though.

Once I get those things handled I'll move onto a pooled interpreter approach and leave the horrible performance of CGI behind. Seeing the Rails 'Welcome aboard' page served from my code was nice, even if the images didn't work yet. Soon.

Saturday, August 19, 2006

FP on the brain

I did a presentation for our internal user group about languages other than C# including SML, Scheme, and Ruby. I was attemping to illustrate functional programming, code as data, lazy evaluation, and dynamic typing to a group who had mostly only dealt with C#, partly to talk about elements from those sources that LINQ will incorporate and partly just to share my enthusiasm.

I hadn't touched SML in quite awhile, but it was really easy to pick back up and still was just as much fun to write. It's powerful, expressive, and has a nice, clean syntax. It might fit really well in a middle-tier type setting, since that would side step the need for strongly typed database access or a web service toolkit. I need to investigate SML.NET and F# some more as potential avenues to make this happen. The OCAML syntax of F# bugs me, but I read a cool paper linked off of LtU about its template/meta-programming capabilities which seemed really promising.

The more I learn about Ruby, the more I like it as a language, too. The dynamic typing bit still scares me some (especially after SML), but metaprogramming as a means to eliminate boilerplate junk and also to build internal DSLs is just cool. The interop with .net story isn't quite there yet, but I bet it will be soon. For the time being, I'm going to play around with using Ruby to generate C#/XML/WSDL/etc.

Tuesday, August 08, 2006

WF, ASP.NET, and transactions

My current project has some pretty simple business logic that seemed like it would be a good fit for WF. I was looking for a good excuse to try it out for real so I started investigating using it and immediately ran into several key problems.

The first is that there aren't many samples available relating to using WF with an ASP.NET host. Some of this is because it's still beta, but the only ASP.NET stuff available is either hopelessly trivial (start a workflow on a button click and then wait for it to finish before returning) or just not analogous to a real situation (no error handling, transactions, etc.). Additionally, you have to use a manual scheduler as opposed to the default one. I'm trying to get WF to replace business logic, so it would live in the service layer. I'd need extra code there to check whether we were running with the manual scheduler or not (in order to call RunWorkflow if necessary), which seems gross.

The other big issue is that it doesn't seem possible to have workflows participate in a transaction that is initiated by the host. My scenario involves keeping track of many running instances that all need to be finished and exported at once. This would involve iterating over each instance, sending it a specific event, and then, once all instances have been notified, running a stored proc (or something) once to do the export. All of that needs to be in one transaction, but it appears this isn't possible.

I might have to not use WF for this scenario if I can't find answers to those questions. I'm also concerned that if there aren't good answers, it may severely limit the applicability of WF.

Tuesday, August 01, 2006

More WinFX WF Beta 2 fun

Although I got my installation problems resolved on my laptop, my work machine continued to have problems with Workflow Foundation. When starting Visual Studio I'd get the following error:

Package Load Failure
Package 'Microsoft.Workflow.VSDesigner.DesignerPackage, Microsoft.Workflow.VSDesigner, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' has failed to load properly ( GUID = {FD526733-BD72-4F81-BEEC-BEB06E2AF59F} ).

I found some people who had similar problems that arose from using an incompatible WinFX beta with a pre-release version of Visual Studio, but nothing with RTM. While tracking down the problem I discovered how to generally troubleshoot Visual Studio package load failures. If you start Visual Studio from the command line with the "/log" option ("devenv /log"), an xml log file is created in "c:\Documents and Settings\[username]\Application Data\Microsoft\VisualStudio\8.0\" called "ActivityLog.xml".

Mine showed 1 warning (CheckPackageSignature failed; invalid Package Load Key {FD526733-BD72-4F81-BEEC-BEB06E2AF59F}) and 2 errors (SetSite failed for package [Microsoft.Workflow.VSDesigner.DesignerPackage, Microsoft.Workflow.VSDesigner, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35] and End package load).

Searching on those GUIDs and more detailed error messages I was able to find someone with a similar problem that resulted from having an old Add In file pointing to a location that no longer existed. Sure enough, in "C:\Documents and Settings\[username]\Application Data\Microsoft\MSEnvShared\Addins" I had a ".AddIn" file I had created ages ago in a folder that no longer exists. Once I deleted this file, Visual Studio loaded just fine.

I have no idea why this seemingly unrelated thing caused WF to fail, but at least it's resolved.

Friday, July 21, 2006

Quick .NET Web Reference Note

Apparently setting the URL property of a web service proxy class resets the Credentials property to the default. This means that if you're setting both, make sure to set URL and then Credentials. Intuitively I guess this would make sense if the pattern of use involved reusing the same proxy for calls to identical services in different locations. Given that in reality you usually create the proxy, optionally set properties like URL and Credentials, make some calls, and ditch the proxy, I don't know why that behavior exists.

Sunday, July 16, 2006

Http.sys and Rails

I made some good progress today on my .Net web server for Rails based on Http.sys. Ultimately I want it to support a pool of ruby interpretters, URL rewriting, and serving regular files (static and dynamic (aspx)) via http.sys. I already have the basic non-Ruby file serving in place through the magic of HttpRuntime.ProcessRequest. Right now I'm working on some simple wrapper classes to handle CGI. Once I have that done, I'll add URL rewriting so I can get Rails running. The last step is the process pool, so things actually run quickly.

Initially I'm going to try using a TCP socket as a control channel where the server can signal the child process to begin working and the child can tell the server when it's done. FastCGI looks like it actually multiplexes control information along with standard input and output over the TCP. Perhaps there's a good reason for that, but I'm going to start with the server writing to and reading from the child's standard in and out directly, orchestrated by the control channel.

My goals are both to learn about Rails from a development and solution architecture perspective and to simply the process of getting a production-ready Rails server setup on Windows. Rails can definitely have a place in a real enterprise architecture, but requiring clients with Windows servers to install and maintain apache (or lighttpd, etc.) in addition to IIS is a big hurdle. Having something based on http.sys should mitigate a lot of this (unified instrumentation, etc.).

Thursday, July 13, 2006

WinFX WF Beta 2 installation problems resolved

Over the course of messing with a new version of LINQ and other things, my WinFX Beta 2 install somehow got corrupted. When I tried to load the Workflow Foundation Designer I got the following error message:

Method 'ElementRemoved' in type 'TypeProviderHost' from assembly 'Microsoft.Workflow.VSDesigner, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' does not have an implementation.

I tried removing everything (Visual Studio Extensions for WF, Orcas CTP, Windows SDK, WinFX Runtime) and reinstalling and the same thing happened. Finally I was able to find this which solved my problem, even though the original question is about Beta 1. Basically, if you have 2 versions of EnvDTE80 in the GAC, remove the one with the lower Version (right click->Properties->Version tab).

Now everything is working and I can get back to preparing for my upcoming Devcare presentation.

Sunday, July 09, 2006

WEBrick Issues

I spent several hours today trying to get the most basic Rails site possible running on my laptop and only recently succeeded. Initially, something prevented remote Ruby Gem installs from working at all (gem install would just hang), so I had to chase down all the gem files and download them manually. When I finally got all that done, WEBRick just wouldn't respond to requests. Everything started ok, but when I hit http://localhost:3000/ in my browser I got "Waiting for..." and nothing else. I eventually had to kill the cmd.exe process to even get WEBrick to exit.

After lots of running in circles (even attempting to install mongrel which proved difficult since I couldn't find a win32-service.gem >= version 0.5.0 anywhere) I discovered the issue. A few weeks ago I did a half a day's work for a remote client that required secure VPN access. I had to install some software that worked with the little key generator they mailed me. After ruling out all sorts of firewall and connectivity problems I thought of this Juniper Networks stuff they had me install and once I removed it everything worked fine. Both WEBrick and my remote gem installs functioned perfectly. Whatever it was doing was enough to mess up Ruby's network code, without bothering my browser or any .NET apps I wrote.

Very odd.

Code Goes Here


public void Solve()
{
//code goes here
}