How to specify dynamic field names in a Linq where clause?

流过昼夜 提交于 2019-11-27 07:55:25

I suggest you have a look at Dynamic Query in the Linq to SQL examples. You can use that to write conditions in "plain text", e.g.:

var employees = db.Employee.Where("Salary < 40000").Select(...);

To clarify: These extensions essentially build the same expression trees out of the string that you normally construct via lambdas, so this isn't the same as writing raw SQL against the database. The only disadvantage is that you can't use the query comprehension syntax (from x in y etc.) with it.

You can build the Expression by hand, like so:

var eParam = Expression.Parameter(typeof(Employee), "e");

var comparison = Expression.Lambda(
    Expression.LessThan(
        Expression.Property(eParam, "Salary"),
        Expression.Constant(40000)),
    eParam);

return from e in db.Employee.Where(comparison)
       select new EmployeeViewModel { Name = e.name, Salary = e.Salary };

I'm pretty late to this party, I realize - however I've written some code that I believe satisfies this without using DynamicLINQ, which I've found to be poorly supported, and dodgy to use (at best).

    /// <summary>
    ///     A method to create an expression dynamically given a generic entity, and a propertyName, operator and value.
    /// </summary>
    /// <typeparam name="TEntity">
    ///     The class to create the expression for. Most commonly an entity framework entity that is used
    ///     for a DbSet.
    /// </typeparam>
    /// <param name="propertyName">The string value of the property.</param>
    /// <param name="op">An enumeration type with all the possible operations we want to support.</param>
    /// <param name="value">A string representation of the value.</param>
    /// <param name="valueType">The underlying type of the value</param>
    /// <returns>An expression that can be used for querying data sets</returns>
    private static Expression<Func<TEntity, bool>> CreateDynamicExpression<TEntity>(string propertyName,
        Operator op, string value, Type valueType)
    {
        Type type = typeof(TEntity);
        object asType = AsType(value, valueType);
        var p = Expression.Parameter(type, "x");
        var property = Expression.Property(p, propertyName);
        MethodInfo method;
        Expression q;

        switch (op)
        {
            case Operator.Gt:
                q = Expression.GreaterThan(property, Expression.Constant(asType));
                break;
            case Operator.Lt:
                q = Expression.LessThan(property, Expression.Constant(asType));
                break;
            case Operator.Eq:
                q = Expression.Equal(property, Expression.Constant(asType));
                break;
            case Operator.Le:
                q = Expression.LessThanOrEqual(property, Expression.Constant(asType));
                break;
            case Operator.Ge:
                q = Expression.GreaterThanOrEqual(property, Expression.Constant(asType));
                break;
            case Operator.Ne:
                q = Expression.NotEqual(property, Expression.Constant(asType));
                break;
            case Operator.Contains:
                method = typeof(string).GetMethod("Contains", new[] {typeof(string)});
                q = Expression.Call(property, method ?? throw new InvalidOperationException(),
                    Expression.Constant(asType, typeof(string)));
                break;
            case Operator.StartsWith:
                method = typeof(string).GetMethod("StartsWith", new[] {typeof(string)});
                q = Expression.Call(property, method ?? throw new InvalidOperationException(),
                    Expression.Constant(asType, typeof(string)));
                break;
            case Operator.EndsWith:
                method = typeof(string).GetMethod("EndsWith", new[] {typeof(string)});
                q = Expression.Call(property, method ?? throw new InvalidOperationException(),
                    Expression.Constant(asType, typeof(string)));
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(op), op, null);
        }

        return Expression.Lambda<Func<TEntity, bool>>(q, p);
    }

    /// <summary>
    ///     Extract this string value as the passed in object type
    /// </summary>
    /// <param name="value">The value, as a string</param>
    /// <param name="type">The desired type</param>
    /// <returns>The value, as the specified type</returns>
    private static object AsType(string value, Type type)
    {
        //TODO: This method needs to be expanded to include all appropriate use cases
        string v = value;
        if (value.StartsWith("'") && value.EndsWith("'"))
            v = value.Substring(1, value.Length - 2);

        if (type == typeof(string))
            return v;
        if (type == typeof(DateTime))
            return DateTime.Parse(v);
        if (type == typeof(DateTime?))
            return DateTime.Parse(v);
        if (type == typeof(int))
            return int.Parse(v);
        if (type == typeof(int?)) return int.Parse(v);

        throw new ArgumentException("A filter was attempted for a field with value '" + value + "' and type '" +
                                    type + "' however this type is not currently supported");
    }

This code can be used thusly:

var whereClause = CreateDynamicExpression<MyDatabaseTable>("MyFieldName",
                    Operator.Contains, "some string value",typeof(string));
var results = _db.MyDatabaseTable.Where(whereClause);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!