【问题标题】:C# Dynamic Linq Ternary OperatorC# 动态 Linq 三元运算符
【发布时间】:2018-11-30 08:00:45
【问题描述】:

我想对空值进行动态检查。 我想创建一个 where 子句,它只比较日期字段的日期部分。

它适用于不可为空的日期字段,但对于可空的日期字段,我们需要检查值,因为在空数据上使用 .Date 会引发错误

让我们说

p => (p.Date.Value == null ? null : p.Date.Value.Date) == SelectedDate.Date

 p => ( p.Date.Value == null ? p.Date.Value : p.Date.Value.Date) == SelectedDate.Date

p => (p.Date.Value == null ? p.Date : p.Date.Value.Date) == SelectedDate.Date

基本上是一个空检查三元运算符,它只选择

的日期部分

我已经试过了

ConstantExpression argument = Expression.Constant(MyDateField, typeof(DateTime));
ParameterExpression parameter = Expression.Parameter(typeof(T), "p");
string field = "Date";
BinaryExpression condition = Expression.Equal(Expression.Property(parameter, field), Expression.Constant(null, typeof(DateTime?)));
ConditionalExpression ternary = Expression.Condition(condition, property, Expression.Property(property, "Date"));
Expression equalExp = Expression.Equal(ternary, argument);
lambda = Expression.Lambda<Func<T, bool>>(equalExp, parameter);

这给了我

p => (IIF((p.EventDate == null), p.EventDate.Value, p.EventDate.Value.Date) == 21-Jun-18 12:00:00 AM)

但这不起作用。 我面临的问题是

如果我在 BinaryExpression 中使用 p.Date.Value 那么它不允许,因为 .Value 使它成为 DateTime 并且 null 仅在 DateTime 中可用?

IIF 条件生成而不是?: 三元运算符

感谢任何和所有的帮助。

【问题讨论】:

  • @Amit 这里的类型是DateTime?或者Nullable
  • @SirRufo 是的,那个问题也有。我可能已经看到这里的 OP 正在使用 DateTime? ..让我再读一遍
  • @Amit 第二段:但是对于可为空的日期字段我们需要检查
  • p.Date.Value == null ? null : p.Date.Value.Date 这已经错了。 p.Date == null ? (DateTime?)null : (DateTime?)p.Date.Value.Date

标签: c# linq where-clause expression-trees dynamic-linq


【解决方案1】:

DateTime?DateTime 是不同的类型。虽然 C# 编译器有时会进行一些隐式转换(例如,当您将它们与 == 进行比较时),但对于 Lambda 表达式,您必须进行显式转换。要获取 DateTime? 的值,您必须使用 .Value 属性。

public static Expression<Func<T, bool>> MakeExpression<T>(DateTime myDateField)
{
    ConstantExpression argument = Expression.Constant(myDateField, typeof(DateTime));
    ParameterExpression parameter = Expression.Parameter(typeof(T), "p");

    string propertyName = "Date";
    Expression property = Expression.Property(parameter, propertyName);

    BinaryExpression condition = Expression.Equal(property, Expression.Constant(null, typeof(DateTime?)));
    Expression propertyValue = Expression.Property(property, nameof(Nullable<DateTime>.Value));
    Expression propertyValueDate = Expression.Property(propertyValue, nameof(DateTime.Date));
    ConditionalExpression ternary = Expression.Condition(condition, Expression.Constant(null, typeof(DateTime?)), Expression.Convert(propertyValueDate, typeof(DateTime?)));
    Expression argumentDate = Expression.Property(argument, nameof(DateTime.Date));
    Expression equalExp = Expression.Equal(ternary, Expression.Convert(argumentDate, typeof(DateTime?)));
    var lambda = Expression.Lambda<Func<T, bool>>(equalExp, parameter);
    return lambda;
}

请注意,Nullable&lt;&gt; 定义了一个 HasValue 属性,而不是将值与 null 进行比较...所以您可以:

public static Expression<Func<T, bool>> MakeExpression<T>(DateTime myDateField)
{
    ConstantExpression argument = Expression.Constant(myDateField, typeof(DateTime));
    ParameterExpression parameter = Expression.Parameter(typeof(T), "p");

    string propertyName = "Date";
    Expression property = Expression.Property(parameter, propertyName);

    Expression propertyHasvalue = Expression.Property(property, nameof(Nullable<DateTime>.HasValue));
    Expression propertyValue = Expression.Property(property, nameof(Nullable<DateTime>.Value));
    Expression propertyValueDate = Expression.Property(propertyValue, nameof(DateTime.Date));
    ConditionalExpression ternary = Expression.Condition(Expression.Not(propertyHasvalue), Expression.Constant(null, typeof(DateTime?)), Expression.Convert(propertyValueDate, typeof(DateTime?)));
    Expression argumentDate = Expression.Property(argument, nameof(DateTime.Date));
    Expression equalExp = Expression.Equal(ternary, Expression.Convert(argumentDate, typeof(DateTime?)));
    var lambda = Expression.Lambda<Func<T, bool>>(equalExp, parameter);
    return lambda;
}

【讨论】:

  • 所创建的表达式是正确的,但我收到错误消息“LINQ to Entities 不支持指定的类型成员 'Date'。仅支持初始化程序、实体成员和实体导航属性。 "我要过滤的字段可以为空。
  • @DevanshiParikh 实体框架可能不支持DateTime.Date。有关可能的解决方案,请参阅stackoverflow.com/a/9642274/613130DbFunctions 用于 EF6.0,EF.Functions 用于 EF Core
【解决方案2】:

假设我们有两个表达式leftright,其中right 类型是DateTime,我们想比较它们是否相等。

left类型为DateTime时,比较简单

left == right

left类型为DateTime?时,则

(left == (DateTime?)null ? (DateTime?)null : (DateTime?)left.Value.Date) == (DateTime?)right

我专门添加了所需的演员表。 C# 编译器会隐式执行其中的一些(如(DateTime?)null),但重要的是三元运算符结果类型应为DateTime?,因此三元运算符操作数类型相等运算符操作数类型必须为DateTime? 也是。

话虽如此,让我们将上述规则转换为代码:

static Expression<Func<T, bool>> DateEquals<T>(string memberName, DateTime value)
{
    var parameter = Expression.Parameter(typeof(T), "p");
    Expression left = Expression.PropertyOrField(parameter, memberName);
    Expression right = Expression.Constant(value.Date);
    if (left.Type == typeof(DateTime?))
    {
        var leftValue = Expression.Property(left, "Value");
        var nullValue = Expression.Constant(null, typeof(DateTime?));
        left = Expression.Condition(
            Expression.Equal(left, nullValue),
            nullValue,
            Expression.Convert(Expression.Property(leftValue, "Date"), typeof(DateTime?))
        );
        right = Expression.Convert(right, typeof(DateTime?));
    }
    var condition = Expression.Equal(left, right);
    return Expression.Lambda<Func<T, bool>>(condition, parameter);
}

(不要担心你会在调试显示中看到IIF。显示为IIFConditional 表达式确实是C# 中? : 运算符的等效表达式)

【讨论】:

    【解决方案3】:

    我猜你的p.DateDateTime?(或Nullable&lt;DateTime&gt;

    p => p.Date?.Date == SelectedDate.Date
    

    【讨论】:

    • ?. 在表达式树中不受支持。
    【解决方案4】:

    最终起作用的是

    public static Expression<Func<T, bool>> MakeExpression<T>(DateTime myDateField, string fieldName)
        {
            var parameter = Expression.Parameter(typeof(T), "p");
            var property = Expression.Property(parameter, fieldName);
            var fieldType = property.Type;
            Expression<Func<T, bool>> lambda = null;
    
            if (fieldType == typeof(DateTime?))
            {
                var truncateTimeMethod = typeof(DbFunctions).GetMethod("TruncateTime", new[] { fieldType });
                if (truncateTimeMethod != null)
                {
                    var propertyHasvalue = Expression.Property(property, nameof(Nullable<DateTime>.HasValue));
                    var truncateTimeMethodCall = Expression.Call(truncateTimeMethod, property);
                    var ternary = Expression.Condition(Expression.Not(propertyHasvalue), property, truncateTimeMethodCall);
                    var argument = Expression.Constant(myDateField.Date, typeof(DateTime?));
                    var equalExp = Expression.Equal(ternary, argument);
    
                    lambda = Expression.Lambda<Func<T, bool>>(equalExp, parameter);
                }
            }
            return lambda;
        }
    

    感谢@xanatos

    对于 .Date 功能

    var truncateTimeMethod = typeof(DbFunctions).GetMethod("TruncateTime", new[] { fieldType });
    var truncateTimeMethodCall = Expression.Call(truncateTimeMethod, property);
    

    三元运算

    var ternary = Expression.Condition(Expression.Not(propertyHasvalue), property, truncateTimeMethodCall);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-02-17
      • 2011-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多