Question

I like the "micro" approach of Dapper, Massive, PetaPoco etc. and I like to have control over the SQL we send to the database, most of the time it's relatively simple. I also like working with POCO's however when dealing with a somewhat flexible schema design you often run into trouble :)

Let's say we have a Person entity, that ALWAYS have the following properties.

  • Id
  • Name
  • Email
  • Phone

But in some cases there might be additional properties like

  • SpecialPhoneNumber
  • VeryCustomValue

I would really like a POCO with the common properties that we know will always be there. But have the other accessible in a key/value collection.

What would be the best approach? And does any of the mentioned "mirco-orm's" support this? I have looked at them all but have not found any indication that they do, but maybe I'm missing something.

Would it be possible to do this directly with an SqlDataReader? or would the performance of reading hundreds of rows be bad when using reflection to create the objects? The mentioned orm's all seem to do pretty well and I guess the use a DataReader underneath.

Hope you can help :)

EDIT: I should probably mention that we have no control over the application architecture. This is an ERP solution that allows the individual customer to customize their application and the underlying database. Adding fields in the application adds columns in the database. Bad me! for not making that clear in the first place

Was it helpful?

Solution

Massive can support this, because it materializes data into ExpandoObject's:

The secret sauce is the ExpandoObject. Everything that goes in and everything that comes out of Massive is an Expando – which allows you to do whatever you want with it. At it’s core, an ExpandoObject is just an IDictionary< string,object >

ExpandoObject implements IDictionary<string, Object> and IEnumerable<KeyValuePair<string, Object>>, so you can enumerate and test for members in any way you need to.

Massive's default is to issue a SELECT * query, so the ExpandoObject will contain all fields in the table, even those you do not know about.

You can get Massive's current code from Github.

Dapper can also select into dynamic objects, but unlike Massive it can only select data and cannot insert, update, or delete it. EDIT: Reviewing the Dapper docs, it appears it can perform modifications, as it can execute any sql. There is some more recent information about performing inserts with Dapper.

OTHER TIPS

Use table per type inheritence or an extension table in your design to allow for the added data on an entity.

MyBatis.NET (formerly iBatis.NET) is a data mapper tool that has a feature I find really useful. If you've never heard of it, it is an ORM tool that allows you to write the SQL, and you create the domain objects, and use XML to map the database columns to the domain object properties.

One of these result mappings might look like:

<resultMap id="Contact" class="Contact" >
    <result property="ContactId" column="ContactId" />
    <result property="FirstName" column="FirstName" />
    <result property="MiddleInitial" column="MiddleInitial" />
    <result property="LastName" column="LastName" />
</resultMap>

In there you have an id for the result, a class alias (which maps to Namespace.Folder.Contact, for example), and a list of column to property mappings.

Where you might have interest is in that there's an ability to extends result maps just as you would inherit from a class. In your situation, you might have:

<resultMap id="Person" class="Person" >
    <result property="Id" column="Id" />
    <result property="Name" column="Name" />
    <result property="Email" column="Email" />
    <result property="Phone" column="Phone" />
</resultMap>

And for your other situation:

<resultMap id="PersonExtended" extends="Person" class="Person">
    <!-- inherits all the "Person" result map properties -->
    <result property="SpecialPhoneNumber" column="SpecialPhoneNumber" />
    <result property="VeryCustomValue" column="VeryCustomValue" />
</resultMap>

And then you would have two separate queries, one that selects just the basic information and maps to the "Person" result map, and another query that maps to the "PersonExtended" result map that includes more columns.

There's also support for selecting to Dictionaries and avoiding N+1 selects. And just to note: I am no way affiliated with the MyBatis project, I've just found it really flexible and a pretty solid data mapper. I use it for all of my projects mostly because I wrote a tool that can generate my entire data access layer just by pointing it to a database.

Setting it up is a bit of PITA but maybe it could work for you!

You could take a look at nHibernate's dynamic mapping.

http://ayende.com/blog/3942/nhibernate-mapping-dynamic-component

It allows you to retrieve such extended properties into generic Directory<string,object>. Thus, you have your classes with such generic bags (dictionaries) and dynamically extend the XML mapping.

nHibernate will do the rest for you.

Id Name Email Phone But in some cases there might be additional properties like SpecialPhoneNumber VeryCustomValue

Send the architect back to programmer school 101. Design error - Phone, SpecialPhoneNumber are both NOT properties of Person (which is not Person anyway but "Entity" because it also can be a legal entity).

They are a list of "contactpoints" or something.

That said, property buckets can also be demonstrated in an ORM, with bad performance but hey, this is the way sql databases work. Bad performance for property buckets.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top