我有一个简单的应用程序,用于监听来自服务总线的事件。根据传入事件,它将使用 Entity Framework Core 更新数据库中的记录。应用程序将在查询数据库并更新实体属性之前批量传入消息。
以下只是示例代码:
var dbResults = _context.SampleDbSet
.Where(s => s.Active)
.ToListAsync();
var dbResultsLookup = dbResults
.ToDictionary(
entity => $"{entity.Id}_{entity.Name}",
v => v
);
foreach (var message in incomingMessages)
{
if (dbResultsLookup.TryGetValue($"{message.Id}_{message.Name}", out var dbEntity))
{
dbEntity.PropertyOne = message.PropertyOne;
dbEntity.LastChangeTime = DateTimeOffset.Now;
}
}
await _context.SaveChangesAsync(cancel);
SQL 缓存计划中发生的情况是:
dm_exec_cached_plans
中得到一个唯一的准备好的计划,这是因为有时 PropertyOne
没有被修改,实体框架不会将其包含在参数化更新中。示例如下: @p76 decimal(9,2)
仅在 PropertyOne
更新时添加。(
@p1 int,@p2 int,@p0 datetimeoffset(7),@p4 int,@p5 int,@p3 datetimeoffset(7),
@p7 int,@p8 int,@p6 datetimeoffset(7),@p10 int,
@p11 int,@p9 datetimeoffset(7),@p13 int,@p14 int,@p12 datetimeoffset(7),
@p16 int,@p17 int,@p15 datetimeoffset(7),@p19 int,@p20 int,@p18 datetimeoffset(7),@p22 int,@p23 int,@p21 datetimeoffset(7),
@p25 int,@p26 int,@p24 datetimeoffset(7),@p28 int,@p29 int,@p27 datetimeoffset(7),@p31 int,@p32 int,@p30 datetimeoffset(7),
@p34 int,@p35 int,@p33 datetimeoffset(7),@p37 int,@p38 int,@p36 datetimeoffset(7),@p40 int,@p41 int,@p39 datetimeoffset(7),
@p43 int,@p44 int,@p42 datetimeoffset(7),@p46 int,@p47 int,@p45 datetimeoffset(7),
@p77 int,@p78 int,@p75 datetimeoffset(7),@p76 decimal(9,2)
)SET NOCOUNT ON;
UPDATE [Sample_Table] SET [LastChangeTime] = @p0 WHERE [Id] = @p1 AND [Name] = @p2;
SELECT @@ROWCOUNT;
UPDATE [Sample_Table] SET [LastChangeTime] = @p3 WHERE [Id] = @p4 AND [Name] = @p5;
SELECT @@ROWCOUNT;
UPDATE [Sample_Table] SET [LastChangeTime] = @p6 WHERE [Id] = @p7 AND [Name] = @p8;
SELECT @@ROWCOUNT;
UPDATE [Sample_Table] SET [LastChangeTime] = @p9 WHERE [Id] = @p10 AND [Name] = @p11;
SELECT @@ROWCOUNT;
UPDATE [Sample_Table] SET [LastChangeTime] = @p12 WHERE [Id] = @p13 AND [Name] = @p14;
SELECT @@ROWCOUNT;
UPDATE [Sample_Table] SET [LastChangeTime] = @p15 WHERE [Id] = @p16 AND [Name] = @p17;
SELECT @@ROWCOUNT;
UPDATE [Sample_Table] SET [LastChangeTime] = @p18 WHERE [Id] = @p19 AND [Name] = @p20;
SELECT @@ROWCOUNT;
UPDATE [Sample_Table] SET [LastChangeTime] = @p21 WHERE [Id] = @p22 AND [Name] = @p23;
SELECT @@ROWCOUNT;
UPDATE [Sample_Table] SET [LastChangeTime] = @p24 WHERE [Id] = @p25 AND [Name] = @p26;
SELECT @@ROWCOUNT;
UPDATE [Sample_Table] SET [LastChangeTime] = @p27 WHERE [Id] = @p28 AND [Name] = @p29;
SELECT @@ROWCOUNT;
UPDATE [Sample_Table] SET [LastChangeTime] = @p30 WHERE [Id] = @p31 AND [Name] = @p32;
SELECT @@ROWCOUNT;
UPDATE [Sample_Table] SET [LastChangeTime] = @p33 WHERE [Id] = @p34 AND [Name] = @p35;
SELECT @@ROWCOUNT;
UPDATE [Sample_Table] SET [LastChangeTime] = @p36 WHERE [Id] = @p37 AND [Name] = @p38;
SELECT @@ROWCOUNT;
UPDATE [Sample_Table] SET [LastChangeTime] = @p39 WHERE [Id] = @p40 AND [Name] = @p41;
SELECT @@ROWCOUNT;
UPDATE [Sample_Table] SET [LastChangeTime] = @p42 WHERE [Id] = @p43 AND [Name] = @p44;
SELECT @@ROWCOUNT;
UPDATE [Sample_Table] SET [LastChangeTime] = @p45 WHERE [Id] = @p46 AND [Name] = @p47;
SELECT @@ROWCOUNT;
UPDATE [Sample_Table] SET [LastChangeTime] = @p75, [PropertyOne] = @p76 WHERE [Id] = @p77 AND [Name] = @p78;
SELECT @@ROWCOUNT;
我还没研究过如何使用存储过程+TVP,如果没有存储过程,还有什么办法
PropertyOne
未更新,我们仍然希望 EF Core 将其包含在参数化查询中?您可以关闭 AutoDetectChanges 以始终获得相同的 SQL 语句。
另一方面,这将提高 SaveChanges 的性能,因为它不必遍历图表来查找更改:
// Switch off autodetect changes:
_context.ChangeTracker.AutoDetectChangesEnabled = false;
foreach (var message in incomingMessages)
{
if (dbResultsLookup.TryGetValue($"{message.Id}_{message.Name}", out var dbEntity))
{
dbEntity.PropertyOne = message.PropertyOne;
dbEntity.LastChangeTime = DateTimeOffset.Now;
// Set fields to be updated:
_context.Entry(dbEntity).Property(x => x.PropertyOne).IsModified = true;
_context.Entry(dbEntity).Property(x => x.LastChangeTime).IsModified = true;
}
}
await _context.SaveChangesAsync(cancel);
// Restore autodetect changes:
_context.ChangeTracker.AutoDetectChangesEnabled = true;