Archives

Archives / 2006 / February
  • Migrating to the GridView and ObjectDataSource

    I "adopted" a small app recently written in .NET 1.1, which I migrated to .NET 2.0 painlessly. It's only about 16 pages and user controls, so I wasn't expecting too many problems with the actual migration into VS 2005, and had none.

    Of course, there's a difference between an app that runs against .NET 2.0 and a true .NET 2.0 app. To this end, I converted the main table of data on one of the main pages from a DataGrid to a GridView control. This too was rather painless--mainly replacing "Column" and "Item" in class/property names with "Field" and "Row".

    Converting it from doing data binding manually in code to using an ObjectDataSource in markup was a bit more work, though. Deleting automatically was a dream--I specified the delete method we already had, and removed the code that manually implemented delete against the DataGrid from the code-behind, and that was all it took. (This, I might add, has been my overall experience with ASP.NET 2.0--add a new feature like a master page or theme to your web site, delete a bunch of code--which is a lot of fun.) However, for selecting data to display in the table, I ran into some surprises, as well as when implementing sorting, which was one of the new features I was adding to the app during this development cycle.

    - A pattern used in this app is lightweight objects used mainly to store data, and separate objects to encapsulate the logic to instantiate these objects from database query results. As far as I could determine from the docs and Google searches, the ObjectDataSource doesn't support this. If you want to bind custom objects, you must do so using a method on the same class that you use to store the data, and specify that class using the TypeName property. To me this seemed a bit short-sighted; why doesn't the ObjectDataSource take two class names, one for the type to be bound, and another for the class with the method(s) to do so, which defaults to the bound type if you don't specify it? I created a short method on my lightweight class that just calls the other method, and it worked fine.

    - One thing touted about the GridView over the DataGrid was how easy sorting and paging and so forth go. Sure, the DataGrid supports sorting and paging, one developer evangelist told a large group of us, but you have to write much of the code to do the sorting and paging yourself. With the GridView, you get it free. Well, not in this case. Turn on sorting, click on a column header, and it gives you this (very helpful and descriptive, at least) exception:


    [NotSupportedException: The data source 'MyDataSource' does not support sorting with IEnumerable data. Automatic sorting is only supported with DataView, DataTable, and DataSet.]
       System.Web.UI.WebControls.ObjectDataSourceView.CreateEnumerableData(Object dataObject, DataSourceSelectArguments arguments) +425
       System.Web.UI.WebControls.ObjectDataSourceView.ExecuteSelect(DataSourceSelectArguments arguments) +2652
       System.Web.UI.DataSourceView.Select(DataSourceSelectArguments arguments, DataSourceViewSelectCallback callback) +13
       System.Web.UI.WebControls.DataBoundControl.PerformSelect() +140
       System.Web.UI.WebControls.BaseDataBoundControl.DataBind() +68
       System.Web.UI.WebControls.GridView.DataBind() +5
       System.Web.UI.WebControls.BaseDataBoundControl.EnsureDataBound() +61
       System.Web.UI.WebControls.GridView.OnPreRender(EventArgs e) +24
       System.Web.UI.Control.PreRenderRecursiveInternal() +88
       System.Web.UI.Control.PreRenderRecursiveInternal() +171
       System.Web.UI.Control.PreRenderRecursiveInternal() +171
       System.Web.UI.Control.PreRenderRecursiveInternal() +171
       System.Web.UI.Control.PreRenderRecursiveInternal() +171
       System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +5731



    So not only did I have to write a pass-through method to the one I already had to retrieve the data, I now had to change it from a simple pass-through to duplicate the functionality of my original select method, returning a DataTable instead of a custom collection. Once I did that, I did get my sorting without additional work, but it was disappointing that it couldn't sort my custom collection without me writing a manual sort method; I had hoped it would just sort the data after pulling it out of the collection, the same way we'd sort any list of numbers or strings.

    This supports my initial impression of the new ObjectDataSource, SqlDataSource, etc. classes, that while it might be great for people who are more comfortable w/ XML/HTML markup than with C# code, it's not something I'm going to use everywhere in my applications instead of binding data with a couple lines of C# code. I'm far from writing them off, though, especially if they're used widely enough that they get filled out a bit more in future versions of .NET.

  • Convert.ChangeType doesn't handle nullables

    I had an assembly, originally written against .NET 1.1 and now running on .NET 2.0, that tried to convert a boxed DateTime object to a Nullable<DateTime> (DateTime?) object, using the Convert.ChangeType method. When this code runs, I get this exception:


    [InvalidCastException: Invalid cast from 'System.DateTime' to ' System.Nullable`1[[System.DateTime, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'.]
      System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider) +864
      System.DateTime.System.IConvertible.ToType(Type type, IFormatProvider provider) +36
      System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider) +433
      System.Convert.ChangeType(Object value, Type conversionType) +38



    Since a DateTime can be assigned to a nullable DateTime, it didn't make sense to me why it couldn't be converted to one, by calling Convert.ChangeType(Object, Type) with a Nullable as the second parameter. A Google search showed that Paul Wilson, Cameron Beccario, and others ran into this too during the beta timeframe.

    The CLR team at Microsoft confirmed that this is a known problem:

    Yes, Convert is not very extensible and was designed around a fixed set of types to address a specific functionality requirement for VB. System.ComponentModel has a more comprehensive data type conversion model, and I believe does deal with nullable.

    The docs showed a lot of System.ComponentModel classes for conversion, e.g. DecimalConverter, but unfortunately I couldn't use those in this particular case, since it doesn't support the late-bound casting that code was doing. It was disappointing that this particular case wasn't handled, but it's probably a rare case, and it's understandable given some of the changes to nullables that came so late in the game.

    My workaround in the short term was to not use the Nullable type for that particular method, until I could dig into System.ComponentModel more to find an alternative, or a patch is released for Convert.ChangeType to handle nullables.

    Edit: I've now implemented a wrapper that handles nullables correctly.

  • Web projects and code-behind in Visual Studio 2005

    Some of my coworkers and I balked at what giving up web project files in VS 2005 also meant we'd give up--default namespaces, ease of referencing classes in the web site (not just App_Code stuff but code-behind files as well), and so on.

    Have no fear--they're coming back.

    On another note, what also came back between the beta and release is the old code-behind model. Now your code-behind (.aspx.cs) and your web form (.aspx) are no longer compiled together as partial classes--your web form inherits from the code-behind as it did in VS.NET 2003.

  • HttpRuntime.Cache vs. HttpContext.Current.Cache

    Here's a development tip I came across on one of the ASP.NET discussion lists I'm on, at AspAdvice.com.

    Original question:
    Is there a difference in accessing the Cache of an application when calling HttpRuntime.Cache vs. HttpContext.Current.Cache?  I "think" I remember reading about a difference in the two a few years ago, but I don't remember the specifics.  This assumes that I am within a web application.

    Answer from Rob Howard:
    HttpRuntime.Cache is the recommended technique.

    Calling the HttpContext does some additional look-ups as it has to resolve the current context relative to the running thread.

    I use HttpContext.Current in a lot of the code I write too; but touching it as little as possible. Rather than calling HttpContext.Current repeatedly it's best to hang onto a reference and pass it around (when possible).

    If you're in a component HttpRuntime.Cache is still the recommendation.

    That said... the differences in performance are not going to be noticeable in 99% of the applications many of us write. The cases where it is going to matter is the 1% (or less) where you're trying to squeeze every last drop of performance out of you system. But this is a *minor* performance tweak, e.g. eliminating a database call, web service call or other out-of-process call in the application is definitely a better place to spend optimizing code.

    For example, if you have 5 database calls in a particular code path reducing (or even optimizing) the queries is a much better use of time.

    So yes, HttpRuntime.Cache is recommended but likely won't make a difference in most applications.

    Another reply from James Shaw at CoverYourASP.NET:
    I discovered another *great* reason to use HttpRuntime too, when writing my unit tests - HttpRuntime.Cache is always available, even in console apps like nunit!

    http://www.coveryourasp.net/UnittestingandCaching

    I never use HttpContext anymore, even in class libraries.