1. 程式人生 > 其它 >== 解決 EF where<T>(func) 查詢的一個性能問題

== 解決 EF where<T>(func) 查詢的一個性能問題

https://www.cnblogs.com/xiaokangufo/p/3901402.html

前兩年幫朋友 做了個網咖管理軟體,採用動軟的三層架構 sql語句生成的。最近因功能變更 要改動,而我這段正在做asp.net mvc +ef+autofac的一個電商網站。索性 就把原來的底層全重新了套用了我現在的架構 EF6.0+autofac+三層架構,上層的asp.net沒有變。改完後發現交班頁面開啟巨慢。

跟蹤EF生成的sql語句 發現生成的sql 有問題,查詢的全表,而全表有近10萬條的資料。

繼續跟蹤資料庫的耗時查詢 發現確實是這條語句佔時間

為什麼會這樣呢,我在查詢裡做條件搜尋了,為啥 結果不對呢?

貼出BaseRepository.cs 的程式碼

public class BaseRepository<T> :IDBbase<T>  where T : class
    {
        //例項化EF框架
       protected skdbContext db = new skdbContext();

        //新增
        public T AddEntities(T entity)
        {
            db.Entry<T>(entity).State = EntityState.Added;
            db.SaveChanges();
            
return entity; } //修改 public bool UpdateEntity(T entity) { db.Set<T>().Attach(entity); db.Entry<T>(entity).State = EntityState.Modified; return db.SaveChanges() > 0; } //修改 public bool DeleteEntities(T entity) { db.Set
<T>().Attach(entity); db.Entry<T>(entity).State = EntityState.Deleted; return db.SaveChanges() > 0; } //查詢 public IQueryable<T> LoadEntities(Func<T, bool> wherelambda) { return db.Set<T>().Where<T>(wherelambda).AsQueryable(); } //查詢單個 public T LoadEntitie(Func<T, bool> wherelambda) { return db.Set<T>().FirstOrDefault<T>(wherelambda); } //分頁 public IQueryable<T> LoadPagerEntities<S>(int pageSize, int pageIndex, out int total, Func<T, bool> whereLambda, bool isAsc, Func<T, S> orderByLambda) { var tempData = db.Set<T>().Where<T>(whereLambda); total = tempData.Count(); //排序獲取當前頁的資料 if (isAsc) { tempData = tempData.OrderBy<T, S>(orderByLambda). Skip<T>(pageSize * (pageIndex - 1)). Take<T>(pageSize).AsQueryable(); } else { tempData = tempData.OrderByDescending<T, S>(orderByLambda). Skip<T>(pageSize * (pageIndex - 1)). Take<T>(pageSize).AsQueryable(); } return tempData.AsQueryable(); } }
View Code

呼叫程式碼

return jiaobanitem.LoadEntities(t => t.JiaoBanID == jiaobanID && t.GoodsID == GoodsID).FirstOrDefault();

參考nopCommerce 修改baserepository

public class EFRepository<T> : IRepository<T> where T : class
    {
        //例項化EF框架
       //protected YaFeiNetContext db = new YaFeiNetContext();
       private DbContext _context;
       private IDbSet<T> _entities;

       public EFRepository(DbContext context)
       {
           this._context = context;
       }


        //新增
        public virtual T AddEntities(T entity)
        {
            try
            {
                if(entity==null)
                    throw new ArgumentNullException("entity");

                this.Entities.Add(entity);
                this._context.SaveChanges();
                return entity;
            }
            catch(DbEntityValidationException dbEx)
            {
                var msg = string.Empty;
                foreach(var validationErrors in dbEx.EntityValidationErrors)
                    foreach (var validationError in validationErrors.ValidationErrors)
                        msg += string.Format("Property:{0} Error:{1}", validationError.PropertyName, validationError.ErrorMessage) + Environment.NewLine ;

                var fail = new Exception(msg,dbEx);
                throw fail;
            }
        }

        //修改
        public virtual bool UpdateEntities(T entity)
        {
            try
            {
                if (entity == null)
                    throw new ArgumentNullException("entity");


               // this.Entities.Attach(entity);
               // _context.Entry<T>(entity).State = EntityState.Modified;
                return this._context.SaveChanges() > 0;
            }
            catch (DbEntityValidationException dbEx)
            {
                var msg = string.Empty;
                foreach (var validationErrors in dbEx.EntityValidationErrors)
                    foreach (var validationError in validationErrors.ValidationErrors)
                        msg += string.Format("Property:{0} Error:{1}", validationError.PropertyName, validationError.ErrorMessage) + Environment.NewLine;

                var fail = new Exception(msg, dbEx);
                throw fail;
            }

        }

        //修改
        public virtual bool DeleteEntities(T entity)
        {
            try
            {
                if (entity == null)
                    throw new ArgumentNullException("entity");

                //db2.Set<T>().Attach(entity);
                //db2.Entry<T>(entity).State = EntityState.Deleted;
                this.Entities.Remove(entity);
                return this._context.SaveChanges() > 0;
            }
            catch (DbEntityValidationException dbEx)
            {
                var msg = string.Empty;
                foreach (var validationErrors in dbEx.EntityValidationErrors)
                    foreach (var validationError in validationErrors.ValidationErrors)
                        msg += string.Format("Property:{0} Error:{1}", validationError.PropertyName, validationError.ErrorMessage) + Environment.NewLine;

                var fail = new Exception(msg, dbEx);
                throw fail;
            }


        }

        //查詢
        public virtual IQueryable<T> LoadEntities(Func<T, bool> wherelambda)
        {
            return this.Entities.Where<T>(wherelambda).AsQueryable();
        }
        //查詢單個
        public virtual T LoadEntitie(Func<T, bool> wherelambda)
        {
            return this.Table.Where(wherelambda).FirstOrDefault();
        }
       /// <summary>
       /// 根據主鍵查詢
       /// </summary>
       /// <param name="id"></param>
       /// <returns></returns>
        public virtual T GetById(object id)
        {
            return this.Entities.Find(id);
        }




        //分頁
        public virtual IQueryable<T> LoadPagerEntities<S>(int pageSize, int pageIndex, out int total,
            Func<T, bool> whereLambda, bool isAsc, Func<T, S> orderByLambda)
        {

            var tempData = this.Entities.Where<T>(whereLambda);

                total = tempData.Count();

                //排序獲取當前頁的資料
                if (isAsc)
                {
                    tempData = tempData.OrderBy<T, S>(orderByLambda).
                          Skip<T>(pageSize * (pageIndex - 1)).
                          Take<T>(pageSize).AsQueryable();
                }
                else
                {
                    tempData = tempData.OrderByDescending<T, S>(orderByLambda).
                         Skip<T>(pageSize * (pageIndex - 1)).
                         Take<T>(pageSize).AsQueryable();
                }
                return tempData.AsQueryable();



        }

        protected virtual IDbSet<T> Entities
        {
            get
            {
                if (_entities == null)
                    _entities = _context.Set<T>();
                return _entities;
            }
        }


        public virtual IQueryable<T> Table
        {
            get { return this.Entities; }
        }


    }
View Code

同時修改呼叫程式碼 為

return jiaobanitem.Table.Where(t=>t.JiaoBanID ==jiaobanID && t.GoodsID ==GoodsID).FirstOrDefault();

問題解決 頁面響應不到100ms 同時除錯中 生成的sql語句已經有 查詢條件了

問題出在

//查詢
public IQueryable<T> LoadEntities(Func<T, bool> wherelambda)
{
  return db.Set<T>().Where<T>(wherelambda).AsQueryable();
}

為了驗證,我在前臺直接呼叫

return this.context.Set<tb_e_jiaoBanItem>().Where(t => t.JiaoBanID == jiaobanID && t.GoodsID == GoodsID).AsQueryable().FirstOrDefault();

頁面響應也是 100ms左右,效能沒問題。直接呼叫 dbcontext的set<>方法 沒問題。但跨了幾層傳遞後 就有問題。並沒有生成我想要的查詢語句。

這個問題原來別人也碰到過 http://www.cnblogs.com/yjmyzz/archive/2008/09/06/1285564.html

如果傳入Where的引數為Expression,則L2S會自動幫忙處理Expression的拼接;而如果傳入的是Func,而L2S無法拼接Expression與Func,所以只好先把資料全部取出來,然後再應用Func來進行資料過濾。

程式碼應該修改為:

public virtual IEnumerable<T> LoadEntities(System.Linq.Expressions.Expression<Func<T,bool>>wherelambda)
{
return this.Table.Where<T>(wherelambda);
}