验证和修改传递给动态LINQ的字符串
本文关键字:动态 LINQ 字符串 修改 验证 | 更新日期: 2023-09-27 18:08:26
我正在使用System.Linq.Dynamic名称空间基于字符串构建谓词,并且我想向用户提供的字符串添加一些额外的验证和格式化。更具体地说,我试图避免用户必须输入ToDate("2014/06/13"),当他想要在字符串中提供日期时,通过识别他想要比较的参数的数据类型是DateTime并在日期周围注入DateTime()字符串。
从字符串中获取Lambda的代码:
var p = Expression.Parameter(typeof(Customer), "Customer");
var e = System.Linq.Dynamic.DynamicExpression.ParseLambda<Customer, bool>(customFilter, p);
到目前为止,我唯一的想法是分析表达式的DebugView,获得属性,以某种方式通过反射将它们转换为类型,并从那里做我的逻辑。但这似乎有点复杂。DebugView:
.Lambda #Lambda1<System.Func`2[Customer,System.Boolean]>(Customer $var1)
{
.Call ($var1.CompanyName).StartsWith("S") || $var1.AttrCount >= 3 && $var1.ConnectionsCount >= 0
}
有人有更好的主意吗?
谢谢!
您可能需要预处理字符串或扩展DynamicLinq以添加对日期/时间文字的支持。后者可能是更好的选择,因为解析器已经写好了;您只需要扩展它。
我这样说的原因是因为如果你试图解析像it.Date >= "1/1/2014"
这样的表达式,DynamicLinq将尝试在DateTime
属性和string
之间构建>=
比较,这将失败,因为没有这样的操作符存在。这有效地防止了您在事后重写表达式树,因为DynamicLinq将无法构建它。
我在下面包含了几个扩展DynamicLinq的概念验证解决方案。我个人比较喜欢第一个解决方案,但第二个更符合你原来的问题。
解决方案1:自定义日期时间文本
我刚刚对DynamicLinq做了一个快速的概念修改证明,允许DateTime
文字与#
符号引用,例如,#6/13/2014#
。这很简单:
向TokenId
enum中添加DateTimeLiteral
表项。
在ExpressionParser.NextToken()
的交换机中增加以下内容:
case '#':
NextChar();
while (textPos < textLen && ch != '#') NextChar();
if (textPos == textLen)
throw ParseError(textPos, Res.UnterminatedDateTimeLiteral);
NextChar();
t = TokenId.DateTimeLiteral;
break;
在ExpressionParser.ParsePrimaryStart()
的交换机中添加以下内容:
case TokenId.DateTimeLiteral:
return ParseDateTimeLiteral();
添加此方法到ExpressionParser
:
Expression ParseDateTimeLiteral() {
ValidateToken(TokenId.DateTimeLiteral);
string s = token.text.Substring(1, token.text.Length - 2);
//
// I used InvariantCulture to force a consistent set of formatting rules.
//
DateTime d = DateTime.Parse(s, CultureInfo.InvariantCulture);
NextToken();
return Expression.Constant(d);
}
将此条目添加到Res
类:
public const string UnterminatedDateTimeLiteral = "Unterminated DateTime literal";
解决方案2:在比较中将字符串隐式转换为DateTime
如果您不想为DateTime
字面量使用特殊的语法,您可以对ExpressionParser.ParseComparison()
进行如下修改,以简单地检测string
何时与DateTime
进行比较,并解析该点的日期:
else if (IsEnumType(left.Type) || IsEnumType(right.Type)) {
// existing code here
}
else {
//
// Begin added code
//
if (IsDateTime(left.Type) && IsStringLiteral(right) ||
IsStringLiteral(left) && IsDateTime(right.Type))
{
if (left.Type == typeof(string))
left = Expression.Constant(DateTime.Parse((string)((ConstantExpression)left).Value, CultureInfo.InvariantCulture));
else
right = Expression.Constant(DateTime.Parse((string)((ConstantExpression)right).Value, CultureInfo.InvariantCulture));
}
//
// End added code
//
CheckAndPromoteOperands(isEquality ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures),
op.text, ref left, ref right, op.pos);
}
并添加以下方法:
static bool IsDateTime(Type type) {
return GetNonNullableType(type) == typeof(DateTime);
}
static bool IsStringLiteral(Expression e) {
var c = e as ConstantExpression;
return c != null && c.Value is string;
}