Use Reflection and Expression to Find an Entity by Primary Key

On occasion I maintain a project based on Entity Framework Database First approach.  This project is using ObjectContext.  I was recently working on a generic feature that was requiring me to find an entity based on primary key. In order to do this I have to dynamically create a where clause that filders data based on ID, since ObjectContext API does not support Find method.  As part of the solution, I need to create a method that returns Expression<Func<T, bool>> , which I will dynamically pass into a method that operates on an object set.  I am not going to describe other methods, as they are less interesting.  I do however want to show how to dynamically create Expression<Func<T, bool>>.  I put comments in line, so that you can see what is going on.

        /// <summary>
        /// Create expression for .Where(entity => entity.Id == 'id')
        /// </summary>
        /// <typeparam name="T">Type of entity</typeparam>
        /// <param name="id">Entity ID</param>
        /// <returns></returns>
        private static Expression<Func<T, bool>> GetExpression<T>(object id)
        {
            // Find primary key property based on primary key attribute.
            var keyProperty = typeof(T).GetProperties().
                First(
                    one =>
                    one.GetCustomAttributes(typeof(EdmScalarPropertyAttribute), true)
                    .Any(two => ((EdmScalarPropertyAttribute)two).EntityKeyProperty));

            // Create entity => portion of lambda expression
            ParameterExpression parameter = Expression.Parameter(typeof(T), "entity");

            // create entity.Id portion of lambda expression
            MemberExpression property = Expression.Property(parameter, keyProperty.Name);

            // create 'id' portion of lambda expression
            var equalsTo = Expression.Constant(id);

            // create entity.Id == 'id' portion of lambda expression
            var equality = Expression.Equal(property, equalsTo);

            // finally create entire expression - entity => entity.Id == 'id'
            Expression<Func<T, bool>> retVal = 
                Expression.Lambda<Func<T, bool>>(equality, new[] { parameter });
            return retVal;
        }

Enjoy.

11 Comments

  1. @Sergey. My apologies. My experience with Entity Framework is pretty new. As such, I am really only familiar with the dbContext object. However, I think that ObjectContext would support .FirstOrDefault(e => e.Id == idParam) – wouldn’t it? If so, why not go that route?

    Not to take away from your method. There’s some very good stuff in there! Very informative for me.

  2. No need to apologize. Yes, you can user FirstOrDefault(), but I want to write a method in such a fashion that the same method works for all collections within object context.

  3. Fantastic post, Sergey! Thank you for sharing. I used it to build a similar method where a PropertyInfo object (like from a lambda expression pointing to a property on a class) is passed as a parameter, and then the expression is built from there. This post also inspired me to learn more about the Expression class and namespace. That has really helped simplify my code. Thanks again!

  4. Hello, that’s a pretty good example. How would you create custom attribute EdmScalarPropertyAttribute within the entities in the model, either manually or automattically from Database upon “Update model from Database” ?

    Thanks!

  5. I’m doing like that, with “database first” approach. The generated model does not include any annotations. System.ComponentModel.DataAnnotations is automattically though, right after new connection is created to the database.

    Any further help with this, please? What is the piece of code in the .tt template file which generates those EdmScalarPropertyAttribute annotations?

    Many thanks indeed.

  6. Well, I’ve just managed to generate [Key] attribute in the Entities, automattically out from .tt template. Just adding this right before the piece of code that outputs the property:

    if (ef.IsKey(edmProperty)) {
    [Key]
    }

    you can then query for this CustomAttribute instead of for EdmScalarPropertyAttribute

    Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *