使用String.IsNullOrEmpty(string)和Nhibernate创建动态Linq表达式。

41 浏览
0 Comments

使用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 == ""

但这是一个变通方法,作为一个程序员,我感到有原则必须找出如何使用静态方法来实现上述操作。

admin 更改状态以发布 2023年5月23日
0
0 Comments

\n\n7年后,在StackOverflow上找不到正确的答案,使用了System.Linq.Expressions, 版本为4.2.2.0,在像 Expression.IsTrue(Expression.Call(isNull, expression)) 这样的方法中找到了解决方法,同时对于非空值也分别使用Expression.IsFalse。这把交易对我来说解决了问题。

0
0 Comments

我不确定您在尝试实现什么。 动态表达式查询语言已经存在:它是内置的LINQ提供程序。但让我们尝试提供更多有关其实现的信息。

当内置的Linq提供程序接收到表示查询的表达式树时,会使用访问者模式将其转换为SQL(中间步骤为HQL)。其中一个强大的部分是由接口 IHqlGeneratorForMethod 表示的方法检查。

public interface IHqlGeneratorForMethod
{
    IEnumerable SupportedMethods { 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

正如您所看到的,它们负责ContainsEndsWithStartsWith。但在其中找不到IsNullOrEmpty。

换句话说,无论在全局中您在做什么,您的IsNullOrEmpty都应该以这样的语句结束:

.Where(x => x.MyProperty == null || x.MyProperty == "")

0