左连接通过linq2db进行分组

时间:2015-05-07 11:25:57

标签: c# 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();

但我不知道如何从群组中获取文件

3 个答案:

答案 0 :(得分:2)

首先,我强烈建议您使用T4模板生成数据模型类,因此请查看此项目:linq2db/t4models。我还建议您在此处查看视频: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个查询中加载所有内容,然后在内存中手动将文件连接到目录。您可以编写自己的扩展方法来简化此操作。

答案 1 :(得分:0)

Linq的连接语法非常难以包围...我通常只使用一个小技巧来使用&#34;项目&#34;您创建的变量作为中间结果:

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)或类似的构造来完成您尝试做的事情。

答案 2 :(得分:0)

试试这个:

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 };