C# 如何使用EF将记录插入到不相关的表中

C# 如何使用EF将记录插入到不相关的表中,c#,.net,entity-framework,entity-framework-6,C#,.net,Entity Framework,Entity Framework 6,我有一个Comment表,可以链接到许多有注释的不同实体,但由于某些原因,我没有链接这些表。相反,Comment包含TableReferenceId和EntryReferenceIdTableReferenceId只是一个int,我们可以在应用层检查注释所引用的实体/表,而EntryReferenceId是一个int,它引用注释所属的所述实体/表中的特定条目 按表和条目引用查询这样的注释是可以的,但在插入批量数据时,我画的是空白。例如,如果我有Vehicle实体,并且Vehicle可以有许多注释

我有一个
Comment
表,可以链接到许多有注释的不同实体,但由于某些原因,我没有链接这些表。相反,
Comment
包含
TableReferenceId
EntryReferenceId
TableReferenceId
只是一个int,我们可以在应用层检查注释所引用的实体/表,而
EntryReferenceId
是一个int,它引用注释所属的所述实体/表中的特定条目


按表和条目引用查询这样的注释是可以的,但在插入批量数据时,我画的是空白。例如,如果我有
Vehicle
实体,并且
Vehicle
可以有许多注释,那么在插入数据时,由于我还没有
VehicleId
,我将如何链接它们?这是可行的还是对链接到注释的每个表进行多对多路由更好?

如果可以避免这种情况,那么应该尝试,或者应该尝试避免支持批量插入。如果您必须这样做,那么以下任何一种模式都可能适合您

  • 分两个阶段执行批量插入,在正常导入之前,维护记录及其链接到的注释的映射或字典,然后在第一次调用
    SaveChanges()
    后,可以插入ID

  • 您可以将映射的注释存储在实体上的未绑定集合中,
    SaveChanges()
    之后,如果此集合中有任何条目,则应使用新记录的Id插入它们

  • 让我们看一下第一个选项:

    var mappedComments = new Dictionary<Vehicle,Comment[]>();
    // bulk processing, however you choose to do it
    // importantly for each item, capture the record reference and the comments
    foreach(var item in source)
    {
        Vehicle newItem;
        ... construct/parse the new Entity object
        List<Comment> newComments = new List<Comment>();
        ... parse the comments records
        // store the map
        mappedComments.Add(newItem, newComments.ToArray());
    
        // Add the entity to the context?
        db.AddToVehicles(newItem);
    }
    
    db.SaveChanges();
    
    foreach(var mapEntry in mappedComments)
    {
        var newVehicle = mapEntry.Key;
        // replace this with your actual logic of course...
        int vehicleTableReferenceId = db.TableReferences.Single(x => x.TableName == nameof(Vehicle));
        foreach(var comment in mappEntry.Value)
        {
            comment.TableReferenceId = vehicleTableReferenceId;
            comment.EntityReferenceId = newVehicle.Id; // the Id that is now populated
            db.AddToComments(comment);
        }
    }
    
    db.SaveChanges();
    
    您可以通过实现
    DbTransaction
    作用域来进一步改进这一点,以便在出现故障时回滚整批代码。这段代码本身与我在生产代码中使用的常用例程不同,因此尽管它可能无法正常工作,但这一概念在许多项目中都很有用

    public interface ICommentsToInsert
    {
        // Only necessary if your convention is NOT to use a common name for the PK
        int Id { get; }
        ICollection<Comment> CommentsToInsert { get;set;}
    }
    
    partial class Vehicle : ICommentsToInsert
    {
        [NotMapped]
        int ICommentsToInsert.Id { get => Vehicle_Id; }
        [NotMapped]
        public ICollection<Comment> CommentsToInsert { get;set; } = new HashSet<Comment>();
    }
    
    public override int SaveChanges()
    {
        var beforeStates = BeforeSaveChanges();
        int result = base.SaveChanges();
        if (AfterSaveChanges(beforeStates);
            result += base.SaveChanges();
        return results;
    }
    
    private Dictionary<DbEntityEntry, EntityState> BeforeSaveChanges()
    {
        var beforeSaveChanges = new Dictionary<DbEntityEntry, EntityState>();
        foreach( var entry in this.ChangeTracker.Entries())
        {
            //skip unchanged entries!
            if (entry.State == EntityState.Unchanged)
                continue;
            // Today, only cache the ICommentsToInsert records...
            if (entry.Entity is ICommentsToInsert)
                beforeSaveChanges.Add(entry, entry.State);
        }
        return beforeSaveChanges;
    }
    
    private bool AfterSaveChanges(Dictionary<DbEntityEntry, EntityState> statesBeforeSaveChanges)
    {
        bool moreChanges = false;
        foreach (var entry in statesBeforeChanges)
        {
            if (entry.Key.Entity is ICommentsToInsert hasComments)
            {
                if(hasComments.CommentsToInsert.Any())
                {
                    moreChanges = true;
                    // Get the Id to the TableReference, based on the name of the Entity type
                    // you would normally cache this type of lookup, rather than hitting the DB every time
                    int tableReferenceId = db.TableReferences.Single(x =
         > x.TableName == entry.Key.Entity.GetType().Name);
                    foreach (var comment in hasComments.CommentsToInsert)
                    {
                        comment.TableReferenceId = tableReferenceId;
                        comment.EntityReferenceId = hasComments.Id;
                        db.AddToComments(comment);
                    }
                }
            }
        }
        return moreChanges;
    }