【问题标题】:Sort entities in foreign key dependency order按外键依赖顺序对实体进行排序
【发布时间】:2018-01-21 01:45:29
【问题描述】:

我正在研究一种通过 EF Core 为表集合播种的机制。如何按外键依赖的顺序对 DbContext 的实体进行排序?由于某些表具有外键,因此某些表需要在其他表之前播种。似乎有一个简单的 API 可以执行这种排序,但我所能找到的只是几个复杂的递归 T-SQL 实现。

我确信对于这种普遍存在的问题有一个通用的解决方案,我只是无法找到它/不知道如何搜索它。

更新: 因为 EF Core 似乎还不支持Merge/Upsert/AddOrUpdate,所以我将沿着特定于数据库(在本例中为 MySQL 系列)的原始 SQL 查询的路径前进。所以,我有一个像下面这样的方法,我想调用一个实体集合,但是我需要按照TEntityType 的外键依赖关系的顺序调用这个方法。由于它是原始 SQL 查询而不是 EF API,因此我认为 EF 不能以正确的依赖顺序自动神奇地更新表(响应 @Gert's comment)。

private static void InsertOnDuplicateKeyUpdate<TEntityType>(DbContext dbContext) where TEntityType : class
{
    var entityType = dbContext.Model.FindEntityType(typeof(TEntityType));
    var properties = GetPropertiesLessValueGeneratedTimestamps(entityType);
    var columns = string.Join(", ", properties.Select(x => x.Name));
    var values = CreateValues<TEntityType>(properties);
    var updates = CreateUpdates(properties);
    var rawSqlString = "INSERT INTO " + entityType.Relational().TableName + " (" + columns + ") VALUES " +
                        values + " ON DUPLICATE KEY UPDATE " + updates;
    dbContext.Set<TEntityType>().FromSql(rawSqlString);
    dbContext.SaveChanges();
}

【问题讨论】:

  • 你能发布代码或解释你到目前为止所尝试的吗?你有任何错误吗?
  • 这里没有 API,你只需要了解你的实体模型并按照正确的顺序编写你的种子代码。
  • 谢谢,@DavidG。这就是我害怕的答案。我真的希望有一些更自动化的东西,每次添加新实体时我都不必碰我的种子算法(这应该是不常见的,当然)。
  • 当您将所有相关条目(设置了正确的 FK/导航属性)添加到上下文时,EF 对这些插入的顺序有两个非常简单的规则: 1. 首先插入父条目类型,因此没有在上下文中失败的 FK 约束将在数据库中失败, 2. 在此类排序中,如果 PK 类型实现 IComparable,则首先插入较低的 PK 值。至少在 EF6 之前是这种情况,我不明白他们为什么会为核心关闭它。
  • @DevilSuichiro 这意味着:EF 为您服务。 Collin:只需将实体添加到您的上下文中,确保它们之间具有正确的引用,EF 将计算出插入/更新/删除顺序。

标签: entity-framework foreign-keys .net-core relational-database entity-framework-core


【解决方案1】:

Entity Framework Core 包含根据外键依赖对实体类型进行排序所需的所有信息。在使用 EF Core 作为表顺序的来源时,我发现的唯一问题是需要以不同方式处理相同的表引用 - 例如是否允许插入标识符、禁用表触发器或其他方式。

要获取可以从中获取表名、DbSet 等的实体类型列表,请使用以下函数:

    private IList<IEntityType> GetDependentTables(IModel model)
    {
        var copied = new List<IEntityType>();
        var tables = model.GetEntityTypes().Where(x => x.GetKeys().Any()).ToList();
        while (tables.Count > 0)
        {
            var copiedCount = copied.Count;
            for (var i = 0; i < tables.Count; i++)
            {
                var table = tables[i];
                if (table.GetForeignKeys().All(x => copied.Contains(x.PrincipalEntityType) || x.DeclaringEntityType == x.PrincipalEntityType))
                {
                    copied.Add(table);
                    tables.RemoveAt(i);
                    break;
                }
            }

            if (copiedCount == copied.Count)
            {
                throw new InvalidOperationException("Circular foreign keys found in remaining tables: " + string.Join(",", tables.Select(x => x.Name)));
            }
        }

        return copied.Where(x => x.BaseType == null).ToList();
    }

用法:

var tableEntityTypes = GetDependentTables(myDbContext.Model);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-08
    • 2011-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-30
    • 2017-11-30
    相关资源
    最近更新 更多