使用String.IsNullOrEmpty(string)和Nhibernate创建动态Linq表达式。
使用String.IsNullOrEmpty(string)和Nhibernate创建动态Linq表达式。
我正在尝试使用表达式创建一个动态的nhibernate查询。使用Contains、StartsWith和EndsWith等函数没有问题,但使用IsNullOrEmpty似乎无法工作。下面是一些上下文:
Contains:
MethodInfo contains = typeof(string).GetMethod("Contains", new Type[] { typeof(string) }); ParameterExpression param = Expression.Parameter(typeof(MyType), "x"); Expression expression = null; PropertyInfo info = typeof(MyType).Property("MyStringProperty"); expression = Expression.Property(param, info); expression = Expression.Call(expression, info, Expression.Constant("Does it contain this string", typeof(string)));
在所有此操作结束后,表达式在调试器中等于:
expression = { x.MyStringProperty };
然后我将其转换为Lambda表达式:
var finalExpression = Expression.Lambda<Func<MyType, bool>>(expression, param);
最终表达式是这样的:
x => x.MyStringProperty.Contains("Does it contain this string");
当我通过nhibernate运行它时,它正好做我想要做的事情。但是对于IsNullOrEmpty,我有以下内容:
MethodInfo isNull = typeof(string).GetMethod("IsNullOrEmpty", new Type[] { typeof(string) } ); ParameterExpression param = Expression.Parameter(typeof(MyType), "x"); PropertyInfo info = typeof(MyType).GetProperty("MyStringProperty"); expression = Expression.Property(param, info); expression = Expression.Call(isNull, expression);
所有操作结束后,表达式等于:
expression = { IsNullOrEmpty(x.MyStringProperty) }
在Lambda转换后,它变成了:
finalExpression = { x => IsNullOrEmpty(x) }
这看起来正好如它应该(尽管我承认它应该读为string.IsNullOrEmpty(x)),但是当我通过nhibernate运行它时,我会得到以下错误:
NotSupportedException Message: Boolean IsNullOrEmpty(System.String)
有人知道为什么吗?如果我运行非动态查询并手写像这样的where子句,它可以无问题运行:
nhibernateDataProvider.Where(x => string.IsNullOrEmpty(x.MySTringProperty));
有人知道为什么吗?如何解决这个问题?
编辑:
为什么我要做这个:
在我的Web层中,我显示了x.MyStringProperty数据给用户。他们有过滤这个属性的能力,我想能够通过这些字符串方法来过滤数据。Web层简单地传回这些函数:
BeginsWith EndsWith Contains IsNullOrEmpty NotIsNullOrEmpty
从这些名称中,我可以直接使用上面的代码将它们转换为字符串方法。由于这一点,我可以传递任何我在我的Web层中拥有的类型,并能够过滤任何字符串属性。这非常强大,因为对于我数百个类的所有类,我可以有一个方法来处理每个类的过滤。如上所述,它与非静态方法一起工作,因为构建的表达式为:
x -> x.MyStringProperty.NonStaticMethod("SomeFilterValue");
在Expression.Call的文档中明确表示,您也应该能够使用静态方法来实现此操作。由于对于非静态方法有效,因此必须有一种方法来使用IsNullOrEmpty使表达式如下所示:
x -> string.StaticMethod(x.MyStringProperty)
我敢打赌,如果我创建如下表达式,它会起作用:
x -> x.MySTringProperty == null || x.MySTringProperty == ""
但这是一个变通方法,作为一个程序员,我感到有原则必须找出如何使用静态方法来实现上述操作。
我不确定您在尝试实现什么。 动态表达式查询语言已经存在:它是内置的LINQ提供程序。但让我们尝试提供更多有关其实现的信息。
当内置的Linq提供程序接收到表示查询的表达式树时,会使用访问者模式将其转换为SQL(中间步骤为HQL)。其中一个强大的部分是由接口 IHqlGeneratorForMethod
表示的方法检查。
public interface IHqlGeneratorForMethod { IEnumerableSupportedMethods { get; } HqlTreeNode BuildHql(MethodInfo method, Expression targetObject , ReadOnlyCollection arguments, HqlTreeBuilder treeBuilder , IHqlExpressionVisitor visitor); }
这里的重要部分是一组实现者:
DictionaryItemGenerator DictionaryContainsKeyGenerator EqualsGenerator BoolEqualsGenerator MathGenerator AnyHqlGenerator AllHqlGenerator MinHqlGenerator MaxHqlGenerator CollectionContainsGenerator HqlGeneratorForExtensionMethod StartsWithGenerator // StartsWith EndsWithGenerator // EndsWith ContainsGenerator // Contains ToLowerGenerator ToUpperGenerator SubStringGenerator IndexOfGenerator ReplaceGenerator TrimGenerator
正如您所看到的,它们负责Contains
、EndsWith
和StartsWith
。但在其中找不到IsNullOrEmpty。
换句话说,无论在全局中您在做什么,您的IsNullOrEmpty都应该以这样的语句结束:
.Where(x => x.MyProperty == null || x.MyProperty == "")