Creating a Dynamic DataRow for easier DataRow Syntax

Posted by Rick Strahl on West-Wind See other posts from West-Wind or by Rick Strahl
Published on Thu, 24 Nov 2011 11:13:23 GMT Indexed on 2011/11/24 17:53 UTC
Read the original article Hit count: 681

Filed under:
|

I've been thrown back into an older project that uses DataSets and DataRows as their entity storage model. I have several applications internally that I still maintain that run just fine (and I sometimes wonder if this wasn't easier than all this ORM crap we deal with with 'newer' improved technology today - but I disgress) but use this older code. For the most part DataSets/DataTables/DataRows are abstracted away in a pseudo entity model, but in some situations like queries DataTables and DataRows are still surfaced to the business layer.

Here's an example. Here's a business object method that runs dynamic query and the code ends up looping over the result set using the ugly DataRow Array syntax:

public int UpdateAllSafeTitles()
{
    int result = this.Execute("select pk, title, safetitle from " + Tablename + " where EntryType=1", "TPks");
    if (result < 0)            
        return result;

    result = 0;

    foreach (DataRow row in this.DataSet.Tables["TPks"].Rows)
    {
        string title = row["title"] as string;
        string safeTitle = row["safeTitle"] as string;
        int pk = (int)row["pk"];

        string newSafeTitle = this.GetSafeTitle(title);
        if (newSafeTitle != safeTitle)
        {
            this.ExecuteNonQuery("update " + this.Tablename + " set safeTitle=@safeTitle where pk=@pk",
                                 this.CreateParameter("@safeTitle",newSafeTitle),
                                 this.CreateParameter("@pk",pk) );
            result++;
        }
    }

    return result;
}

The problem with looping over DataRow objecs is two fold: The array syntax is tedious to type and not real clear to look at, and explicit casting is required in order to do anything useful with the values. I've highlighted the place where this matters.

Using the DynamicDataRow class I'll show in a minute this code can be changed to look like this:

public int UpdateAllSafeTitles()
{
    int result = this.Execute("select pk, title, safetitle from " + Tablename + " where EntryType=1", "TPks");
    if (result < 0)            
        return result;

    result = 0;

    foreach (DataRow row in this.DataSet.Tables["TPks"].Rows)
    {
        dynamic entry = new DynamicDataRow(row);

        string newSafeTitle = this.GetSafeTitle(entry.title);
        if (newSafeTitle != entry.safeTitle)
        {
            this.ExecuteNonQuery("update " + this.Tablename + " set safeTitle=@safeTitle where pk=@pk",
                                 this.CreateParameter("@safeTitle",newSafeTitle),
                                 this.CreateParameter("@pk",entry.pk) );
            result++;
        }
    }

    return result;
}

The code looks much a bit more natural and describes what's happening a little nicer as well.

Well, using the new dynamic features in .NET it's actually quite easy to implement the DynamicDataRow class.

Creating your own custom Dynamic Objects

.NET 4.0 introduced the Dynamic Language Runtime (DLR) and opened up a whole bunch of new capabilities for .NET applications. The dynamic type is an easy way to avoid Reflection and directly access members of 'dynamic' or 'late bound' objects at runtime. There's a lot of very subtle but extremely useful stuff that dynamic does (especially for COM Interop scenearios) but in its simplest form it often allows you to do away with manual Reflection at runtime.

In addition you can create DynamicObject implementations that can perform  custom interception of member accesses and so allow you to provide more natural access to more complex or awkward data structures like the DataRow that I use as an example here.

Bascially you can subclass DynamicObject and then implement a few methods (TryGetMember, TrySetMember, TryInvokeMember) to provide the ability to return dynamic results from just about any data structure using simple property/method access.

In the code above, I created a custom DynamicDataRow class which inherits from DynamicObject and implements only TryGetMember and TrySetMember. Here's what simple class looks like:

/// <summary>
/// This class provides an easy way to turn a DataRow 
/// into a Dynamic object that supports direct property
/// access to the DataRow fields.
/// 
/// The class also automatically fixes up DbNull values
/// (null into .NET and DbNUll to DataRow)
/// </summary>
public class DynamicDataRow : DynamicObject
{
    /// <summary>
    /// Instance of object passed in
    /// </summary>
    DataRow DataRow;
    
    /// <summary>
    /// Pass in a DataRow to work off
    /// </summary>
    /// <param name="instance"></param>
    public DynamicDataRow(DataRow dataRow)
    {
        DataRow = dataRow;
    }

   /// <summary>
   /// Returns a value from a DataRow items array.
   /// If the field doesn't exist null is returned.
   /// DbNull values are turned into .NET nulls.
   /// 
   /// </summary>
   /// <param name="binder"></param>
   /// <param name="result"></param>
   /// <returns></returns>
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = null;

        try
        {
            result = DataRow[binder.Name];

            if (result == DBNull.Value)
                result = null;
            
            return true;
        }
        catch { }

        result = null;
        return false;
    }


    /// <summary>
    /// Property setter implementation tries to retrieve value from instance 
    /// first then into this object
    /// </summary>
    /// <param name="binder"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        try
        {
            if (value == null)
                value = DBNull.Value;

            DataRow[binder.Name] = value;
            return true;
        }
        catch {}

        return false;
    }
}

To demonstrate the basic features here's a short test:

[TestMethod]
[ExpectedException(typeof(RuntimeBinderException))]
public void BasicDataRowTests()
{
    DataTable table = new DataTable("table");
    table.Columns.Add( new DataColumn() { ColumnName = "Name", DataType=typeof(string) });
    table.Columns.Add( new DataColumn() { ColumnName = "Entered", DataType=typeof(DateTime) });
    table.Columns.Add(new DataColumn() { ColumnName = "NullValue", DataType = typeof(string) });

    DataRow row = table.NewRow();

    DateTime now = DateTime.Now;

    row["Name"] = "Rick";
    row["Entered"] = now;
    row["NullValue"] = null; // converted in DbNull

    dynamic drow = new DynamicDataRow(row);

    string name = drow.Name;
    DateTime entered = drow.Entered;
    string nulled = drow.NullValue;

    Assert.AreEqual(name, "Rick");
    Assert.AreEqual(entered,now);
    Assert.IsNull(nulled);
    
    // this should throw a RuntimeBinderException
    Assert.AreEqual(entered,drow.enteredd);
                
}

The DynamicDataRow requires a custom constructor that accepts a single parameter that sets the DataRow. Once that's done you can access property values that match the field names. Note that types are automatically converted - no type casting is needed in the code you write. The class also automatically converts DbNulls to regular nulls and vice versa which is something that makes it much easier to deal with data returned from a database.

What's cool here isn't so much the functionality - even if I'd prefer to leave DataRow behind ASAP -  but the fact that we can create a dynamic type that uses a DataRow as it's 'DataSource' to serve member values. It's pretty useful feature if you think about it, especially given how little code it takes to implement.

By implementing these two simple methods we get to provide two features I was complaining about at the beginning that are missing from the DataRow:

  • Direct Property Syntax
  • Automatic Type Casting so no explicit casts are required

Caveats

As cool and easy as this functionality is, it's important to understand that it doesn't come for free. The dynamic features in .NET are - well - dynamic. Which means they are essentially evaluated at runtime (late bound). Rather than static typing where everything is compiled and linked by the compiler/linker, member invokations are looked up at runtime and essentially call into your custom code. There's some overhead in this. Direct invocations - the original code I showed - is going to be faster than the equivalent dynamic code.

However, in the above code the difference of running the dynamic code and the original data access code was very minor. The loop running over 1500 result records took on average 13ms with the original code and 14ms with the dynamic code. Not exactly a serious performance bottleneck. One thing to remember is that Microsoft optimized the DLR code significantly so that repeated calls to the same operations are routed very efficiently which actually makes for very fast evaluation.

The bottom line for performance with dynamic code is: Make sure you test and profile your code if you think that there might be a performance issue. However, in my experience with dynamic types so far performance is pretty good for repeated operations (ie. in loops). While usually a little slower the perf hit is a lot less typically than equivalent Reflection work.

Although the code in the second example looks like standard object syntax, dynamic is not static code. It's evaluated at runtime and so there's no type recognition until runtime. This means no Intellisense at development time, and any invalid references that call into 'properties' (ie. fields in the DataRow) that don't exist still cause runtime errors. So in the case of the data row you still get a runtime error if you mistype a column name:

// this should throw a RuntimeBinderException
Assert.AreEqual(entered,drow.enteredd);

Dynamic - Lots of uses

The arrival of Dynamic types in .NET has been met with mixed emotions. Die hard .NET developers decry dynamic types as an abomination to the language. After all what dynamic accomplishes goes against all that a static language is supposed to provide. On the other hand there are clearly scenarios when dynamic can make life much easier (COM Interop being one place).

Think of the possibilities. What other data structures would you like to expose to a simple property interface rather than some sort of collection or dictionary? And beyond what I showed here you can also implement 'Method missing' behavior on objects with InvokeMember which essentially allows you to create dynamic methods. It's all very flexible and maybe just as important: It's easy to do.

There's a lot of power hidden in this seemingly simple interface. Your move…

© Rick Strahl, West Wind Technologies, 2005-2011
Posted in CSharp  .NET  

© West-Wind or respective owner

Related posts about CSharp

Related posts about .NET