Frage : Die Zeile price = co?.price ?? 0,
im folgenden Code gibt mir den obigen Fehler. aber wenn ich entfernen ?
aus co.?
es funktioniert gut. Ich habe versucht , folgen in diesem MSDN - Beispiel , wo sie verwenden ?
auf Linie select new { person.FirstName, PetName = subpet?.Name ?? String.Empty };
Also, es scheint , ich muss verstehen , wenn die Verwendung ?
mit ??
und wann nicht.
Fehler :
Ein Ausdrucksbaum Lambda darf keinen Null-Propagierungsoperator enthalten
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,
....
....
};
....
....
}
Antworten:
In dem Beispiel, aus dem Sie zitiert haben, wird LINQ to Objects verwendet, wobei die impliziten Lambda-Ausdrücke in der Abfrage in Delegaten konvertiert werden. Während Sie EF oder ähnliches verwenden, verwenden Sie
IQueryable<T>
Abfragen, bei denen die Lambda-Ausdrücke in Ausdrucksbäume konvertiert werden . Ausdrucksbäume unterstützen den bedingten Nulloperator (oder die Tupel) nicht.Mach es einfach auf die alte Art:
price = co == null ? 0 : (co.price ?? 0)
(Ich glaube, der Null-Koaleszenz-Operator ist in einem Ausdrucksbaum in Ordnung.)
quelle
np()
Methode verwenden. Siehe github.com/StefH/System.Linq.Dynamic.Core/wiki/NullPropagationDer Code, mit dem Sie verknüpfen, wird verwendet
List<T>
.List<T>
implementiertIEnumerable<T>
aber nichtIQueryable<T>
. In diesem Fall wird die Projektion im Speicher ausgeführt und?.
funktioniert.Sie verwenden einige
IQueryable<T>
, was sehr unterschiedlich funktioniert. BeispielsweiseIQueryable<T>
wird eine Darstellung der Projektion erstellt, und Ihr LINQ-Anbieter entscheidet, was zur Laufzeit damit geschehen soll.?.
Kann aus Gründen der Abwärtskompatibilität hier nicht verwendet werden.Abhängig von Ihrem LINQ-Anbieter können Sie möglicherweise Plain verwenden
.
und erhalten immer noch keineNullReferenceException
.quelle
?.
wären nicht bereit gewesen,?.
in angemessener Weise damit umzugehen .?.
ist ein neuer Betreiber nein? So würde alter Code nicht verwendet?.
und somit nicht kaputt gehen. Linq-Anbieter sind nicht bereit, viele andere Dinge wie CLR-Methoden zu handhaben.?.
. Neuer Code kann mit alten LINQ - Anbieter sein, die sind CLR Verfahren hergestellt behandeln sie nicht erkennen (durch Auslösen einer Ausnahme), da solche fit gut in die bestehende Ausdrucksbaum - Objektmodell. Völlig neue Ausdrucksbaumknotentypen passen nicht hinein.Jon Skeets Antwort war richtig, in meinem Fall habe ich sie
DateTime
für meine Entity-Klasse verwendet. Als ich versuchte, wie zu verwenden(a.DateProperty == null ? default : a.DateProperty.Date)
Ich hatte den Fehler
Property 'System.DateTime Date' is not defined for type 'System.Nullable`1[System.DateTime]' (Parameter 'property')
Also musste ich
DateTime?
für meine Entitätsklasse und ändern(a.DateProperty == null ? default : a.DateProperty.Value.Date)
quelle
Während der Ausdrucksbaum die C # 6.0-Nullweitergabe nicht unterstützt, können wir einen Besucher erstellen, der den Ausdrucksbaum für eine sichere Nullweitergabe ändert, genau wie der Operator!
Hier ist mein:
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); } } }
Folgende Tests werden bestanden:
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"); } }
quelle