通过 linq2db 左加入分组
Left join with grouping via linq2db
我有以下表格
class Directory
{
public long Id { get; set;}
public string Path { get; set;}
public IEnumerable<File> Files { get; set;}
}
class File
{
public long Id { get; set;}
public long DirectoryId { get; set; }
public string Name { get; set; }
public Directory Directory { get; set; }
}
如何在一个查询中使用左连接和 linq2db 获取按 ID 分组的目录和适当的文件?
我觉得应该是这样的
var query = from d in db.Direcories
join f in db.Files on d.Id equals f.DirectoryId into items
from i in items.DefaultIfEmpty()
group i by new { d } into g
select new { Directory = g.Key, Files = ????? };
var result = query.ToList();
但我不知道如何从组中获取文件
Linq 的连接语法很难让人理解...我通常会应用一些小技巧来仅使用您创建的 "items" 变量作为中间结果:
var query = from d in db.Direcories
join f in db.Files on d.Id equals f.DirectoryId into items
from f in items.DefaultIfEmpty()
group f by d into g
select new { Directory = g.Key, Files = g /* optionally add .ToList() or .ToEnumerable() */ };
var result = query.ToList();
尽管我怀疑如果您定义了正确的关系属性,使用 db.Directories.Include(d => d.Files)
或类似结构可以通过更简单的方法来完成您尝试做的事情。
首先,我强烈建议使用 T4 模板生成您的数据模型 类,所以请看一下这个项目:linq2db/t4models. I also recommend to look at the videos here: https://github.com/linq2db/linq2db/wiki.
用 T4 生成我的数据模型后,它看起来像这样:
public partial class TestDb : LinqToDB.Data.DataConnection
{
public ITable<Directory> Directories { get { return this.GetTable<Directory>(); } }
public ITable<File> Files { get { return this.GetTable<File>(); } }
}
[Table(Schema="dbo", Name="Directory")]
public partial class Directory
{
[PrimaryKey, Identity] public int ID { get; set; } // int
[Column, NotNull ] public string Path { get; set; } // varchar(max)
#region Associations
/// <summary>
/// FK_File_Directory_BackReference
/// </summary>
[Association(ThisKey="ID", OtherKey="DirectoryID", CanBeNull=true, IsBackReference=true)]
public List<File> Files { get; set; }
#endregion
}
[Table(Schema="dbo", Name="File")]
public partial class File
{
[PrimaryKey, Identity] public int ID { get; set; } // int
[Column, NotNull ] public int DirectoryID { get; set; } // int
[Column, NotNull ] public string Name { get; set; } // varchar(max)
#region Associations
/// <summary>
/// FK_File_Directory
/// </summary>
[Association(ThisKey="DirectoryID", OtherKey="ID", CanBeNull=false, KeyName="FK_File_Directory", BackReferenceName="Files")]
public Directory Directory { get; set; }
#endregion
}
现在有几种方法可以加载目录及其文件。这是一些:
[Test] // 1 query to load directories + separate queries to load files for each directory
public void Test()
{
LinqToDB.Common.Configuration.Linq.AllowMultipleQuery = true;
using(var db = new TestDb())
{
var directoriesWithFiles = db.Directories.LoadWith(d => d.Files).ToList();
}
}
[Test] // same as above, but manually
public void Test2()
{
using(var db = new TestDb())
{
var directories = db.Directories.ToList();
foreach (var d in directories)
{
d.Files = db.Files.Where(f => f.DirectoryID == d.ID).ToList();
}
}
}
[Test] // if you want only 2 queries to the database
public void Test3()
{
using (var db = new TestDb())
{
var dict = new Dictionary<int, List<File>>();
foreach(var file in db.Files)
{
if(!dict.ContainsKey(file.DirectoryID))
dict.Add(file.DirectoryID, new List<File> { file });
else
dict[file.DirectoryID].Add(file);
}
var directories = db.Directories.ToList();
foreach (var d in directories)
{
List<File> files;
d.Files = dict.TryGetValue(d.ID, out files) ? files : new List<File>();
}
}
}
或者您可以只进行连接,在 1 个查询中加载所有内容,然后将文件手动连接到内存中的目录。您可以编写自己的扩展方法来简化此操作。
试一试:
var query = from fileAndFolder in
(
from d in db.Directories
from f in db.Files.Where(ff => ff.DirectoryId == d.Id).DefaultIfEmpty()
select new { d, f }
)
group fileAndFolder by fileAndFolder.d into g
select new { DirectoryId = g.Key, Files = g };
我有以下表格
class Directory
{
public long Id { get; set;}
public string Path { get; set;}
public IEnumerable<File> Files { get; set;}
}
class File
{
public long Id { get; set;}
public long DirectoryId { get; set; }
public string Name { get; set; }
public Directory Directory { get; set; }
}
如何在一个查询中使用左连接和 linq2db 获取按 ID 分组的目录和适当的文件?
我觉得应该是这样的
var query = from d in db.Direcories
join f in db.Files on d.Id equals f.DirectoryId into items
from i in items.DefaultIfEmpty()
group i by new { d } into g
select new { Directory = g.Key, Files = ????? };
var result = query.ToList();
但我不知道如何从组中获取文件
Linq 的连接语法很难让人理解...我通常会应用一些小技巧来仅使用您创建的 "items" 变量作为中间结果:
var query = from d in db.Direcories
join f in db.Files on d.Id equals f.DirectoryId into items
from f in items.DefaultIfEmpty()
group f by d into g
select new { Directory = g.Key, Files = g /* optionally add .ToList() or .ToEnumerable() */ };
var result = query.ToList();
尽管我怀疑如果您定义了正确的关系属性,使用 db.Directories.Include(d => d.Files)
或类似结构可以通过更简单的方法来完成您尝试做的事情。
首先,我强烈建议使用 T4 模板生成您的数据模型 类,所以请看一下这个项目:linq2db/t4models. I also recommend to look at the videos here: https://github.com/linq2db/linq2db/wiki.
用 T4 生成我的数据模型后,它看起来像这样:
public partial class TestDb : LinqToDB.Data.DataConnection
{
public ITable<Directory> Directories { get { return this.GetTable<Directory>(); } }
public ITable<File> Files { get { return this.GetTable<File>(); } }
}
[Table(Schema="dbo", Name="Directory")]
public partial class Directory
{
[PrimaryKey, Identity] public int ID { get; set; } // int
[Column, NotNull ] public string Path { get; set; } // varchar(max)
#region Associations
/// <summary>
/// FK_File_Directory_BackReference
/// </summary>
[Association(ThisKey="ID", OtherKey="DirectoryID", CanBeNull=true, IsBackReference=true)]
public List<File> Files { get; set; }
#endregion
}
[Table(Schema="dbo", Name="File")]
public partial class File
{
[PrimaryKey, Identity] public int ID { get; set; } // int
[Column, NotNull ] public int DirectoryID { get; set; } // int
[Column, NotNull ] public string Name { get; set; } // varchar(max)
#region Associations
/// <summary>
/// FK_File_Directory
/// </summary>
[Association(ThisKey="DirectoryID", OtherKey="ID", CanBeNull=false, KeyName="FK_File_Directory", BackReferenceName="Files")]
public Directory Directory { get; set; }
#endregion
}
现在有几种方法可以加载目录及其文件。这是一些:
[Test] // 1 query to load directories + separate queries to load files for each directory
public void Test()
{
LinqToDB.Common.Configuration.Linq.AllowMultipleQuery = true;
using(var db = new TestDb())
{
var directoriesWithFiles = db.Directories.LoadWith(d => d.Files).ToList();
}
}
[Test] // same as above, but manually
public void Test2()
{
using(var db = new TestDb())
{
var directories = db.Directories.ToList();
foreach (var d in directories)
{
d.Files = db.Files.Where(f => f.DirectoryID == d.ID).ToList();
}
}
}
[Test] // if you want only 2 queries to the database
public void Test3()
{
using (var db = new TestDb())
{
var dict = new Dictionary<int, List<File>>();
foreach(var file in db.Files)
{
if(!dict.ContainsKey(file.DirectoryID))
dict.Add(file.DirectoryID, new List<File> { file });
else
dict[file.DirectoryID].Add(file);
}
var directories = db.Directories.ToList();
foreach (var d in directories)
{
List<File> files;
d.Files = dict.TryGetValue(d.ID, out files) ? files : new List<File>();
}
}
}
或者您可以只进行连接,在 1 个查询中加载所有内容,然后将文件手动连接到内存中的目录。您可以编写自己的扩展方法来简化此操作。
试一试:
var query = from fileAndFolder in
(
from d in db.Directories
from f in db.Files.Where(ff => ff.DirectoryId == d.Id).DefaultIfEmpty()
select new { d, f }
)
group fileAndFolder by fileAndFolder.d into g
select new { DirectoryId = g.Key, Files = g };