[英]How to get entity save order from Entity Framework Core
When Entity Framework Core bulk saves all your changes, it uses an internal priority computed by all your foreign keys to make sure everything is saved in the right order to prevent foreign key errors.当 Entity Framework Core 批量保存所有更改时,它使用由所有外键计算的内部优先级来确保以正确的顺序保存所有内容,以防止外键错误。
How can I get this priority list from Entity Framework?如何从 Entity Framework 获取此优先级列表?
I'm constructing a synchronizer that moves a lot of entities between servers.我正在构建一个在服务器之间移动大量实体的同步器。 I need to send the entities in the right order because I cannot save everything in one go due to memory usage.我需要以正确的顺序发送实体,因为由于 memory 的使用,我无法将所有内容保存在一个 go 中。 I want to bulk save entities from the stream, but at the moment I got issues with foreign keys.我想从 stream 批量保存实体,但目前我遇到了外键问题。 It would be nice if I could use the work done by Entity Framework to get the correct save order.如果我可以使用 Entity Framework 完成的工作来获得正确的保存顺序,那就太好了。
I've done some digging inside EF Core, and found that the sorting is done inside CommandBatchPreparer.我在 EF Core 中进行了一些挖掘,发现排序是在 CommandBatchPreparer 中完成的。 This class has a protected method called TopologicalSort that sorts all queued commands for execution order.这个 class 有一个名为 TopologicalSort 的受保护方法,它对所有排队的命令进行排序以按执行顺序。 I could inherit from CommandBatchPreparer, but this is very specialized for ModificationCommand.我可以从 CommandBatchPreparer 继承,但这对于 ModificationCommand 来说非常专业。 So in my case it's not possible.所以在我的情况下这是不可能的。
I've prepared my own entity sorter based on that information.我已经根据这些信息准备了自己的实体分类器。 Keep in mind that entities that have internal self referencing properties with ForeignKey need extra handling to sort based on internal references.请记住,具有带 ForeignKey 的内部自引用属性的实体需要额外处理才能根据内部引用进行排序。
public class DbContextEntityProperty
{
public PropertyInfo Property { get; set; }
public Type Type { get; set; }
public bool SelfReferencing { get; set; }
public IEntityType EntityType { get; set; }
}
public List<DbContextEntityProperty> SortedEntityProperties()
{
var result = new List<DbContextEntityProperty>();
var properties = GetType().GetProperties()
.Where(o => o.PropertyType.IsGenericType && (o.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>)))
.Select(o =>
{
var t = o.PropertyType.GetGenericArguments()[0];
return new DbContextEntityProperty { Property = o, Type = null, SelfReferencing = false,EntityType = Model.FindEntityType(t) };
})
.ToDictionary(o => o.EntityType);
Action<DbContextEntityProperty> recursiveDependencyTraverser = null;
recursiveDependencyTraverser = (p) =>
{
if (p.Type == null)
{
p.Type = p.EntityType.ClrType;
foreach (var et in p.EntityType.GetDeclaredForeignKeys().Select(o => o.PrincipalEntityType))
if (et.ClrType == p.Type)
p.SelfReferencing = true;
else if (properties.TryGetValue(et, out var nextP))
recursiveDependencyTraverser(nextP);
result.Add(p);
}
};
foreach (var p in properties.Values)
recursiveDependencyTraverser(p);
return result;
}
To programmatically determine the foreign keys in your context you can use something like要以编程方式确定上下文中的外键,您可以使用类似
Dictionary<string, Dictionary<string, IEnumerable<IForeignKey>>> foreignKeys = new Dictionary<string, Dictionary<string, IEnumerable<IForeignKey>>>();
IEnumerable<IEntityType> ets = dbContext.Model.GetEntityTypes();
foreach (var (et, prop) in from IEntityType et in ets
let entityProps = et.GetProperties()
from IProperty prop in entityProps
select (et, prop))
{
IEnumerable<IForeignKey> fks = et.FindForeignKeys(prop);
if(fks.Any())
{
if (!foreignKeys.Keys.Contains(et.DisplayName()))
{
foreignKeys.Add(et.DisplayName(), new Dictionary<string, IEnumerable<IForeignKey>>());
}
foreignKeys[et.DisplayName()].Add(prop.Name, fks);
}
}
This will create a dictionary of tables names, each dictionary entry will be a dictionary of fields and the foreign keys attached to the field ie这将创建一个表名字典,每个字典条目将是一个字段字典和附加到该字段的外键,即
IEnumerable<IForeignKey> fks = foreignKeys["TableName"]["FieldName"];
From this you should be able to work out what data is required for you to insert your records into the database.由此,您应该能够计算出将记录插入数据库所需的数据。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.