Pergunta : A linha price = co?.price ?? 0,
no código a seguir me dá o erro acima. mas se eu remover ?
de co.?
que excelentes trabalhos. Eu estava tentando seguir este exemplo do MSDN em que eles estão usando ?
on-line. select new { person.FirstName, PetName = subpet?.Name ?? String.Empty };
Portanto, parece que preciso entender quando usar ?
com ??
e quando não.
Erro :
uma árvore de expressão lambda não pode conter um operador de propagação 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,
....
....
};
....
....
}
Respostas:
O exemplo que você citou usa LINQ to Objects, onde as expressões lambda implícitas na consulta são convertidas em delegados ... enquanto você está usando EF ou semelhante, com
IQueryable<T>
consultas, onde as expressões lambda são convertidas em árvores de expressão . Árvores de expressão não suportam o operador condicional nulo (ou tuplas).Basta fazer do jeito antigo:
price = co == null ? 0 : (co.price ?? 0)
(Acredito que o operador de coalescência nula seja adequado em uma árvore de expressão.)
fonte
np()
método. Consulte github.com/StefH/System.Linq.Dynamic.Core/wiki/NullPropagationO código para o qual você vincula usa
List<T>
.List<T>
implementa,IEnumerable<T>
mas nãoIQueryable<T>
. Nesse caso, a projeção é executada na memória e?.
funciona.Você está usando alguns
IQueryable<T>
, que funcionam de maneira muito diferente. ParaIQueryable<T>
, uma representação da projeção é criada e seu provedor LINQ decide o que fazer com ela em tempo de execução. Por motivos de compatibilidade com versões anteriores,?.
não pode ser usado aqui.Dependendo do seu provedor de LINQ, você pode usar simples
.
e ainda não obter nenhumNullReferenceException
.fonte
?.
não estariam preparados para lidar de?.
maneira razoável.?.
é um novo operador, não? Portanto, o código antigo não seria usado?.
e, portanto, não seria quebrado. Os provedores Linq não estão preparados para lidar com muitas outras coisas, como métodos CLR.?.
. O novo código pode estar usando provedores LINQ antigos, que estão preparados para lidar com métodos CLR que eles não reconhecem (lançando uma exceção), uma vez que eles se encaixam perfeitamente no modelo de objeto de árvore de expressão existente. Tipos de nó de árvore de expressão completamente novos não se encaixam.A resposta de Jon Skeet estava certa, no meu caso eu estava usando
DateTime
para minha aula de Entidade. Quando tentei usar like(a.DateProperty == null ? default : a.DateProperty.Date)
Eu tive o erro
Property 'System.DateTime Date' is not defined for type 'System.Nullable`1[System.DateTime]' (Parameter 'property')
Então, eu precisava mudar
DateTime?
para minha classe de entidade e(a.DateProperty == null ? default : a.DateProperty.Value.Date)
fonte
Embora a árvore de expressão não suporte a propagação nula do C # 6.0, o que podemos fazer é criar um visitante que modifique a árvore de expressão para uma propagação nula segura, assim como o operador faz!
Aqui é minha:
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); } } }
Ele passa nos seguintes testes:
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"); } }
fonte