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.
Why would we need this? If we know the type and the id, why go through the trouble? It seems very interesting. Could you explain a scenario where this could be used?
@Mark. Object context API does not support find method. Do you know of any other way to achieve this?
@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.
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.
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!
I’m sorry to ask. But how can we use this?
Just an example on how to use expression trees as part of your reflection tool belt to write generic code.
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!
It is generated in database first approach
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.
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!