实体框架repostory模式GetAll()太慢了

Kle*_*ezk 0 c# linq asp.net-mvc entity-framework-6

我正在使用存储库层.我的问题是,当连接具有大记录的表时,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();
}
Run Code Online (Sandbox Code Playgroud)

GenericRepository:

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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

LINQ:

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
                               };
Run Code Online (Sandbox Code Playgroud)

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

我做错了什么?

Chr*_*att 8

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

解决这个问题的最简单方法可能就是返回IQueryable<TEntity>而不是IEnumerable<TEntity>GetAll方法中返回.使用IEnumerable<TEntity>强制查询进行评估.如果您要返回,IEnumerable<TEntity>您的数据应该是完全烘焙的,即不需要进一步更改查询(包括连接).

也就是说,这还有一个尝试在EF中使用存储库模式的失败.如果你不是小心,你最终会引入这样的逻辑错误,这些错误并不明显.实体框架已经实现了存储库模式; 这是一个什么样DbSet.如果您想要对其进行抽象,请引入服务层.有了它,你只需要一个像这样的方法:

public IEnumerable<MyViewModel> GetConceptosDetalle()
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

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