【问题标题】:Extract information from rows that are connected to each other with id从以 id 相互连接的行中提取信息
【发布时间】:2021-06-19 20:46:59
【问题描述】:

在sqlite中,有一个表叫pathparts,这个表包含3列

rowId | parent | pathpart

行如下

rowId | parent | pathpart
--------------------------
1     | null   | manifests
100   | 1      | h
154   | 100    | publisher
184   | 154    | appname
654   | 184    | version
985   | 654    | url
1452  | 1      | f
1460  | 1452   | publisher
2456  | 1460   | appname
3456  | 2456   | url

如您所见,每一行都包含一个父行,它将其连接到另一行并且它们最终都连接到父级 1(这是 Winget 软件的 Microsoft database)如果我们将这些行按顺序放在一起,它会创建一个包的规格,例如:

h\microsoft\vscode\1.0.1.0\url.yml

我可以使用以下代码提取此信息

var query =
    from item in msixDB.IdsMSIXTable
    from manifest in msixDB.Set<ManifestMSIXTable>().Where(e => e.id == item.rowid)
    
    from yml in msixDB.PathPartsMSIXTable.Where(e => e.rowid == manifest.pathpart).Take(1).DefaultIfEmpty()
    from pathPartVersion in msixDB.PathPartsMSIXTable.Where(e => e.rowid == yml.parent).Take(1).DefaultIfEmpty()
    from pathPartAppName in msixDB.PathPartsMSIXTable.Where(e => e.rowid == pathPartVersion.parent).Take(1).DefaultIfEmpty()
    from pathPartPublisher in msixDB.PathPartsMSIXTable.Where(e => e.rowid == pathPartAppName.parent).Take(1).DefaultIfEmpty()
    from pathPart in msixDB.PathPartsMSIXTable.Where(e => e.rowid == pathPartPublisher.parent).Take(1).DefaultIfEmpty()
    from version in msixDB.VersionsMSIXTable.Where(e => e.rowid == manifest.version).Take(1).DefaultIfEmpty()
    select new ManifestTable
    {
        PackageId = item.id,
        YamlName = $@"{pathPart.pathpart}\{pathPartPublisher.pathpart}\{pathPartAppName.pathpart}\{pathPartVersion.pathpart}\{yml.pathpart}",
        Version = version.version
    };

mydb.ManifestTable.AddRange(await query.ToListAsync());

但问题是这些行没有固定的编号,所以我写的代码不起作用。 例如,在上表中,如您所见,其中一个包包含 4 行,另一个包含 3 行。 我需要一种方法来跟踪父行以到达父 1 并提取信息。

更新:

[Table("pathparts")]
public class PathPartsMSIXTable
{
    [Key]
    public long rowid { get; set; }
    public long parent { get; set; }
    public string pathpart { get; set; }

}

更新 2:

我稍微修改了你写的代码,结果如下

from manifest in msixDB.Set<ManifestMSIXTable>().Where(e => e.id == item.rowid)

                from yml in msixDB.PathPartsMSIXTable.Where(e => e.rowid == manifest.pathpart)
                
                from pathPart in pathCte.Where(e => e.rowid == yml.parent && e.parent == null)
                from version in msixDB.VersionsMSIXTable.Where(e => e.rowid == manifest.version)
                select new ManifestTable
                {
                    PackageId = item.id,
                    YamlName = $@"{pathPart.path}\{yml.pathpart}",
                    Version = version.version
                };

现在一切正常,除了一些行没有添加

微软数据库

将项目添加到数据库后

如您所见,只添加了 1 行


更新 3

我注意到有些项目是重复存储在数据库中的,如何防止基于 ID 和版本号的重复信息? 我使用了 Distinct 方法,但出现错误

System.AggregateException: 'One or more errors occurred. (Sequence 'value(LinqToDB.EntityFrameworkCore.LinqToDBForEFToolsDataConnection).GetTable().SelectMany(item => value(LinqToDB.EntityFrameworkCore.LinqToDBForEFToolsDataConnection).GetTable().Where(e => (e.id == item.rowid)), (item, manifest) => new <>f__AnonymousType0`2(item = item, manifest = manifest)).SelectMany(<>h__TransparentIdentifier0 => value(LinqToDB.EntityFrameworkCore.LinqToDBForEFToolsDataConnection).GetTable().Where(e => (e.rowid == <>h__TransparentIdentifier0.manifest.pathpart)), (<>h__TransparentIdentifier0, yml) => new <>f__AnonymousType1`2(<>h__TransparentIdentifier0 = <>h__TransparentIdentifier0, yml = ...

query = query.Distinct(new GenericCompare<ManifestTable>(x => x.PackageId));
            var data = await query.ToArrayAsyncLinqToDB();
            mydb.AddRange(data);

【问题讨论】:

  • 我感觉你为你的任务选择了糟糕的 ORM。对于这种情况,您需要 CTE。如果你OK,我会准备这个扩展的示例:github.com/linq2db/linq2db.EntityFrameworkCore,注意我是这个扩展的创建者。
  • @SvyatoslavDanyliv 我对数据库的工作不多,所以我做得不够,我很好
  • 你能用PathPartsMSIXTable类定义更新问题吗?
  • @SvyatoslavDanyliv 完成

标签: c# entity-framework-core


【解决方案1】:

对于此类任务,您需要 EF 任何版本都不支持的递归 CTE。我建议使用扩展 linq2db.EntityFrameworkCore,它可以将此类功能引入现有的 EF Core 项目。

class PathCte
{
    public long rowid { get; set; }
    public long? parent { get; set; }
    public string path { get; set; }
}
using var msixDB = new MSIXContext();

var mydb = new HWGContext();

await mydb.Database.EnsureDeletedAsync();
await mydb.Database.EnsureCreatedAsync();

using var db = msixDB.CreateLinqToDbConnection();

var pathCte = db.GetCte<PathCte>(cte => (
        from pathPart in msixDB.PathPartsMSIXTable
        select new PathCte
        {
            rowid = pathPart.rowid,
            parent = pathPart.parent,
            path = pathPart.pathpart
        }
    )
    .Concat(
        from pathPart in msixDB.PathPartsMSIXTable
        from child in cte.Where(child => child.parent == pathPart.rowid)
        select new PathCte
        {
            rowid = child.rowid,
            parent = pathPart.parent,
            path = pathPart.pathpart + "\\" + child.path
        }       
    )
);

var query =
    from item in msixDB.IdsMSIXTable
    from manifest in msixDB.Set<ManifestMSIXTable>().Where(e => e.id == item.rowid)

    from yml in msixDB.PathPartsMSIXTable.Where(e => e.rowid == manifest.pathpart)
    from pathPartVersion in msixDB.PathPartsMSIXTable.Where(e => e.rowid == yml.parent)
    from pathPartAppName in msixDB.PathPartsMSIXTable.Where(e => e.rowid == pathPartVersion.parent)
    from pathPartPublisher in msixDB.PathPartsMSIXTable.Where(e => e.rowid == pathPartAppName.parent)

    from pathPart in pathCte.Where(e => e.rowid == pathPartPublisher.parent && e.parent == null)
    from version in msixDB.VersionsMSIXTable.Where(e => e.rowid == manifest.version)
    select new ManifestTable
    {
        PackageId = item.id,
        YamlName = $@"{pathPart.path}\{pathPartPublisher.pathpart}\{pathPartAppName.pathpart}\{pathPartVersion.pathpart}\{yml.pathpart}",
        Version = version.version
    };

var data = await query.ToArrayAsyncLinqToDB();

mydb.AddRange(data);
await mydb.SaveChangesAsync();

【讨论】:

  • 我也必须使用LinqToDBForEFTools.Initialize();吗?
  • 谢谢,但我在mydb.BulkCopy(query.ToLinqToDB()); Microsoft.Data.Sqlite.SqliteException: 'SQLite Error 19: 'UNIQUE constraint failed: manifests.Id'.' 中遇到了错误,我也在PathCte 中将int 更改为long
  • 我在行尾添加了.Take(1).DefaultIfEmpty() 现在我收到另一个错误Database do not support CROSS/OUTER APPLY join required by the query.
  • 1) 你。可以使用 EF SaveChanges。 2) 为什么添加了 Take(1).DeefaultIfEmpty()?
  • 我应该删除mydb.BulkCopy 并使用SaveChanges?这个问题是我之前你回答的问题的延续stackoverflow.com/questions/67900436/…所以我根据你之前的回答添加了它
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多