按外键依赖顺序

时间:2018-01-21 01:45:30

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

我正在研究一种通过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();
}

1 个答案:

答案 0 :(得分:0)

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