分頁查詢接口。
接口定義:
接口實現:
{
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這也沒也問題,而且也不推薦將倉儲接口直接開放給客戶端應用程序,應該在倉儲接口上為每個特定的應用系統提供全新的接口,比如可以這樣:
{
List<aspnet_UsersModel> QueryByPage(Expression<Func<T, bool>> filter, Expression<Func<T, string>> FunOrder, int PageSize, int PageIndex, out int recordsCount);
}
這里為了演示方便,就直接調用倉儲接口做測試。
IEnumerable調用代碼:
IEnumerable情況下生成的腳本:
[ 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調用
IQueryable情況下生成的腳本

[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,那么在一定程度上就會對程序員造成誤導。
缺陷二:不太實用的接口定義
我們來看一下這個接口定義
再看下它的實現,可以看出這個方法實現了按需所取,但這是多么的不協調啊(上面那個分頁方法就不是按需所取)。就像有人做事一樣,50%的工作可以打100分,其余的50%只能打60分,而且這40分很容易得到。

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>>,用戶根本不知道需要傳遞什么參數,提供的接口參數一定要讓調用者容易理解。
我理解的倉儲接口
如果還有哪些不夠的,可以根據情況再決定增加哪些功能。創建接口不推薦直接開放給客戶端,應該在此基礎上重新為每個應用定義接口。
{
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);
}