c# - 字符串表达式到 Linq Where
问题描述
在我的项目中,我希望用户能够提供以下形式的参数:“Fieldname=Value”;然后,我将检查提供的参数,并检查 Fieldname 是否是模型中的有效属性;然后,我需要有一个 Linq.Where() 查询来动态选择过滤所需的字段名:
start.exe -Filter "TestField=ThisValue"
应翻译为:
List<Mutations>.Where(s => s.{TestField} == "ThisValue" ).FirstOrDefault
问题是我不知道如何将字符串或属性名称转换为s.{TestField}
部件..
解决方案
您可以为此使用反射和表达式 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->GetField
,Expression.Property->Expression.Field
和member.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?
)增加了自己的复杂性。
推荐阅读
- javafx - Javafx 导入问题
- websocket - 向所有连接的客户端发送消息 socket.io
- gradle - Gradle Kotlin 脚本:“包含”- 不能使用提供的参数调用以下函数
- python - 生成随机的 28 × 28 灰度图像,绘制一个数字
- javascript - Nativescript Vue TypeError:无法读取未定义的属性“0”
- java - 在操作栏上按下后退按钮后 RecyclerView 不显示
- javascript - 找不到导致变量未定义的问题(“无法读取未定义的属性 'toString')
- android - 每次单击浮动操作按钮时是否可以更改其颜色?
- sql-server-2016 - 合并和逗号分隔的行
- python - Python - Google Firebase 中的复杂查询