Thursday, April 03, 2008

Repository.GetById using LINQ Expression Syntax

A while ago I talked about using the IRepository<T> pattern with LINQ to SQL. One of the methods of my repository is GetById. The slight difficulty here is that we need to discover the primary key property of the (generated) type at runtime so we can't use a vanilla lambda expression in the Where clause. Before I used the DynamicQueriable helper library, but I've just re-written the function using an explicit expression tree which removes the need to reference the helper library.

public T GetById(int id)
{
    var itemParameter = Expression.Parameter(typeof(T), "item");

    var whereExpression = Expression.Lambda<Func<T, bool>>
        (
            Expression.Equal(
                Expression.Property(
                    itemParameter,
                    typeof(T).GetPrimaryKey().Name
                ),
                Expression.Constant(id)
            ),
            new ParameterExpression[] { itemParameter }
        );

    return dataContext.GetTable<T>().Where(whereExpression).Single();
}

Here is the extension method that finds the primary key:

public static PropertyInfo GetPrimaryKey(this Type entityType)
{
    foreach (PropertyInfo property in entityType.GetProperties())
    {
        ColumnAttribute[] attributes = (ColumnAttribute[])property.GetCustomAttributes(typeof(ColumnAttribute), true);
        if (attributes.Length == 1)
        {
            ColumnAttribute columnAttribute = attributes[0];
            if (columnAttribute.IsPrimaryKey)
            {
                if (property.PropertyType != typeof(int))
                {
                    throw new ApplicationException(string.Format("Primary key, '{0}', of type '{1}' is not int",
                        property.Name, entityType));
                }
                return property;
            }
        }
    }
    throw new ApplicationException(string.Format("No primary key defined for type {0}", entityType.Name));
}

7 comments:

  1. Repository pattern discussed here is great.. Suppose if we need to cache business objects... where do we handle caching? DAL or BIZ layer?

    Thanks

    ReplyDelete
  2. Hi Anil,

    Thanks for the comment. In my opinion caching should be the responsibility of the DAL. It's an infrastructure thing, and your Domain or Business Layer should simply be responsible for representing your domain rules.

    LINQ-to-SQL does object caching for the lifetime of the DataContext, but then you've got to decide how long you want your DataContext hanging around for. In most web applications it would be a mistake have a lifetime beyond the request, which is probably not what you want.

    ReplyDelete
  3. Anonymous2:26 am

    Really helped me a lot and works like a charm. Thanks

    ReplyDelete
  4. Thank you, This was really useful to me......

    ReplyDelete
  5. Mike,

    Why are you throwing an Exception if the Primary Key is not of type int? What if you have a PK set as GUID (string) ?

    ReplyDelete
  6. Hi Dreas,

    In my case all my primary keys are ints. If you want to use GUIDs, just change the code to check for them instead.

    ReplyDelete
  7. Very nice, just came across the same issue, you saved me a lot of time

    ReplyDelete

Note: only a member of this blog may post a comment.