C# 动态 Linq 三元运算符

C# Dynamic Linq Ternary Operator

我想对空值进行动态检查。 我想创建一个只比较日期字段的日期部分的 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条件生成而不是?:三元运算符

感谢任何帮助。

我想你的p.DateDateTime?(或Nullable<DateTime>

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

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<> 定义了一个 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;
}

假设我们有两个表达式 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# ? : 的等效表达式运算符)

最终起作用的是

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);