gpt4 book ai didi

c# - 表达式树中的可空比较

转载 作者:太空狗 更新时间:2023-10-29 22:26:24 26 4
gpt4 key购买 nike

我正在尝试根据用户提供的一组收集条件在 Entity Framework 中构建动态 LINQ 查询。最终,这将包括更复杂的行为,但目前我只有一个字段名称和值的列表,我想返回字段名称具有这些值的所有记录。

我的基本结构是这样的:

public IEnumerable<ThingViewModel> getMythings(SelectionCriteria selectionCriteria)
{
var predicate = constructPredicate<Thing>(selectionCriteria);
var things = this.dbContext.Things.Where(predicate).ToList();
return Mapper.Map<List<Thing>, List<ThingViewModel>>(things);
}

所有有趣的工作都在 constructPredicate() 中:

private static Expression<Func<T, bool>> constructPredicate<T>(SelectionCriteria selectionCriteria)
{
// using Pete Montgomery's PredicateBuilder:
// http://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/

var predicate = PredicateBuilder.True<T>();

foreach (var item in selectionCriteria.andList)
{
// Accessing foreach values in closures can result in unexpected results.
// http://stackoverflow.com/questions/14907987/access-to-foreach-variable-in-closure
var fieldName = item.fieldName;
var fieldValue = item.fieldValue;

var parameter = Expression.Parameter(typeof (T), "t");
var property = Expression.Property(parameter, fieldName);
var value = Expression.Constant(fieldValue);

var lambda = buildCompareLambda<T>(property, value, parameter);

predicate = predicate.And(lambda);
}

return predicate;
}

所有这些看起来都是一个非常合理的结构,但我在 buildCompareLambda() 中遇到了困难。我没有看到以通用方式执行此操作的方法,我必须为不同的类型创建不同的方法。我从处理字符串开始,这很简单。接下来我尝试处理整数,但事实证明数据库中的整数字段可以为空,这引入了全新的复杂性。

到目前为止,我的 buildCompareLambda():

private static Expression<Func<T, bool>> buildCompareLambda<T>(
MemberExpression property,
ConstantExpression value,
ParameterExpression parameter)
{
Expression<Func<T, bool>> lambda = null;
if (property.Type == typeof (string))
lambda = buildStringCompareLambda<T>(property, value, parameter);
else if (property.Type.IsGenericType && Nullable.GetUnderlyingType(property.Type) != null)
lambda = buildNullableCompareLambda<T>(property, value, parameter);

if (lambda == null)
throw new Exception(String.Format("SelectrionCriteria cannot handle property type '{0}'", property.Type.Name));

return lambda;
}

正如我所说,buildStringCompareLambda 非常简单:

private static Expression<Func<T, bool>> buildStringCompareLambda<T>(
MemberExpression property,
ConstantExpression value,
ParameterExpression parameter)
{
var equalsMethod = typeof (string).GetMethod("Equals",
new[] {typeof (string), typeof (string)});
var comparison = Expression.Call(equalsMethod, property, value);
return Expression.Lambda<Func<T, bool>>(comparison, parameter);
}

但是 buildNullableCompareLambda() 变得丑陋了:

private static Expression<Func<T, bool>> buildNullableCompareLambda<T>(MemberExpression property,
ConstantExpression value,
ParameterExpression parameter)
{
var underlyingType = Nullable.GetUnderlyingType(property.Type);

if (underlyingType == typeof (int) || underlyingType == typeof (Int16) || underlyingType == typeof (Int32) ||
underlyingType == typeof (Int64) || underlyingType == typeof (UInt16) || underlyingType == typeof (UInt32) ||
underlyingType == typeof (UInt64))
{
var equalsMethod = underlyingType.GetMethod("Equals", new[] {underlyingType});

var left = Expression.Convert(property, underlyingType);
var right = Expression.Convert(value, underlyingType);

var comparison = Expression.Call(left, equalsMethod, new Expression[] {right});

return Expression.Lambda<Func<T, bool>>(comparison, parameter);
}

return null;
}

我打算在 buildNullableCompareLambda() 中添加对更多类型的支持,并将每种类型的处理移动到一个函数中,以便可以从 buildCompareLambda() 和 buildNullableCompareLambda() 调用相同的代码。但那是为了将来——目前我坚持比较整数。目前,我正在将属性和值都转换为基础类型,因为我不想为每个整数类型使用单独的函数,并且我不希望用户必须关心 EF 是否为字段建模转换为 Int16 或 Int32。这对非空字段有效。

我一直在浏览 SO,并找到了一些答案,这就是我所得到的,但我所看到的关于处理可空类型的答案都没有真正对我有用,因为它们不'真正处理空值。

在我的例子中,如果用户向我传递一个选择标准,其中一个项目应该等于 null,我想返回该字段为 null 的记录,而这一点是关于将双方都转换为基本类型似乎不起作用。我收到“对象引用未设置到对象的实例”异常。

在 SQL 中,我想要的是“WHERE field IS NULL”,如果值为 null,或者“WHERE field = 'value'”,如果不是。而且我不知道如何将这种替代方法构建到表达式树中。

有什么想法吗?

补充:有人建议我使用 Expression.Equal()。

这样,我的循环就变成了:

private static Expression<Func<T, bool>> constructPredicate<T>(SelectionCriteria selectionCriteria)
{
var predicate = PredicateBuilderEx.True<T>();
var foo = PredicateBuilder.True<T>();

foreach (var item in selectionCriteria.andList)
{
var fieldName = item.fieldName;
var fieldValue = item.fieldValue;

var parameter = Expression.Parameter(typeof (T), "t");
var property = Expression.Property(parameter, fieldName);
var value = Expression.Constant(fieldValue);

var comparison = Expression.Equal(property, value);
var lambda = Expression.Lambda<Func<T, bool>>(comparison, parameter);

predicate = PredicateBuilderEx.And(predicate, lambda);
}
return predicate;
}

那是行不通的。我得到一个异常(exception):

The binary operator Equal is not defined for the types 'System.Nullable`1[System.Int16]' and 'System.Int16'.

最佳答案

通常情况下,这里的人可能不会完全想出答案,但他们已经知道了大部分的方法,并且足够接近,我可以算出其余的。

Expression.Equal 要求两个参数的类型相同。如果一个可为空,则它们都需要为空。但这并不难处理:

private static Expression<Func<T, bool>> constructPredicate<T>(SelectionCriteria selectionCriteria)
{
var predicate = PredicateBuilderEx.True<T>();
var foo = PredicateBuilder.True<T>();

foreach (var item in selectionCriteria.andList)
{
var fieldName = item.fieldName;
var fieldValue = item.fieldValue;

var parameter = Expression.Parameter(typeof (T), "t");
var property = Expression.Property(parameter, fieldName);
var value = Expression.Constant(fieldValue);
var converted = Expression.Convert(value, property.Type);

var comparison = Expression.Equal(property, converted);
var lambda = Expression.Lambda<Func<T, bool>>(comparison, parameter);

predicate = PredicateBuilderEx.And(predicate, lambda);
}

return predicate;
}

谢谢大家。

关于c# - 表达式树中的可空比较,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23920450/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com