C#动态Linq三元运算符

时间:2018-06-21 05:19:25

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

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

它适用于不可为空的日期字段,但对于可为空的日期字段,我们需要检查值,因为使用.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条件会生成,而不是?:三元运算符

感谢您的帮助。

4 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:3)

假设我们有两个表达式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。显示为Conditional的{​​{1}}表达式确实等同于C#IIF运算符)

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

最终起作用的是

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);
相关问题