Dynamically Mapping a Custom Entity
This post from Chris Wallace
got me thinking about dynamically mapping data results to custom
entities. In his post Chris outlines a way to dynamically fill a
custom entity using a switch statement on the columns that are present
in the datareader. Towards the end, he mentions dynamically
creating a method (using the CodeDom presumably) and compiling and
executing the method on the fly.
I thought of a different way to accomplish the same goal, one that
requires no knowledge of the custom entity, by using some simple
reflection. Basically, you have a datareader (or datatable,
dataset, whatever), you loop through the columns of the datareader and
get the name of the current column. Using reflection, check to
see if the custom entity has a property with the same name, if it does
then fill the value, if not, continue looping.
I’m going to show a small sample below which is very simplistic, but
it gets the point across. There are a few caveats that I’m not
addressing:
- Null values (i.e. trying to set an int property to null)
- I’m assuming that a default parameterless constructor exists for the custom entity
- I’m not doing anything for indexed properties (like DataReader[int ordinal])
You can download a little sample winforms application here. Just change the “DB” value in the app.config to point to the sample access db and you should be all set.
So, heres the code. The method below takes in a datareader and
the Type of the custom entity that we want to fill and returns a custom
entity with its properties filled in with the data from the datareader.
public static object MapEntity(IDataRecord record, Type customType)
{
object customEntity = Activator.CreateInstance(customType);
PropertyInfo[] properties = customType.GetProperties();
for(int i=0;i < record.FieldCount;i++)
{
string columnName = record.GetName(i);
foreach(PropertyInfo prop in properties)
{
if (string.Compare(prop.Name, columnName) == 0)
{
prop.SetValue(customEntity, record.GetValue(i), null);
break;
}
}
}
return customEntity;
}
This works pretty slick for most cases, but there are situations
that you would run into where this break down. If you wanted to
have property names that didn’t match the database schema for instance,
but it could certainly handle a large amount of the data access,
so long as you address the issues that I mentioned above.
[Edit: Fixed typo]
jmiller said,
Wrote on August 24, 2005 @ 10:57 pm
That’s fine as a cool thought exercise, but why swallow all that inefficiency when you could predefine the mappings with one of any number of O/R tools? This would also tightly couple the domain classes to the database structure.
The other big issue you’d hit doing this would be type coercion between the DB types and your entity properties.
breichelt said,
Wrote on August 24, 2005 @ 11:21 pm
Jeremy, I realize that there are some perf issues as well, the intent of this was really just academic because Chris’s post piqued my interest. However, I think that by caching the reflection part you could improve perf.
As far as having classes tied to the database structure I agree with you theoretically, but what this really means that is that the classes are tightly coupled to the capabilities of the sql language. I could define a view that combines tables and aggregates some information, and use that as the datasource that drives an object, rather than just a table.
You are absolutely right about the data type mismatch, can’t deny that
jmiller said,
Wrote on August 25, 2005 @ 12:07 am
You *could* beat the performance issue by emitting IL on the fly. Someone someday is going to write an O/R tool this way that’ll blow away everything else in performance.
The only little problem I see is the order of magnitude increase in developer effort;)
Anonymous said,
Wrote on August 25, 2005 @ 7:39 am
I didn’t follow the reflection code too closely but we do some reflection on mapping data from a db into our value objects. There are things to consider such as performance. There was an article not too long back (wish I could find the url) but there are reflection methods that are faster than others. If you keep the reflection simple and don’t go looking up dll’s it’s fairly quick. The one downside to how we are approaching is is the method we are using is in each data object class. That keeps the reflection faster.
Anonymous said,
Wrote on August 25, 2005 @ 7:51 am
While I don’t necessarily think what we have explored so far is optimal, I do think it is these little explorations to see what we can do that inspire product changes or new products. Yes, you can dynamically gen the code up front with an OR tool but that leaves you (at least from I’ve seen) with the reader to object mapping code in every method (duplicated code). That duplicated code is what I was trying to see if we could avoid.
Nick Harrison said,
Wrote on September 28, 2005 @ 12:37 am
Here is a link to a blog where I discuss my findings in regards to performance of similiar custom entity mappings. One thing that I found was that a “reflective mapper” written well is faster than a “hard coded mapper” written badly. Since a reflective mapper has to be written well only once and a hard coded mapper could be written badly any time that it is reproduced. Odds are that the hard coded approach will be written badly atleast on occassion.
I have also seen good results with code generation. But there is no need for the code to be generated at run time. The mapping logic and the custom business entity object for that matter could be generated at design time. Generated code would be consistent, follow best practices, and potentially yield faster code than the other methods.
Nick Harrison said,
Wrote on September 28, 2005 @ 12:38 am
Here is the link I mentioned earlier http://www.geekswithblogs.com/nharrison/archive/2005/05/29/40909.aspx
Nick Harrison said,
Wrote on September 28, 2005 @ 12:39 am
Here is the link that I mentioned earlier
http://www.geekswithblogs.com/nharrison/archive/2005/05/29/40909.aspx