我们正在研究EF 6.1.3速度缓慢的性能问题,而我们无法弄清楚可能导致它的原因。
数据库上下文初始化为:
Configuration.ProxyCreationEnabled = false;
Configuration.AutoDetectChangesEnabled = false;
Configuration.ValidateOnSaveEnabled = false;
我们已将性能问题与以下方法隔离开来:
protected virtual async Task<long> UpdateEntityInStoreAsync(T entity,
string[] changedProperties)
{
using (var session = sessionFactory.CreateReadWriteSession(false, false))
{
var writer = session.Writer<T>();
writer.Attach(entity);
await writer.UpdatePropertyAsync(entity, changedProperties.ToArray()).ConfigureAwait(false);
}
return entity.Id;
}
changedProperties列表中有两个名称,EF正确生成了一个更新语句,只更新这两个属性。
重复调用此方法(处理数据项的集合),大约需要15-20秒才能完成。
如果我们用以下方法替换上述方法,执行时间将减少到3-4秒:
protected virtual async Task<long> UpdateEntityInStoreAsync(T entity,
string[] changedProperties)
{
var sql = $"update {entity.TypeName()}s set";
var separator = false;
foreach (var property in changedProperties)
{
sql += (separator ? ", " : " ") + property + " = @" + property;
separator = true;
}
sql += " where id = @Id";
var parameters = (from parameter in changedProperties.Concat(new[] { "Id" })
let property = entity.GetProperty(parameter)
select ContextManager.CreateSqlParameter(parameter, property.GetValue(entity))).ToArray();
using (var session = sessionFactory.CreateReadWriteSession(false, false))
{
await session.UnderlyingDatabase.ExecuteSqlCommandAsync(sql, parameters).ConfigureAwait(false);
}
return entity.Id;
}
在writer(存储库实现)上调用的UpdatePropertiesAsync方法如下所示:
public virtual async Task UpdatePropertyAsync(T entity, string[] changedPropertyNames, bool save = true)
{
if (changedPropertyNames == null || changedPropertyNames.Length == 0)
{
return;
}
Array.ForEach(changedPropertyNames, name => context.Entry(entity).Property(name).IsModified = true);
if (save)
await context.SaveChangesAsync().ConfigureAwait(false);
}
}
EF做什么完全杀死了性能?我们可以做些什么来解决它(没有使用另一个ORM)?
答案 0 :(得分:0)
看起来好像你已经尝试过设置:
Configuration.AutoDetectChangesEnabled = false;
Configuration.ValidateOnSaveEnabled = false;
并且您没有使用有序列表,我认为您将不得不重构代码并进行基准测试。
我认为瓶颈来自foreach
,因为上下文必须处理潜在的大量批量数据(不确定在您的情况下有多少)。
在调用SaveChanges();
或SaveChangesAsync();
方法之前,请尝试将数组中包含的项目缩小为较小的批次,并注意性能偏差,以使上下文变得过大。
此外,如果您仍然没有看到进一步的收益,请尝试处理上下文帖子SaveChanges();
,然后根据实体列表的大小创建一个新的帖子,清除上下文可能会产生进一步的改进
但这完全取决于我们谈论的实体数量,并且可能只会在成百上千的记录场景中引人注意。