Question : La ligne price = co?.price ?? 0,
dans le code suivant me donne l'erreur ci-dessus. mais si je retire ?
de co.?
cela fonctionne très bien. J'essayais de suivre cet exemple MSDN où ils utilisent ?
en ligne select new { person.FirstName, PetName = subpet?.Name ?? String.Empty };
Donc, il semble que j'ai besoin de comprendre quand utiliser ?
avec ??
et quand ne pas le faire.
Erreur :
une arborescence d'expression lambda ne peut pas contenir d'opérateur de propagation nul
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,
....
....
};
....
....
}
Réponses:
L'exemple que vous citiez utilise LINQ to Objects, où les expressions lambda implicites de la requête sont converties en délégués ... alors que vous utilisez EF ou similaire, avec des
IQueryable<T>
requêtes, où les expressions lambda sont converties en arborescences d'expression . Les arborescences d'expressions ne prennent pas en charge l'opérateur conditionnel nul (ou les tuples).Faites-le simplement à l'ancienne:
price = co == null ? 0 : (co.price ?? 0)
(Je pense que l'opérateur de fusion nul convient parfaitement dans un arbre d'expression.)
la source
np()
méthode. Voir github.com/StefH/System.Linq.Dynamic.Core/wiki/NullPropagationLe code auquel vous liez utilise
List<T>
.List<T>
implémenteIEnumerable<T>
mais pasIQueryable<T>
. Dans ce cas, la projection est exécutée en mémoire et?.
fonctionne.Vous en utilisez
IQueryable<T>
, ce qui fonctionne très différemment. PourIQueryable<T>
, une représentation de la projection est créée et votre fournisseur LINQ décide quoi en faire au moment de l'exécution. Pour des raisons de compatibilité descendante,?.
ne peut pas être utilisé ici.En fonction de votre fournisseur LINQ, vous pourrez peut-être utiliser plain
.
et ne pas en obtenirNullReferenceException
.la source
?.
n'auraient pas été préparés à gérer?.
de manière raisonnable.?.
est un nouvel opérateur non? Donc, l'ancien code ne serait pas utilisé?.
et donc pas cassé. Les fournisseurs Linq ne sont pas préparés à gérer beaucoup d'autres choses telles que les méthodes CLR.?.
. Le nouveau code peut utiliser d'anciens fournisseurs LINQ, qui sont prêts à gérer les méthodes CLR qu'ils ne reconnaissent pas (en lançant une exception), car ceux-ci s'intègrent parfaitement dans le modèle d'objet d'arborescence d'expression existant. Les types de nœuds d'arbre d'expression complètement nouveaux ne rentrent pas dans.La réponse de Jon Skeet était juste, dans mon cas, j'utilisais
DateTime
pour ma classe Entity. Quand j'ai essayé d'utiliser comme(a.DateProperty == null ? default : a.DateProperty.Date)
J'ai eu l'erreur
Property 'System.DateTime Date' is not defined for type 'System.Nullable`1[System.DateTime]' (Parameter 'property')
J'avais donc besoin de changer
DateTime?
pour ma classe d'entité et(a.DateProperty == null ? default : a.DateProperty.Value.Date)
la source
Bien que l'arborescence d'expression ne prenne pas en charge la propagation Null C # 6.0, ce que nous pouvons faire est de créer un visiteur qui modifie l'arborescence d'expression pour une propagation nulle sûre, tout comme l'opérateur le fait!
Voici le mien:
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); } } }
Il passe les tests suivants:
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"); } }
la source