Linq-to-sql不会产生多个外连接?

Gof*_*off 7 c# outer-join linq-to-sql

关于linq-to-sql我有一个奇怪的问题,我真的试过搜索它.我正在设计一个sql数据库,最近刚尝试从中检索一个对象.

问题在于多个连接.我的所有表都使用identity-columns作为主键.

Db设计如下:

MasterTable:Id(主键,标识列,int),MasterColumn1(nvarchar(50))

Slave1:Id(主键,标识列,int),MasterId(int,主键 - > MasterTable Id),SlaveCol1

Slave2:Id(主键,标识列,int),MasterId(int,主键 - > MasterTable Id),SlaveColumn2

使用的代码:

var db = new TestDbDataContext() { Log = Console.Out };
var res = from f in db.MasterTables
          where f.MasterColumn1 == "wtf"
          select new
                     {
                         f.Id, 
                         SlaveCols1 = f.Slave1s.Select(s => s.SlaveCol1),
                         SlaveCols2 = f.Slave2s.Select(s => s.SlaveColumn2)
                     };
foreach (var re in res)
{
    Console.Out.WriteLine(
        re.Id + " "
      + string.Join(", ", re.SlaveCols1.ToArray()) + " "
      + string.Join(", ", re.SlaveCols2.ToArray())
    );
}
Run Code Online (Sandbox Code Playgroud)

日志是:

SELECT [t0].[Id], [t1].[SlaveCol1], (
   SELECT COUNT(*)
   FROM [FR].[Slave1] AS [t2]
   WHERE [t2].[MasterId] = [t0].[Id]
   ) AS [value]
FROM [FR].[MasterTable] AS [t0]
LEFT OUTER JOIN [FR].[Slave1] AS [t1] ON [t1].[MasterId] = [t0].[Id]
WHERE [t0].[MasterColumn1] = @p0
ORDER BY [t0].[Id], [t1].[Id]
-- @p0: Input NVarChar (Size = 3; Prec = 0; Scale = 0) [wtf]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 3.5.30729.5420
SELECT [t0].[SlaveColumn2]
   FROM [FR].[Slave2] AS [t0]
   WHERE [t0].[MasterId] = @x1
-- @x1: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 3.5.30729.5420
1 SlaveCol1Wtf SlaveCol2Wtf
Run Code Online (Sandbox Code Playgroud)

为什么哦为什么不做两个外连接呢?我真的非常关心这个,因为我有一个更大的数据库,有许多表引用同一个表(都有一对多的关系),并且有20个选择往返数据库服务器并不是最佳的!

正如我所说的那样.我可以通过使用显式外连接产生想要的结果,如下所示:

var db = new TestDbDataContext() { Log = Console.Out };
var res = from f in db.MasterTables
          join s1 in db.Slave1s on f.Id equals s1.MasterId into s1Tbl
          from s1 in s1Tbl.DefaultIfEmpty()
          join s2 in db.Slave2s on f.Id equals s2.MasterId into s2Tbl
          from s2 in s2Tbl.DefaultIfEmpty()
          where f.MasterColumn1 == "wtf"
          select new { f.Id, s1.SlaveCol1, s2.SlaveColumn2 };
foreach (var re in res)
{
    Console.Out.WriteLine(re.Id + " " + re.SlaveCol1 + " " + re.SlaveColumn2);
}
Run Code Online (Sandbox Code Playgroud)

但我想使用Linq-To-Sql提供的参考而不是手动连接!怎么样?

-----------编辑-----------------

我也试过像这样的预取:

using (new DbConnectionScope())
{
    var db = new TestDbDataContext() { Log = Console.Out };
    DataLoadOptions loadOptions = new DataLoadOptions();
    loadOptions.LoadWith<MasterTable>(c => c.Slave1s);
    loadOptions.LoadWith<MasterTable>(c => c.Slave2s);
    db.LoadOptions = loadOptions;

    var res = from f in db.MasterTables
              where f.MasterColumn1 == "wtf"
              select f;
    foreach (var re in res)
    {
        Console.Out.WriteLine(re.Id + " " + 
            string.Join(", ", re.Slave1s.Select(s => s.SlaveCol1).ToArray()) + " " + 
            string.Join(", ", re.Slave2s.Select(s => s.SlaveColumn2).ToArray()));
    }
}
Run Code Online (Sandbox Code Playgroud)

相同的结果=(

Jim*_*ley 1

使用 LoadOptions 的预取选项并遍历关联而不是显式联接,您走在正确的轨道上,但是由于您尝试从 MasterTable 进行多个 1-M 导航,因此您将有效地在 Slave1 和 Slave1 之间创建笛卡尔积Slave2记录。因此,LINQ to SQL 会忽略您的加载选项并延迟加载每个子项的子记录。

您可以通过删除第二个子加载选项来稍微优化这一点。生成的查询现在将执行单个请求,返回 MasterTable 和 Slave1,但随后延迟加载每个 Slave2。如果执行以下操作,您应该会看到相同的结果:

var res = from f in db.MasterTables
          where f.MasterColun1 == "wtf"
          select new 
          {
             f.Id,
             Cols1 = f.Slave1s.Select(s => s.SlaveCol1).ToArray()
             Cols2 = f.Slave2s.Select(s => s.SlaveColumn2).ToArray()
          }
Run Code Online (Sandbox Code Playgroud)

您应该看到 MasterTables 和 Slave1s 之间存在左连接,然后延迟加载 Slave2s,以避免在 SQL 的扁平化结果中出现 Slave1 和 Slave2 之间的笛卡尔积。