博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
IRepository
阅读量:5135 次
发布时间:2019-06-13

本文共 4856 字,大约阅读时间需要 16 分钟。

转自:

最近有一个项目采用了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<
string,
object>> queryWhere, List<KeyValuePair<
string,
int>> 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 :
class,
new()     {         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);     }

 

posted on
2012-07-01 15:01  阅读(
...) 评论(
...) 收藏

转载于:https://www.cnblogs.com/iverson3/archive/2012/07/01/2571969.html

你可能感兴趣的文章
sl学习疑问
查看>>
老李分享:android手机测试之适配(1)
查看>>
彻底理解position与anchorPoint - Wonderffee's Blog(转)
查看>>
C# 读取EXCEL文件的三种经典方法
查看>>
记错严格管理时间---记一次老师讲的学习方法
查看>>
02: DOM 实例
查看>>
nisi 脚本示例
查看>>
连接Linux服务器操作Oracle数据库
查看>>
JavaBean到底是个什么东西
查看>>
Html input file控件使用accept过滤 限制的文件类型
查看>>
赛道对比测试高尔夫6/7 全面解析后悬架
查看>>
右侧悬浮菜单
查看>>
induced pluripotent stem cell (iPSC) 诱导性多能干细胞
查看>>
告白气球--吉他教学
查看>>
GNOME 3 + Ubuntu 11.04
查看>>
2.2使用urllib的简单传输
查看>>
XML基础概念
查看>>
fastReport 小总结
查看>>
数据库_day03_对数据库的基本操作
查看>>
Mac上创建cocos2d-x工程
查看>>