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