使用AsNoTracking改进长时间运行的EF查询

时间:2016-01-27 11:26:37

标签: entity-framework-6

我正在使用Entity Framework v6.1.3并且一个查询正在处理> 10秒从我的应用程序运行。它需要<将AsNoTracking()添加到呼叫时为1:

    public IList<Job> GetByERPHeaderIds(string[] headerIds)
    {
        return base.Query()
            .Include(j => j.PreferredBranch)
            .Include(j => j.JobType)
            .Include(j => j.Technicians)
            .Include(j => j.GmcJobStates)
            .Where(j => headerIds.Contains(j.ERPServiceOrderId))
            .AsNoTracking()
            .ToList();
    }

GetByERPHeaderIds()方法获取一个包含400个元素的数组。问题是我需要在修改查询检索的一些项后保存回数据库。在我的上下文中,我使用以下代码来管理实体状态:

    public virtual void Save(TEntity entity)
    {
        if (_context.Entry(entity).State == EntityState.Detached)
        {
            if (entity.IsNew)
            {
                _dbSet.Add(entity);
            }
            else
            {
                var cachedEntity = _dbSet.Local.FirstOrDefault(x => x.Equals(entity));

                if (cachedEntity != null)
                    _context.Entry(cachedEntity).State = EntityState.Detached;

                _context.Entry(entity).State = EntityState.Modified;
            }
        }
        else
        {
            _context.Entry(entity).State = EntityState.Modified;
        }

        if (!_context.IsUnitOfWorkActive)
        {
            _context.SaveChanges();
        }
    }

当我得到&#34;修改&#34;状态实体有时我得到DbUpdateException或有时我得到重复的条目

  

ex.InnerException.InnerException {&#34;违反PRIMARY KEY约束   &#39; PK_dbo.TechnicianJob&#39 ;.无法在对象中插入重复键   &#39; dbo.TechnicianJob&#39 ;.重复键值为(1039,239)。\ r \ n   语句已被终止。&#34;} System.Exception   {System.Data.SqlClient.SqlException}

UPDATE1 : 在这个例子中,我试图保存一个&#34; Job&#34;实体回到数据库。该实体包含对其他表的引用,在本例中是对表&#34; TechnicianJob&#34;的引用。看起来当从数据库中检索分离的实体时,不会正确检索它的表依赖性。我认为这就是我获得该异常的原因,因为它试图保存已存在于数据库中的实体。

UPDATE2 : 这是它在阅读article之后查看我的存储库的方式:

    public virtual void Save(TEntity entity)
    {
        if (_context.Entry(entity).State == EntityState.Detached)
        {
            if (entity.IsNew)
            {
                _dbSet.Add(entity);
            }
            else
            {
                var cachedEntity = _dbSet.Local.FirstOrDefault(x => x.Equals(entity));

                if (cachedEntity != null)
                {
                    var cachedEntry = _context.Entry(cachedEntity);
                    cachedEntry.CurrentValues.SetValues(entity);
                }
                else
                {
                    var entry = _context.Entry(entity);
                    entry.State = EntityState.Modified;
                }
            }
        }
        else
        {
            _context.Entry(entity).State = EntityState.Modified;
        }

        if (!_context.IsUnitOfWorkActive)
        {
            _context.SaveChanges();
        }
    }

2 个答案:

答案 0 :(得分:0)

您的通用保存方法取决于更改跟踪。或者至少说实体处于正确的状态。

我建议你在修改它们并调用Save()

之前附加Job实体
context.Jobs.Attach(job);

答案 1 :(得分:0)

旅程 为了得到解决方案,我做了一些研究。我发现了使用&#34; AsNoTracking &#34; EF表现非常好。另一方面,您的实体不再被跟踪,除非这些实体与其他表格有关系(我的情况),否则这不应该是一个大问题。通过将实体的状态设置为&#34; Modified&#34;,我能够将实体(即 Job )重新附加到上下文中。然后EF知道它必须对该实体进行更新。 EF不知道如何处理相关实体(即 TechnicianJob )。它一直试图插入它们,最终会出现重复的密钥异常和多个不正确的分配。

我的解决方案 我找到了解决问题的方法。我从我的存储库中删除了急切加载,因此我不强制EF在一个查询中跟踪与其他表的4种不同关系的状态。这将性能从10s提升到100ms。

全球解决方案 看起来非跟踪实体会在EF7中解决,但在发布之前...... GraphDiff:看来这个nuget包提供了非常好的结果,以解决这个不便: