首页 > 解决方案 > Linq 查询表达式中的空值检查

问题描述

我有一个基于工作方法的查询,如下所示:

string param = request.QRBarang.Split('~')[1];
var a = await _db.A.FirstOrDefaultAsync(a => a.IdA == param);
var b = await _db.B.FirstOrDefaultAsync(b => b.IdB1 == a.IdA);
var c = await _db.C.FirstOrDefaultAsync(c => c.IdC1 == b.IdB2);
var d = c != null
         ? await _db.D.FirstOrDefaultAsync(d => d.IdD == c.IdC2)
         : null;
var dto = new DTOFin { ...};

我仍在学习 LINQ 并尝试将语法转换为查询表达式,但是当 c 的值为 null 时总是会出错。这是我的尝试:

var dto = (from a in _db.A
           join b in _db.B on a.IdA equals b.IdB1
           join c in _db.C on b.IdB2 equals c.IdC1
           join d in _db.D on c.IdC2 equals d.IdD
           where a.IdA == param && object.Equals(c.IdC2, d.IdD)
           select new DTOFin { ...}).FirstOrDefaultAsync();

我也尝试过使用join c in _db.C on b.IdB2 equals c?.IdC1但产生了

错误 CS8072 表达式树 lambda 可能不包含空传播运算符。

我应该如何在查询表达式中编写第一个等效语法?

标签: c#linqentity-framework-core.net-5

解决方案


假设您正确配置了导航属性,那么这应该可以工作:

(我注意到 EF6 和 EF Core 的脚手架模板(用于从现有数据库自动生成实体类)已经为您完成了这项工作。)

(另外,我真的不喜欢 C# 的关键字风格的 Linq 表达式,因为为了做几乎任何事情,你需要附加 Linq 扩展方法,这在美学上与关键字风格的表达式相冲突。我想不出有什么好处老实说,如今使用关键字风格的 Linq 表达式的理由)。

我假设_db是你的DbContext.

using System.Linq;
using System.Data.Entity; // for EF6
using Microsoft.EntityFrameworkCore.Query; // for EF Core

A a = await _db.A
    .Include( a => a.B )
    .Include( a => a.B.C )
    .Include( a => a.B.C.D )
    .Where( a => a.IdA == param )
    .SingleOrDefaultAsync();

// b, c, and d can then be gotten from A:
B b = a.B;
C c = a.B.C;
D d = a.B.C?.D; // It looks like A.B and B.C are INNER JOIN while C.D is LEFT OUTER JOIN. Linq will use the correct JOIN type (INNER, LEFT, etc) based on your model configuration. You cannot tell from the query alone.

return new DTOFin() { ... };

如果您没有设置导航属性(并且您应该...),那么您可以手动执行 Joins - 但它明显更粗糙,因为 Linq 的.Join方法从未打算直接使用,因为您应该使用 Navigation而是属性。

  • 请注意,由于此 Linq 查询与 Entity Framework 一起使用,这意味着您的查询必须在 SQL 中表示...
    • 这意味着某些限制适用:例如不使用?.操作员 - 这是您的问题。
    • 其他限制包括不能使用您自己的自定义谓词函数(除非它们也是Expression<Func<>>),因为您不能只将 C# 代码放入 SQL 查询中。

我相信下面的查询应该可以工作,但如果不了解您的 EF 模型配置和数据库设计的更多信息,我不能肯定地说 - 您在开题帖中没有提供足够的详细信息。

var a_b_c_d = await _db.A
    .Join( _db.B, a     => a.IdA       , b => b.IdB1, ( a    , b ) => new { a, b } )
    .Join( _db.C, a_b   => a_b.b.IdB2  , c => c.IdC1, ( a_b  , c ) => new { a_b.a, a_b.b, c } )
    .Join( _db.D, a_b_c => a_b_c.c.IdC2, d => d.IdD , ( a_b_c, d ) => new { a_b_c.a, a_b_c.b, a_b_c.c, d } )
    .Where( a_b_c_d => a_b_c_d.a.IdA == param )
    .SingleOrDefaultAsync();

A a = a_b_c_d.a;
B b = a_b_c_d.b;
C c = a_b_c_d.c;
D d = a_b_c_d.d;

return new DTOFin() { ... };

推荐阅读