【问题标题】:Entity Framework repostory pattern GetAll() too slow实体框架存储库模式 GetAll() 太慢
【发布时间】:2017-07-13 17:20:16
【问题描述】:

我正在使用存储库层。我的问题是在加入包含大记录的表时,GetAll() 方法太慢。运行一个简单的查询需要 40 秒。

IGenericRepository:

public interface IGenericRepository<TEntity>
{
    TEntity FindBy(Expression<Func<TEntity, bool>> predicate);
    IEnumerable<TEntity> GetAll();
    TEntity GetById(int id);
    TEntity Insert(TEntity entity);
    TEntity Update(TEntity entity);
    void Delete(object id);
    void Save();
}

通用存储库:

public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
    private MyStoreContext _dbContext;
    protected DbSet<TEntity> DbSet;

    public GenericRepository()
    {
        _dbContext = new MyStoreContext ();
        DbSet = _dbContext.Set<TEntity>();
    }

    public TEntity FindBy(Expression<Func<TEntity, bool>> predicate)
    {
        return DbSet.Where(predicate).SingleOrDefault();
    }

    public IEnumerable<TEntity> GetAll()
    {
        return DbSet.AsNoTracking();
    }

    public TEntity GetById(int id)
    {
        return DbSet.Find(id);
    }

    public TEntity Insert(TEntity entity)
    {
        DbSet.Add(entity);
        Save();
        return entity;
    }

    public TEntity Update(TEntity obj)
    {
        DbSet.Attach(obj);
        _dbContext.Entry(obj).State = EntityState.Modified;

        Save();
        return obj;
    }

    public void Delete(object id)
    {
        TEntity entityToDelete = DbSet.Find(id);
        Delete(entityToDelete);
    }

    public void Delete(TEntity entityToDelete)
    {
        if (_dbContext.Entry(entityToDelete).State == EntityState.Detached)
        {
            DbSet.Attach(entityToDelete);
        }

        DbSet.Remove(entityToDelete);
        Save();
    }

    public void Save()
    {
        try
        {
            _dbContext.SaveChanges();
        }
        catch (DbEntityValidationException dbEx)
        {
            foreach (var validationErrors in dbEx.EntityValidationErrors)
            {
                foreach (var validationError in validationErrors.ValidationErrors)
                {
                    System.Console.WriteLine("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage); // you just put the log to know the errors
                }
            }
        }
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_dbContext != null)
            {
                _dbContext.Dispose();
                _dbContext = null;
            }
        }
    }
}

林克:

var conceptosDetalle = from pf in _parFactfRepository.GetAll()
                               join inve in _inveRepository.GetAll() on pf.CVE_ART equals inve.CVE_ART
                               where inve.STATUS == "A" && pf.CVE_DOC == cveDoc
                               orderby inve.CTRL_ALM, inve.CVE_ART
                               select new MyViewModel()
                               {
                                   CTRL = inve.CTRL_ALM,
                                   CVE_ART = inve.CVE_ART,
                                   UNID = "PIEZA",
                                   CANT = pf.CANT,
                                   DESCR = inve.DESCR,
                                   PREC = pf.PREC,
                                   DESC1 = pf.DESC1,
                                   TOTIMP4 = pf.TOTIMP4
                               };

查询返回 10 条记录。 parFactfRepository 包含 992590 行,inveRepository 包含 41908 行。

我做错了什么?

【问题讨论】:

  • 我认为这将下载这两个表的全部并将它们加入内存中,而不是数据库中,因为 GetAllIEnumerable

标签: c# asp.net-mvc linq entity-framework-6


【解决方案1】:

这是因为您正在混合和匹配基于存储库的查询和 LINQ 查询。您不是进行真正的连接,而是获取每个表的所有行,然后将它们连接到内存中。

解决此问题的最简单方法可能是从您的 GetAll 方法中返回 IQueryable&lt;TEntity&gt; 而不是 IEnumerable&lt;TEntity&gt;。使用IEnumerable&lt;TEntity&gt; 强制查询进行评估。如果您要返回 IEnumerable&lt;TEntity&gt;,则您的数据应该是完整的,即不需要对查询进行进一步的更改(包括连接)。

也就是说,这是尝试将存储库模式与 EF 一起使用的又一次失败。如果您不非常小心,您最终会引入这样的逻辑错误,这些错误对于它们发生的原因并不明显。实体框架已经实现了存储库模式; DbSet 就是这样。如果您想要对此进行抽象,请引入服务层。有了它,你就可以简单地使用如下方法:

public IEnumerable<MyViewModel> GetConceptosDetalle()
{
    ...
}

并且该方法将包含整个查询(直接使用 EF,而不是完全不必要的存储库层)。这样,您的应用程序只需调用一个返回所需数据的方法,并且该服务层包含所有逻辑。这才是真正的抽象。使用存储库,您的代码库中的所有逻辑都在流血。

注意:我让它返回 MyViewModel 只是为了便于解释,但实际上,您应该返回某种 DTO,然后您可以将其映射到您的视图模型。将视图业务逻辑泄漏到服务层是个坏主意。

【讨论】:

  • 非常感谢您的建议。现在我正在努力。 +1
  • 赞成这个如此困难,因为有人反对尝试将实体框架包装在存储库/工作单元模式中。本周到目前为止,我已经进行了两次的对话。
  • @BradleyUffner:我不需要投票。根本没有充分的理由将存储库模式与 EF 或任何 ORM 时期一起使用。存储库模式解决的问题是由 ORM 解决的,这就是为什么我见过的几乎每个 ORM 都实现了存储库模式。如果您的目标是抽象 EF,还有更合适的方法,例如前面提到的服务层。
  • 对,我只是想表达我对你的同意,因为我觉得我需要每周向工作人员解释同样的事情。
  • @Rolando:好吧,不是为了自我推销,而是这样:softwareengineering.stackexchange.com/questions/180851/…cpratt.co/…。我不知道其他人写的任何其他内容,但我确信一个简单的谷歌搜索可以显示一些。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多