我的系统中有一个功能,允许用户从数据中创建场景。
为此,我需要将数据从一种情况复制到另一种情况,并更改ID和外键以保持一致性。
我自己使用EFCore可以很好地完成工作,但是现在我们有更多的数据,并且每小时变得越来越慢。
然后,我尝试使用EFCore.BulkExtensions
帮助我提高性能。
我遇到的一种情况是大量插入一些带有其翻译的实体。
基本上,我具有以下类似实体:
// My book class
public class DbBook
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public string Title { get; set; }
public long ScenarioId { get; set; }
public ICollection<DbBookTranslation> BookTranslations { get; set; } = new HashSet<DbBookTranslation>();
}
// The book translation class, that contains translations for the book description in different languages
public class DbBookTranslation
{
public long BookId { get; set; }
public string Language { get; set; }
public string Description { get; set; }
}
// Config class for book translation in order to configure its key and some other properties
public class DbBookTranslationMap
{
public void Configure(EntityTypeBuilder<DbBookTranslation> builder)
{
builder.HasKey(a => new {a.Language, a.BookId});
}
}
然后,我可以将这些实体复制到新方案中。 流程如下:
// I open a transaction
using (var tr = _ctx.Database.BeginTransaction())
{
// I get all the books from the old scenario
var oldBooks = ctx.DbBooks.Where(o => o.ScenarioId == oldScenarioId).AsNoTracking().ToList();
// I iterate through oldBooks setting their ID to a new one and the ScenarioId to the
// new one and add on a list for further inserting
var newBooks = new List<DbBooks>();
var i = 0;
for (var b in oldBooks)
{
b.ScenarioId = newScenarioId;
b.Id = i++;
newBooks.Add(b);
}
// Then I bulk insert it
// !!THIS IS WHERE IT FAILS!!
var bulkConfig = new BulkConfig { PreserveInsertOrder = true, SetOutputIdentity = true };
_ctx.BulkInsert(newBooks, bulkConfig);
// Then I iterate through the translations, setting the new Id with some reflection
var newTranslations = new List<DbBookTranslation>();
for (var b in newBooks) {
for (var t in b.BookTranslations) {
t.BookId = b.Id; // Its done with reflection in rly
newTranslations.Add(t);
}
}
// Then bulk insert the translations
_ctx.BulkInsert(newTranslations, bulkConfig);
tr.Commit();
}
当我尝试制作BulkInsertions
中的第一个DbBook
时,它失败并显示以下错误:
Value cannot be null. Parameter name: member
at System.Linq.Expressions.Expression.MakeMemberAccess(Expression expression, MemberInfo member)
at EFCore.BulkExtensions.TableInfo.OrderBy[T](Expression`1 source, String ordering)
at EFCore.BulkExtensions.TableInfo.QueryOutputTable[T](DbContext context, String sqlQuery)
at EFCore.BulkExtensions.TableInfo.LoadOutputData[T](DbContext context, IList`1 entities)
at EFCore.BulkExtensions.SqlBulkOperation.Merge[T](DbContext context, IList`1 entities, TableInfo tableInfo, OperationType operationType, Action`1 progress)
at EFCore.BulkExtensions.DbContextBulkExtensions.BulkInsert[T](DbContext context, IList`1 entities, BulkConfig bulkConfig, Action`1 progress)
at MyProjectData.Services.ScenarioCopyService.CopyScenarioEntities[T](IEnumerable`1 entitiesEnumerable, Int64 newScenarioId) in C:\Projects\VSTS\MyProject-dotnet\MyProject\MyProject.Data\Services\ScenarioCopyService.cs:line 204
at MyProject.Data.Services.ScenarioCopyService.CopyScenario(Int64 newScenarioId, Int64 oldScenarioId) in C:\Projects\VSTS\MyProject-dotnet\MyProject\MyProject.Data\Services\ScenarioCopyService.cs:line 77
奇怪的是,如果我不将任何BulkConfig
传递给第一个BulkInsert
,它将起作用。实体已保存。但是随后,第二个BulkInsert
失败并显示UniqueKeyViolation
,因为BookIds
的设置不正确。
我尝试调试EFCore.BulkExtensions
代码,但无法成功。
任何帮助表示赞赏。谢谢!