Вопрос : строка price = co?.price ?? 0,
в следующем коде дает мне указанную выше ошибку. но если я удалю ?
из co.?
него все работает нормально. Я пытался следовать этому примеру MSDN , где они используют ?
на линии select new { person.FirstName, PetName = subpet?.Name ?? String.Empty };
Таким образом, кажется , мне нужно понять , когда следует использовать ?
с , ??
а когда нет.
Ошибка :
лямбда-выражение дерева не может содержать нулевой оператор распространения
public class CustomerOrdersModelView
{
public string CustomerID { get; set; }
public int FY { get; set; }
public float? price { get; set; }
....
....
}
public async Task<IActionResult> ProductAnnualReport(string rpt)
{
var qry = from c in _context.Customers
join ord in _context.Orders
on c.CustomerID equals ord.CustomerID into co
from m in co.DefaultIfEmpty()
select new CustomerOrdersModelView
{
CustomerID = c.CustomerID,
FY = c.FY,
price = co?.price ?? 0,
....
....
};
....
....
}
Ответы:
Пример, из которого вы цитируете, использует LINQ to Objects, где неявные лямбда-выражения в запросе преобразуются в делегаты ... тогда как вы используете EF или аналогичный, с
IQueryable<T>
запросами, где лямбда-выражения преобразуются в деревья выражений . Деревья выражений не поддерживают условный оператор null (или кортежи).Просто сделайте это по-старому:
price = co == null ? 0 : (co.price ?? 0)
(Я считаю, что оператор объединения с нулевым значением подходит для дерева выражений.)
источник
np()
метод. См. Github.com/StefH/System.Linq.Dynamic.Core/wiki/NullPropagationКод, на который вы ссылаетесь, использует
List<T>
.List<T>
орудияIEnumerable<T>
но нетIQueryable<T>
. В этом случае проекция выполняется в памяти и?.
работает.Вы используете некоторые
IQueryable<T>
, которые работают по-другому. ДляIQueryable<T>
создается представление проекции, и ваш поставщик LINQ решает, что с ним делать во время выполнения.?.
Здесь нельзя использовать по причинам обратной совместимости .В зависимости от вашего поставщика LINQ вы можете использовать простой
.
и все равно не получить егоNullReferenceException
.источник
?.
, не были готовы к обработке?.
каким-либо разумным способом.?.
разве нового оператора нет? Таким образом, старый код не будет использоваться?.
и, следовательно, не будет сломан. Поставщики Linq не готовы обрабатывать многие другие вещи, такие как методы CLR.?.
. Новый код может быть использование старых поставщиков LINQ, которые будут подготовлены для обработки методов CLR они не признают (бросая исключение), так как те , вписывались в существующем объекте модель выражения дерева. Абсолютно новые типы узлов дерева выражений не подходят.Ответ Джона Скита был правильным, в моем случае я использовал
DateTime
для своего класса Entity. Когда я пытался использовать лайк(a.DateProperty == null ? default : a.DateProperty.Date)
У меня была ошибка
Property 'System.DateTime Date' is not defined for type 'System.Nullable`1[System.DateTime]' (Parameter 'property')
Поэтому мне нужно было изменить
DateTime?
класс сущности и(a.DateProperty == null ? default : a.DateProperty.Value.Date)
источник
Хотя дерево выражений не поддерживает распространение NULL в C # 6.0, мы можем создать посетителя, который изменяет дерево выражений для безопасного распространения NULL, как это делает оператор!
Вот мой:
public class NullPropagationVisitor : ExpressionVisitor { private readonly bool _recursive; public NullPropagationVisitor(bool recursive) { _recursive = recursive; } protected override Expression VisitUnary(UnaryExpression propertyAccess) { if (propertyAccess.Operand is MemberExpression mem) return VisitMember(mem); if (propertyAccess.Operand is MethodCallExpression met) return VisitMethodCall(met); if (propertyAccess.Operand is ConditionalExpression cond) return Expression.Condition( test: cond.Test, ifTrue: MakeNullable(Visit(cond.IfTrue)), ifFalse: MakeNullable(Visit(cond.IfFalse))); return base.VisitUnary(propertyAccess); } protected override Expression VisitMember(MemberExpression propertyAccess) { return Common(propertyAccess.Expression, propertyAccess); } protected override Expression VisitMethodCall(MethodCallExpression propertyAccess) { if (propertyAccess.Object == null) return base.VisitMethodCall(propertyAccess); return Common(propertyAccess.Object, propertyAccess); } private BlockExpression Common(Expression instance, Expression propertyAccess) { var safe = _recursive ? base.Visit(instance) : instance; var caller = Expression.Variable(safe.Type, "caller"); var assign = Expression.Assign(caller, safe); var acess = MakeNullable(new ExpressionReplacer(instance, IsNullableStruct(instance) ? caller : RemoveNullable(caller)).Visit(propertyAccess)); var ternary = Expression.Condition( test: Expression.Equal(caller, Expression.Constant(null)), ifTrue: Expression.Constant(null, acess.Type), ifFalse: acess); return Expression.Block( type: acess.Type, variables: new[] { caller, }, expressions: new Expression[] { assign, ternary, }); } private static Expression MakeNullable(Expression ex) { if (IsNullable(ex)) return ex; return Expression.Convert(ex, typeof(Nullable<>).MakeGenericType(ex.Type)); } private static bool IsNullable(Expression ex) { return !ex.Type.IsValueType || (Nullable.GetUnderlyingType(ex.Type) != null); } private static bool IsNullableStruct(Expression ex) { return ex.Type.IsValueType && (Nullable.GetUnderlyingType(ex.Type) != null); } private static Expression RemoveNullable(Expression ex) { if (IsNullableStruct(ex)) return Expression.Convert(ex, ex.Type.GenericTypeArguments[0]); return ex; } private class ExpressionReplacer : ExpressionVisitor { private readonly Expression _oldEx; private readonly Expression _newEx; internal ExpressionReplacer(Expression oldEx, Expression newEx) { _oldEx = oldEx; _newEx = newEx; } public override Expression Visit(Expression node) { if (node == _oldEx) return _newEx; return base.Visit(node); } } }
Он проходит следующие тесты:
private static string Foo(string s) => s; static void Main(string[] _) { var visitor = new NullPropagationVisitor(recursive: true); Test1(); Test2(); Test3(); void Test1() { Expression<Func<string, char?>> f = s => s == "foo" ? 'X' : Foo(s).Length.ToString()[0]; var fBody = (Expression<Func<string, char?>>)visitor.Visit(f); var fFunc = fBody.Compile(); Debug.Assert(fFunc(null) == null); Debug.Assert(fFunc("bar") == '3'); Debug.Assert(fFunc("foo") == 'X'); } void Test2() { Expression<Func<string, int>> y = s => s.Length; var yBody = visitor.Visit(y.Body); var yFunc = Expression.Lambda<Func<string, int?>>( body: yBody, parameters: y.Parameters) .Compile(); Debug.Assert(yFunc(null) == null); Debug.Assert(yFunc("bar") == 3); } void Test3() { Expression<Func<char?, string>> y = s => s.Value.ToString()[0].ToString(); var yBody = visitor.Visit(y.Body); var yFunc = Expression.Lambda<Func<char?, string>>( body: yBody, parameters: y.Parameters) .Compile(); Debug.Assert(yFunc(null) == null); Debug.Assert(yFunc('A') == "A"); } }
источник