Expression generation for EF with dynamic properties

Asked

Viewed 54 times

4

I’d like to conduct a search on where for Entity Framework passing the name of the property you would like to compare as a string.

For example, I have the following classes:

public class ClientData
{
    public Guid Id { get; set; }
    public CLient CLient { get; set; }
    public Guid ClientId { get; set; }
    public int Shipments { get; set; }
}

public class Client
{
    public Guid Id { get; set; }
    public string Nome { get; set; }
}

And I would like to create a method as follows:

Expression<Func<ClientData, bool>> DynamicExpression(string propertyName, string value)
{
    Expression<Func<ClientData, bool>> predicate = _ =>
                                              _.Client.Name.ToUpper() == value.ToUpper();

    return predicate;
}

But instead of using directly Client.Name would like to make the comparison using the property referring to the name that was passed in the parameter propertyName.

Is that possible? If so, how could I accomplish this?

  • This code has more problems than you think. It’s possible, but it will slow down. With a larger context it could do something completely different, it may not be as bad as it would like, but it would be a much better solution.

1 answer

2


The intention is to pass a code so you know how this works under the table, extracted code - Dynamic Query Expressions With Entity Framework and altered a small part by me to run data nullable, example of the code:

public static class Utils
{
    internal class PropertyAccessorCache<T> where T : class
    {
        private IDictionary<string, LambdaExpression> Cache { get; set; }
        public PropertyAccessorCache()
        {
            var storage = new Dictionary<string, LambdaExpression>();
            var t = typeof(T);
            var parameter = Expression.Parameter(t, "p");
            foreach (var property in t.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                var propertyAccess = Expression.MakeMemberAccess(parameter, property);
                var lambdaExpression = Expression.Lambda(propertyAccess, parameter);
                storage[property.Name] = lambdaExpression;
            }
            Cache = storage;
        }
        public LambdaExpression Get(string propertyName)
        {
            return Cache.TryGetValue(propertyName, out LambdaExpression result) ? result : null;
        }
    }

    public static IQueryable<T> ApplyWhere<T, TValue>(this IQueryable<T> source,
        string propertyName,
        TValue propertyValue) where T : class
    {   
        var propertyAccessorCache = new PropertyAccessorCache<T>();
        var mba = propertyAccessorCache.Get(propertyName);
        if (mba == null) return source;           
        var eqe = Expression.Equal(mba.Body, Expression.Constant(propertyValue, mba.ReturnType));
        var expression = Expression.Lambda(eqe, mba.Parameters[0]);
        MethodCallExpression resultExpression = Expression.Call(null,
            GetMethodInfo(Queryable.Where, source, (Expression<Func<T, bool>>)null),
            new Expression[] { source.Expression, Expression.Quote(expression) });
        return source.Provider.CreateQuery<T>(resultExpression);
    }

    private static MethodInfo GetMethodInfo<T1, T2, T3>(Func<T1, T2, T3> f, T1 unused1, T2 unused2)
         => f.Method;
}

note that it is an extensive code, and for small solutions (which I think is also unnecessary, but worth as information for the site) it is even possible to use and this is translated directly to SQL (tests performed in Entity Framework Core environment and 6 with Sqlserver database).

Example of use:

var peopleList = ctx.People.ApplyWhere("Name", "VALOR_EXEMPLO").ToList();

This code section does not cover, for example, a LIKE, etc, but, answers what the question reports, it is worth remembering that I would not do it, since all this is already foreseen in the Entity Framework, and maybe it lacks to do what it needs otherwise, but, it is as an example that I find very valid.

Reference and rights:

Browser other questions tagged

You are not signed in. Login or sign up in order to post.