EntityFramework在执行更新查询时非常缓慢

时间:2015-11-23 14:01:41

标签: c# entity-framework

我们正在研究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)?

1 个答案:

答案 0 :(得分:0)

看起来好像你已经尝试过设置:

Configuration.AutoDetectChangesEnabled = false;
Configuration.ValidateOnSaveEnabled = false;

并且您没有使用有序列表,我认为您将不得不重构代码并进行基准测试。

我认为瓶颈来自foreach,因为上下文必须处理潜在的大量批量数据(不确定在您的情况下有多少)。

在调用SaveChanges();SaveChangesAsync();方法之前,请尝试将数组中包含的项目缩小为较小的批次,并注意性能偏差,以使上下文变得过大。

此外,如果您仍然没有看到进一步的收益,请尝试处理上下文帖子SaveChanges();,然后根据实体列表的大小创建一个新的帖子,清除上下文可能会产生进一步的改进

但这完全取决于我们谈论的实体数量,并且可能只会在成百上千的记录场景中引人注意。