首页 > 解决方案 > 字符串表达式到 Linq Where

问题描述

在我的项目中,我希望用户能够提供以下形式的参数:“Fieldname=Value”;然后,我将检查提供的参数,并检查 Fieldname 是否是模型中的有效属性;然后,我需要有一个 Linq.Where() 查询来动态选择过滤所需的字段名:

start.exe -Filter "TestField=ThisValue" 应翻译为: List<Mutations>.Where(s => s.{TestField} == "ThisValue" ).FirstOrDefault

问题是我不知道如何将字符串或属性名称转换为s.{TestField}部件..

标签: c#linq

解决方案


您可以为此使用反射和表达式 API。首先,我将假设您实际上使用的是属性不是字段(您使用的是属性,吗?)

var type = typeof(Mutations);
var member = type.GetProperty("TestProperty");
if (member == null)
   throw new Exception("property does not exist");

var p1 = Expression.Parameter(type, "s");
var equal = Expression.Equal(
  Expression.Property(p1, member),
  Expression.Constant("Test Value", member.PropertyType)
);
var lambda = Expression.Lambda<Func<Mutations, bool>>(equal, p1);

var result = list.AsQueryable().FirstOrDefault(lambda);

如果您实际使用公共字段(为什么?!),您可以进行以下修改GetProperty->GetFieldExpression.Property->Expression.Fieldmember.PropertyType->member.FieldType. 不过要小心;一些 ORM 仅适用于属性,因此会拒绝其他有效的Expression.

我们可以把上面的东西变成一个可重用的通用方法,返回一个Expression

using System.Linq.Expressions;

public static class ExpressionHelpers {
  public static Expression CreateWhere<T>(string propertyName, string targetValue) {
    var type = typeof(T);
    var member = type.GetProperty(propertyName) ?? throw new Exception("Property does not exist");

    var p1 = Expression.Parameter(type, "s");
    var equal = Expression.Equal(
      Expression.Property(p1, member), 
      Expression.Constant(targetValue, member.PropertyType)
    );
    return Expression.Lambda<Func<T, bool>>(equal, p1);
  }
}

调用此方法可能如下所示:

public static void SomeMethod() {
  var list = new List<Mutations> { /* ... */ };

  Expression clause = ExpressionHelpers.CreateWhere<Mutations>("TestProperty", "TestValue");
  var result = list.AsQueryable().FirstOrDefault(clause);
  if (result != null)
    Console.WriteLine("Result = {0}", result);
}

请注意,这不会对数据类型进行任何类型的验证——它假定属性与输入的类型相同,当前为string. 如果您需要处理数字或日期或您有什么,您需要打开数据类型并将适当的解析数据提供给常量:

public static Expression CreateWhere<T>(string propertyName, string targetValue) {
  var type = typeof(T);
  var member = type.GetProperty(propertyName) ?? throw new Exception("Property does not exist");
  var propType = member.PropertyType;

  if ((propType.IsClass && propType != typeof(string)) || propType.IsInterface)
    throw new Exception("Interfaces and Class Types are not supported");

  var p1 = Expression.Parameter(type, "s");

  Expression target = null;
  if (propType == typeof(string)) 
    target = Expression.Constant(targetValue, typeof(string));

  else if (propType == typeof(int) && int.TryParse(targetValue, out var intVal))
    target = Expression.Constant(intVal, typeof(int));

  else if (propType == typeof(long) && long.TryParse(targetValue, out var longVal))
    target = Expression.Constant(longVal, typeof(long));

  else if (propType == typeof(DateTime) && DateTime.TryParse(targetValue, out var dateVal))
    target = Expression.Constant(dateVal, typeof(DateTime));

  else
     throw new Exception("Target property type is not supported or value could not be parsed");

  var equal = Expression.Equal(
    Expression.Property(p1, member), 
    target
  );
  return Expression.Lambda<Func<T, bool>>(equal, p1);
}

正如您所看到的,随着您想要支持的类型越多,这开始变得相当复杂。另请注意,如果您在没有 ORM 的情况下使用它(只是列表上的 LINQ),您可能需要添加一些对区分大小写的字符串比较的支持。这可以委托给一个看起来像这样的string.Equals调用:

bool ignoreCase = true; // maybe a method parameter?
var prop = Expression.Property(p1, member);

Expression equal = null;
if (propType != typeof(string))
{
  equal = Expression.Equal(prop, target);
}
else 
{
  var compareType = ignoreCase 
    ? StringComparison.OrdinalIgnoreCase 
    : StringComparison.Ordinal;

  var compareConst = Expression.Constant(compareType, typeof(StringComparison));

  equal = Expression.Call(
   typeof(string), 
   nameof(string.Equals), 
   new[] { typeof(string), typeof(string), typeof(StringComparison) }, 
   prop, 
   target, 
   compareConst
  );
}

return Expression.Lambda<Func<T, bool>>(equal, p1);

请注意,根据他们的支持,这可能会或可能不会与 ORM 一起使用(并且可能不是必需的,因为默认情况下许多数据库是不敏感的比较)。以上也没有处理Nullable<T>(即int?)增加了自己的复杂性。


推荐阅读