【问题标题】:How to specify dynamic field names in a Linq where clause?如何在 Linq where 子句中指定动态字段名称?
【发布时间】:2010-03-23 02:56:26
【问题描述】:

如果您创建一个包含 Linq 标准的 Filter 对象,该标准通常放在这样的 where 子句中:

 var myFilterObject = FilterFactory.GetBlank();
 myFilterObject.AddCondition("Salary", "lessThan", "40000");

 var myResult = myRepository.GetEmployees(myFilterObject);

如何在不使用大写语句的情况下将 Linq 字段与字段名称匹配?

 return from e in db.Employee
        where e.Salary < 40000
        select new IList<EmployeeViewModel> { Name= e.name, Salary= e.Salary };

我假设您需要将一个对象发送到指定过滤的存储库,以便您只提取您需要的记录。我假设 Linq 不会预编译(除非您创建自定义委托和函数),因此您应该能够动态指定要过滤的字段。

如果你能做一些类似 e["Salary"] 之类的事情就好了,比如某种 Expando 对象。

【问题讨论】:

    标签: c# linq where-clause


    【解决方案1】:

    您可以手动构建表达式,如下所示:

    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 };
    

    【讨论】:

    【解决方案2】:

    我建议您查看 Linq to SQL 示例中的Dynamic Query。您可以使用它在“纯文本”中编写条件,例如:

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

    澄清一下:这些扩展本质上是从您通常通过 lambda 构造的字符串中构建相同的表达式树,因此这与针对数据库编写原始 SQL 不同。唯一的缺点是您不能使用查询理解语法(from x in y 等)。

    【讨论】:

    【解决方案3】:

    我意识到我参加这个聚会已经很晚了 - 但是我已经编写了一些我认为无需使用 DynamicLINQ 就可以满足这一点的代码,我发现它的支持很差,而且使用起来也很狡猾(充其量)。

        /// <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");
        }
    

    这段代码可以这样使用:

    var whereClause = CreateDynamicExpression<MyDatabaseTable>("MyFieldName",
                        Operator.Contains, "some string value",typeof(string));
    var results = _db.MyDatabaseTable.Where(whereClause);
    

    【讨论】:

      猜你喜欢
      • 2012-02-05
      • 2010-11-03
      • 1970-01-01
      • 2010-10-25
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 2021-06-21
      相关资源
      最近更新 更多