Thursday, October 22, 2009 10:09 AM
dparys
Visual Studio 2010 - Fun with DynamicObject in C# 4.0
This morning I just played around with the new C# 4.0 dynamic feature. Using the dynamic keyword allows you to bind to properties, indexers and methods during runtime. This means that during compile time there is no check if the methods, properties or whatever you are calling are really existent, you experience any missing pieces during runtime.
On the other hand this allows you to implement some flexible stuff. You can implement your own dynamic objects just deriving from DynamicObject and overriding the methods you are interested in.
So, I just implemented a short demo how to use DynamicObject derived classes to wrap some parts of System.Data.SqlClient to access data from tables directly, without any code gen.
The result is, that I can write the following code and it will resolve during runtime if it can find data for the specified tables.
using( dynamic db = new DirectDataAccess("connectionstring") )
{
Console.WriteLine( db.Products.Count );
Console.WriteLine( db.Customers[15].CompanyName );
Console.WriteLine( db.Employees.Row10.LastName );
}
Here is how it works. First lets have a look at the responsible classes:
DirectDataAccess, DirectRowAccess and DirectColumnAccess are deriving from DynamicObject. My approach is simple and just for demo purposes. Also it just enables read access and does not handle errors well. I encapsulate DataTable as member and create new instances of DirectRowAccess and DirectColumnAccess through the overriden TryGetMember methods.
Lets have a look into the DirectDataAccess implementation:
public class DirectDataAccess : DynamicObject, IDisposable
{
SqlConnection connection = null;
public DirectDataAccess(string connectionString)
{
connection = new SqlConnection(connectionString);
connection.Open();
}
public void Dispose()
{
/// removed for clarity
}
public override bool TryGetMember(GetMemberBinder binder,
out object result)
{
DataTable table = new DataTable(binder.Name);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "SELECT * FROM " + binder.Name;
cmd.CommandType = CommandType.Text;
cmd.Connection = connection;
try
{
table.Load(cmd.ExecuteReader());
}
catch (Exception)
{
result = null;
return false;
}
result = new DirectRowAccess(table);
return true;
}
}
The constructor is creating the connection and opens it. The TryGetMember implementation just constructs a SQL Query for the given binder. The binder specifies exactly the property name I use in the code. For instance, I want to get a specific table, I just type:
// db is an instance of DirectDataAccess
var table = db.Products;
table is an instance of DirectRowAccess which is also derived from DynamicObject.
public class DirectRowAccess : DynamicObject
{
private DataTable table = null;
public DirectRowAccess(DataTable table)
{
this.table = table;
}
public override bool TryGetMember(GetMemberBinder binder,
out object result)
{
string raw = binder.Name.Substring(3,
binder.Name.Length - 3);
int index = Convert.ToInt32(raw);
try
{
result = new DirectColumnAccess(table.Rows[index]);
return true;
}
catch (Exception)
{
result = null;
return false;
}
}
public override bool TryGetIndex(GetIndexBinder binder,
object[] indexes,
out object result)
{
result = new DirectColumnAccess(
table.Rows[(int)indexes[0]]);
return true;
}
public int Count
{
get
{
return table.Rows.Count;
}
}
}
Methods, properties and indexers which are implemented by the class are called directly. The override TryGetMember will be called if a method or property was not found by the binder natively.
I can access rows from the table in two ways. I can use an indexer like
var row = db.Customers[10];
or I can use an alternative implementation like
var row = db.Customers.Row10;
both returning a new instance of DirectColumnAccess, which, yes you can guess, is also derived from DynamicObject:
public class DirectColumnAccess : DynamicObject
{
private DataRow row = null;
public DirectColumnAccess(DataRow row)
{
this.row = row;
}
public override bool TryGetMember(GetMemberBinder binder,
out object result)
{
try
{
result = row[binder.Name];
return true;
}
catch (Exception)
{
result = null;
return false;
}
}
public override string ToString()
{
return string.Join(",", row.ItemArray);
}
}
This TryGetMember implementation tries to retrieve the column value specified in the binder, for instance:
var companyName = db.Customers[10].CompanyName;
The ToString() override is just an convenient way to retrieve all values from a specific row, so you can just console out the following:
Console.WriteLine( db.Customers[10] );
Just remember:
This is just demo code, written just for fun!
Another Idea I had was to write a generic REST object which implements the common HTTP Verbs GET, POST, PUT and DELTE to talk to REST Services. I thought on writing something like this:
dynamic rest = new REST("http://twitter.com")
var xml = rest.GET.statuses_public__timeline.AsXml();
var postXml = ...;
var xml2 = rest.POST.statuses_update(postXml).AsXml();
But there are some problems to solve like the fact that you can’t use a forward slash in a method or property name and masking the underscore is maybe not a good idea.
What do you think, are there any usages in your environment where DynamicObject derived implemenations could help?