Pregunta : La línea price = co?.price ?? 0,
del siguiente código me da el error anterior. pero si quito ?
de co.?
lo bien que funciona. Yo estaba tratando de seguir este ejemplo de MSDN donde se están utilizando ?
en la línea select new { person.FirstName, PetName = subpet?.Name ?? String.Empty };
lo tanto, parece que necesito para entender cuándo usar ?
con ??
y cuándo no.
Error :
un árbol de expresión lambda no puede contener un operador de propagación nulo
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,
....
....
};
....
....
}
Respuestas:
El ejemplo del que estaba citando usa LINQ to Objects, donde las expresiones lambda implícitas en la consulta se convierten en delegados ... mientras que está usando EF o similar, con
IQueryable<T>
consultas, donde las expresiones lambda se convierten en árboles de expresión . Los árboles de expresión no admiten el operador condicional nulo (o tuplas).Solo hazlo de la manera antigua:
price = co == null ? 0 : (co.price ?? 0)
(Creo que el operador de fusión nula está bien en un árbol de expresión).
fuente
np()
método. Ver github.com/StefH/System.Linq.Dynamic.Core/wiki/NullPropagationEl código al que se vincula usa
List<T>
.List<T>
implementaIEnumerable<T>
pero noIQueryable<T>
. En ese caso, la proyección se ejecuta en memoria y?.
funciona.Estás usando algunos
IQueryable<T>
, que funcionan de manera muy diferente. ParaIQueryable<T>
, se crea una representación de la proyección y su proveedor LINQ decide qué hacer con ella en tiempo de ejecución. Por razones de compatibilidad con versiones anteriores,?.
no se puede utilizar aquí.Dependiendo de su proveedor de LINQ, es posible que pueda usar simple
.
y aún no obtener ningunoNullReferenceException
.fuente
?.
no habrían estado preparados para manejarlos?.
de manera razonable.?.
es un nuevo operador, ¿no? Por lo tanto, el código antiguo no se usaría?.
y, por lo tanto, no se rompería. Los proveedores de Linq no están preparados para manejar muchas otras cosas, como los métodos CLR.?.
. Es posible que el código nuevo esté utilizando proveedores LINQ antiguos, que están preparados para manejar métodos CLR que no reconocen (lanzando una excepción), ya que encajan perfectamente en el modelo de objetos de árbol de expresión existente. Los tipos de nodo de árbol de expresión completamente nuevos no encajan.La respuesta de Jon Skeet fue correcta, en mi caso la estaba usando
DateTime
para mi clase de Entidad. Cuando traté de usar like(a.DateProperty == null ? default : a.DateProperty.Date)
Tuve el error
Property 'System.DateTime Date' is not defined for type 'System.Nullable`1[System.DateTime]' (Parameter 'property')
Entonces necesitaba cambiar
DateTime?
para mi clase de entidad y(a.DateProperty == null ? default : a.DateProperty.Value.Date)
fuente
Si bien el árbol de expresión no admite la propagación nula de C # 6.0, lo que podemos hacer es crear un visitante que modifique el árbol de expresión para una propagación nula segura, ¡tal como lo hace el operador!
Aquí esta el mio:
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); } } }
Pasa las siguientes pruebas:
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"); } }
fuente