Friday, May 11, 2012

Generic XML attribute parser

I may just end up using something like LINQ to XSD for this (if possible), but I wanted to see how easy it would be to write an XML parser that takes in an element of any kind, and returns a strongly-typed value. Here's what I came up with:

private T parseAttr<T>(XElement x, string elemName)
    Type type = typeof(T);
    T parsedValue;

        parsedValue = type.IsEnum ? (T)Enum.Parse(typeof(T), x.Element(elemName).Value) : (T)Convert.ChangeType(x.Element(elemName).Value, typeof(T));
        parsedValue = default(T);

    return parsedValue;

which can be used like this (example is from a LINQ projection):

let picks =
    from pick in playerPicks.Element("PlayerList").Descendants("Player").Elements("Picks")
    select new PlayerPick
        TickerSymbol = parseAttr<string>(pick, "TickerSymbol"),
        Status = parseAttr<Status>(pick, "Status"),
        PickCall = parseAttr<PickCall>(pick, "PickCall"),
        StartPrice = parseAttr<double>(pick, "StartPrice"),
        EndPrice = parseAttr<double>(pick, "EndPrice"),
        StartDate = parseAttr<DateTime>(pick, "StartDate"),
        EndDate = parseAttr<DateTime>(pick, "EndDate")

It works on regular types as well as enums. I'm not completely happy with it - "Convert" is generally less useful than .Parse(), but I couldn't figure out how to use Parse with generics on static methods (i.e. (T).Parse()).

Also, it's hardly bulletproof, but I think I may validate the whole XML document first anyway if it doesn't take too long. Not sure if I'll end up using this or not - I was just amazed about how easy this was to write using generics, and thought it might be useful to somebody.

Monday, May 7, 2012

Renaming an ASP.NET MVC solution in Visual Studio

The Platinum Bay blog has a good writeup on how to rename a Visual Studio project correctly (so that the underlying folders are renamed and re-hooked up). 

But I still ran into problems - an MVC project that I renamed returned the following error :
Multiple types were found that match the controller named 'Home'. This can happen if the route that services this request ('{controller}/{action}/{id}') does not specify namespaces to search for a controller that matches the request. If this is the case, register this route by calling an overload of the 'MapRoute' method that takes a 'namespaces' parameter. 
The request for 'Home' has found the following matching controllers:
(new project namespace).Controllers.HomeController
(old project namespace).Controllers.HomeController
A file system search even turned up nothing, but I finally figured out the solution while reading a great in-depth article on controllers by Scott Allen, where he states:
...if you tell the factory to look for a "Home" controller, the factory can return a newly instantiated instance of a HomeController class regardless of the namespace or assembly it lives in—as long as it implements IController
If you've build the old project, you'll probably have old debugging dlls in the project's "bin" folder, so MVC will search and find those. Clear them out and rebuild, and everything should work. By the way, you could actually just follow the instructions in the error, and specify your route like this:

                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                namespaces: new string[] {"(new project namespace)"}

but it's cleaner to delete the old .dlls, as they could potentially cause you other problems as well. I'm very surprised that even the VS 11 beta still doesn't rename solutions correctly out of the box.