I Can't Believe That Worked

Code and Ideas, minus the profanity (the one language all developers know)

MetaTable Expression Builder for Fetching Any Object Via Collection of Primary Keys

Basically, I wanted to build a method that gets an object from a data context whatever the primary key may look like, and whatever type that object may be.  I wrote this code a little while ago when I was exploring the DynamicData libs.  I was checking out some of the code in the libs, and noticed a very slick way one of devs was building expressions to grab back a data item based on a single key ID.  I thought the idea of building up the expression completely ROCKED, but I wanted to expand it out to include composite primary keys.  Below is my implementation of the composite primary key object fetcher.  Hopefully, it will make your life a little easier.

 

(Needed to grab the MetaModel class)

 

using System.Web.DynamicData;

 

 

(Create a new MetaModel for your Context or Contexts -- make sure they are only registered once!!  This can be an issue if you put this code in a static constructor, and you happen to have a generic class.  That would cause the code to get executed each time you specify a new generic argument for type construction (sry, I digress))

 

Model = new MetaModel();

Model.RegisterContext(typeof([Your Context Type]));

 

 

(Get your meta table based on the type from your registered context -- need the metadata about the table to figure out the keys)

 

protected static MetaTable GetMetaTable<T>()

{

    return dataModelMetaModel.GetTable(typeof (T));

}


(This is where the heavy lifting occurs.  I commented what is going on.  There is not much more to say.  Enjoy!)

 

private static T FetchObject<T>(MetaTable metatable, IDictionary<string, object> pks)

    where T : class

{

    IQueryable query = metatable.GetQuery();

 

    //build linq expression tree for where clause

    ParameterExpression entityparam = Expression.Parameter(metatable.EntityType, "e");

    List<Expression> internalExpressons = new List<Expression>();

    Expression equalexpression;

    foreach (MetaColumn member in metatable.PrimaryKeyColumns)

    {

        //convert primary key to proper type

        PropertyInfo columnpropertyinfo = member.EntityTypeProperty;

        MemberExpression columnexpresson = Expression.MakeMemberAccess(entityparam, columnpropertyinfo);

        internalExpressons.Add(Expression.Equal(columnexpresson, Expression.Constant(pks[member.Name])));

    }

 

    // build up the primary key expressions

    if (internalExpressons.Count > 1)

    {

        equalexpression = Expression.And(internalExpressons[0], internalExpressons[1]);

        for (int i = 2; i < internalExpressons.Count; i++)

        {

            equalexpression = Expression.And(equalexpression, internalExpressons);

        }

    }

    else

    {

        equalexpression = internalExpressons[0];

    }

 

    // Create the where clause

    LambdaExpression wherelambda = Expression.Lambda(equalexpression, entityparam);

    MethodCallExpression wherecall = Expression.Call(typeof (Queryable), "Where", new[] {metatable.EntityType},

                                                     query.Expression, wherelambda);

    query = query.Provider.CreateQuery(wherecall);

 

    // Call FirstOrDefault to make sure the connection is closed when finished

    // saw some connections staying open when just grabbing the first object.

    return query.Cast<T>().FirstOrDefault();

}

 

Twitter: @DavidJustice

Leave a Comment

(required) 

(required) 

(optional)

(required)