Pertanyaan : Baris price = co?.price ?? 0,
dalam kode berikut memberi saya kesalahan di atas. tapi jika saya menghapus ?
dari co.?
itu bekerja dengan baik. Saya mencoba mengikuti contoh MSDN ini di mana mereka menggunakan secara ?
online. select new { person.FirstName, PetName = subpet?.Name ?? String.Empty };
Jadi, sepertinya saya perlu memahami kapan harus menggunakan ?
dengan ??
dan kapan tidak.
Kesalahan :
pohon ekspresi lambda tidak boleh berisi operator penyebaran null
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,
....
....
};
....
....
}
Jawaban:
Contoh yang Anda kutip dari menggunakan LINQ ke Objek, di mana ekspresi lambda implisit dalam kueri diubah menjadi delegasi ... saat Anda menggunakan EF atau serupa, dengan
IQueryable<T>
kueri, di mana ekspresi lambda diubah menjadi pohon ekspresi . Pohon ekspresi tidak mendukung operator bersyarat null (atau tupel).Lakukan saja dengan cara lama:
price = co == null ? 0 : (co.price ?? 0)
(Saya yakin operator penggabungan nol baik-baik saja di pohon ekspresi.)
sumber
np()
metode ini. Lihat github.com/StefH/System.Linq.Dynamic.Core/wiki/NullPropagationKode yang Anda tautkan untuk digunakan
List<T>
.List<T>
mengimplementasikanIEnumerable<T>
tetapi tidakIQueryable<T>
. Dalam hal ini, proyeksi dijalankan dalam memori dan?.
berfungsi.Anda menggunakan beberapa
IQueryable<T>
, yang bekerja sangat berbeda. UntukIQueryable<T>
, representasi proyeksi dibuat, dan penyedia LINQ Anda memutuskan apa yang harus dilakukan dengannya saat runtime. Untuk alasan kompatibilitas mundur,?.
tidak dapat digunakan di sini.Bergantung pada penyedia LINQ Anda, Anda mungkin bisa menggunakan biasa
.
dan masih belum mendapatkannyaNullReferenceException
.sumber
?.
tidak akan siap untuk menangani?.
dengan cara yang masuk akal.?.
apakah ada operator baru? Jadi kode lama tidak akan digunakan?.
dan dengan demikian tidak akan rusak. Penyedia Linq tidak siap untuk menangani banyak hal lain seperti metode CLR.?.
. Kode baru mungkin menggunakan penyedia LINQ tua, yang sedang disiapkan untuk menangani metode CLR mereka tidak mengenali (dengan melemparkan pengecualian), karena mereka cocok baik ke dalam model objek pohon ekspresi yang ada. Jenis simpul pohon ekspresi yang benar-benar baru tidak cocok.Jawaban Jon Skeet benar, dalam kasus saya, saya menggunakan
DateTime
untuk kelas Entitas saya. Ketika saya mencoba menggunakan suka(a.DateProperty == null ? default : a.DateProperty.Date)
Saya mengalami kesalahan
Property 'System.DateTime Date' is not defined for type 'System.Nullable`1[System.DateTime]' (Parameter 'property')
Jadi saya perlu mengubah
DateTime?
kelas entitas saya dan(a.DateProperty == null ? default : a.DateProperty.Value.Date)
sumber
Sementara pohon ekspresi tidak mendukung penyebaran null C # 6.0, apa yang dapat kita lakukan adalah membuat pengunjung yang mengubah pohon ekspresi untuk propagasi null yang aman, seperti yang dilakukan operator!
Ini milikku:
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); } } }
Ini lolos dalam tes berikut:
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"); } }
sumber