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.Date
是DateTime?
(或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;
}
假设我们有两个表达式 left
和 right
,其中 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
。显示为 IIF
的 Conditional
表达式确实是 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);
我想对空值进行动态检查。 我想创建一个只比较日期字段的日期部分的 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.Date
是DateTime?
(或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;
}
假设我们有两个表达式 left
和 right
,其中 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
。显示为 IIF
的 Conditional
表达式确实是 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);