我所理解的IRepository


    最近有一個項目采用了EntityFramework,對於基本的增刪改查操作,我們采用了傳統的倉儲模式(IRepository),但對於項目中的倉儲接口的定義及實現上我認為存在部分缺陷。這個創建模式是以前同事編寫好的,然后我們在新項目中利用它。
    
    分頁查詢接口。
     

    接口定義:

       IEnumerable<T> QueryByPage(Func<T,  bool> FunWhere, Func<T,  string> FunOrder,  int PageSize,  int PageIndex,  out  int recordsCount);
   

  接口實現:

   

        public IEnumerable<T> QueryByPage(Func<T,  bool> FunWhere, Func<T,  string> FunOrder,  int PageSize,  int PageIndex,  out  int recordsCount)
        {
            recordsCount = context.Set<T>().Where(FunWhere).OrderByDescending(FunOrder).Count();
             return context.Set<T>().Where(FunWhere).OrderByDescending(FunOrder).Select(t => t).Skip((PageIndex -  1) * PageSize).Take(PageSize);
        }

    缺陷一:對於倉儲接口,集合返回IEnumerable。
    如果返回的是IEnumerable,那么實際上,系統會將表中的所有數據加載到內存中,然后再進行條件過濾,排序,再分頁。如果表記錄稍微多一點的話,性能可想而知。此種情況下應該推薦返回IQueryable,它才是真正適合和數據庫打交道的對象。在客戶端應用程序沒有訪問實際對象值之前,比如ToList()操作,它只是一個編譯過程,根據用戶傳入的參數構建查詢計划最終生成用於查詢所用的SQLScript腳本。這種方式才是真正意義上的按需所取。
    
    下面我們來定義一個新接口:
   

    IQueryable<T> Query(Expression<Func<T,  bool>> filter);

    注意這里是一個開放性特別大的查詢接口,如果說不要輕易為客戶端開放IQueryable這也沒也問題,而且也不推薦將倉儲接口直接開放給客戶端應用程序,應該在倉儲接口上為每個特定的應用系統提供全新的接口,比如可以這樣:
   

    public  interface Iaspnet_UsersRepository
    {
             List<aspnet_UsersModel> QueryByPage(Expression<Func<T,  bool>> filter, Expression<Func<T,  string>> FunOrder,  int PageSize,  int PageIndex,  out  int recordsCount);
    }

    這里為了演示方便,就直接調用倉儲接口做測試。 
    
    IEnumerable調用代碼:
   

var list = service.QueryByPage(p => p.UserId != Guid.Empty,p=>p.UserName, 1, 1, out recordsCount).ToList();

    IEnumerable情況下生成的腳本:
   

SELECT
[ Extent1 ]. [ UserId ]  AS  [ UserId ],
[ Extent1 ]. [ UserName ]  AS  [ UserName ],
[ Extent1 ]. [ LoweredUserName ]  AS  [ LoweredUserName ],
[ Extent1 ]. [ MobileAlias ]  AS  [ MobileAlias ],
[ Extent1 ]. [ IsAnonymous ]  AS  [ IsAnonymous ],
[ Extent1 ]. [ LastActivityDate ]  AS  [ LastActivityDate ]
FROM  [ dbo ]. [ aspnet_Users ]  AS  [ Extent1 ]

 
IQueryable調用

var list  = service.Query(p =>p.UserId !=Guid.Empty).OrderBy(p =>p.UserName).Take( 1).Skip( 1).ToList();

    IQueryable情況下生成的腳本
 

View Code
exec sp_executesql N ' SELECT
[Limit1].[UserId] AS [UserId],
[Limit1].[UserName] AS [UserName],
[Limit1].[LoweredUserName] AS [LoweredUserName],
[Limit1].[MobileAlias] AS [MobileAlias],
[Limit1].[IsAnonymous] AS [IsAnonymous],
[Limit1].[LastActivityDate] AS [LastActivityDate]
FROM ( SELECT [Limit1].[UserId] AS [UserId], [Limit1].[UserName] AS [UserName], [Limit1].[LoweredUserName] AS [LoweredUserName], [Limit1].[MobileAlias] AS [MobileAlias], [Limit1].[IsAnonymous]
AS [IsAnonymous], [Limit1].[LastActivityDate] AS [LastActivityDate], row_number() OVER (ORDER BY [Limit1].[UserName] ASC) AS [row_number]
    FROM ( SELECT TOP (1) [Project1].[UserId] AS [UserId], [Project1].[UserName] AS [UserName], [Project1].[LoweredUserName] AS [LoweredUserName], [Project1].[MobileAlias] AS
[MobileAlias], [Project1].[IsAnonymous] AS [IsAnonymous], [Project1].[LastActivityDate] AS [LastActivityDate]
        FROM ( SELECT
            [Extent1].[UserId] AS [UserId],
            [Extent1].[UserName] AS [UserName],
            [Extent1].[LoweredUserName] AS [LoweredUserName],
            [Extent1].[MobileAlias] AS [MobileAlias],
            [Extent1].[IsAnonymous] AS [IsAnonymous],
            [Extent1].[LastActivityDate] AS [LastActivityDate]
            FROM [dbo].[aspnet_Users] AS [Extent1]
            WHERE [Extent1].[UserId] <> @p__linq__0
        )  AS [Project1]
        ORDER BY [Project1].[UserName] ASC
    )  AS [Limit1]
)  AS [Limit1]
WHERE [Limit1].[row_number] > 0
ORDER BY [Limit1].[UserName] ASC
',N ' @p__linq__0 uniqueidentifier ', @p__linq__0 = ' 00000000-0000-0000-0000-000000000000 '


    小結:

          只有返回IQueryable,才會實現按需所取。盡管這個方法在最上層的服務層沒有進行封裝,理論上客戶端應用程序無法訪問到此方法,但既然提供了,而且對外是Public,那么在一定程度上就會對程序員造成誤導。
   

 缺陷二:不太實用的接口定義
    我們來看一下這個接口定義
  

IEnumerable<T> QueryByPage(List<KeyValuePair< stringobject>> queryWhere, List<KeyValuePair< stringint>> funOrder,  int PageSize,  int PageIndex,  out  int recordsCount)

    再看下它的實現,可以看出這個方法實現了按需所取,但這是多么的不協調啊(上面那個分頁方法就不是按需所取)。就像有人做事一樣,50%的工作可以打100分,其余的50%只能打60分,而且這40分很容易得到。
   

View Code
string objName =  string.Empty;
            Type type =  typeof(T);
             var properties = Context.GetType().GetProperties();
             foreach ( var propertyInfo  in properties)
            {
                 if (propertyInfo.PropertyType.GetGenericArguments()[ 0].Name == type.Name)
                {
                    objName = propertyInfo.Name;
                     break;
                }
            }
             string queryObj = EntitySQLGenerator.GenerateQuery(queryWhere, funOrder, Context.DefaultContainerName, objName);
            IQueryable<T> result = Context.CreateQuery<T>(queryObj);
            recordsCount = result.Count();
             return result.Select(t => t).Skip((PageIndex -  1) * PageSize).Take(PageSize).AsEnumerable<T>();

   查詢條件難以理解
   List<KeyValuePair<string, object>>,用戶根本不知道需要傳遞什么參數,提供的接口參數一定要讓調用者容易理解。
    
   我理解的倉儲接口

   如果還有哪些不夠的,可以根據情況再決定增加哪些功能。創建接口不推薦直接開放給客戶端,應該在此基礎上重新為每個應用定義接口。
  

    public  interface IMyRepository<T>  where T :  classnew()
    {
        T Create();
        T Update(T entity);
        T Insert(T entity);
         void Delete(T entity);
        List<T> FindAll();
        IQueryable<T> Query(Expression<Func<T,  bool>> filter);
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM