Repository 仓储,你的归宿究竟在啊?(三)-SELECT 某某某。。。

描绘在前面

第一,本篇博文主要含有两个主题:

  1. 世界服务遭遇动用仓储
  2. SELECT 某某某(有点晕?请圈下。)

上一篇:Repository
仓储,你的归宿究竟在啊?(二)-这样的应用层代码,你能承受吗?

关于仓储这个系列,很多园友问我:怎么纠结仓储?我觉着要再行说明下(请不要再次“纠结”了),引用上同首博文被某个一样段评论的复:

关于“纠结于储存”这个题目,其实博文中自我哪怕来说明,不是说自家纠结或者陷入这个题材,而是自己看当履行领域让设计着,仓储的调用是一个好重要的事物,如果下的不正好,也许就是像上面我所粘贴出的应用层代码一样,我个人认为,这是成千上万丁在推行领域让设计被,很易踩的一个坑,我只是想得以拿这进程分享出去,给来一样困惑的口,可以借鉴一下。

天地服务和仓储的少数栽“微妙关系”

马上边的“领域服务”和仓储的关系,可以知道呢以世界面临调用仓储,具体表现为在领域服务遭遇动用。

当生长远之前,我为保所谓的“领域纯洁”,在领域服务计划的当儿,没有参杂仓储任何的调用,但是就应用程序的纷繁,很多业务增长进去,一个才的“业务描述”并无克确实去贯彻工作用例,所以这时候的小圈子服务就是被“架空”了,一些工作实现“迫不得已”放在了应用层,也不怕是上一致首我所粘贴出的应用层代码,不晓得您会无克经受?反正我是接受不了,所以自己举行了部分优化,领域服务被调用了仓储。

关于世界服务受到调用仓储,在达标等同篇博文讨论着(czcz1024、Jesse
Liu、netfocus、刘标才…),主要得有些许种实现方式,这边我又盖总结下:

  1. 习俗艺术:仓储接口定义在世界层,实现以基础层,通过则来约束查询,一般返回路也聚合根集合对象,如果世界对象的查询逻辑比较多,具体体现就是是储存接口变多。
  2. IQueryable 方式:和方面不同之是接口的计划性变少了,因为归路也
    IQueryable,具体查询表达式的整合在了调用层,也就是是世界服务着,比如:xxxRepository.GetAll().Where(x=>….)

实质上这片种植艺术还是同样拿双刃剑,关键在于自己因现实的事情场景进行选了,我说一样下自己之有明,比如现实生活中车库的情景,我们得以拿车库看作是储存,取车的经过作为是储存的调用,车子的摆设根据汽车的标准化,也不怕是储存中之律概念,比如自己今天如起来平辆德系、红色、敞篷、双宗的赛车(条件稍微多哈),然后我就是错过车库取车,在车库的“调度系统“(在储存底具体表现,可以当是
EF)中输入这些命令,然后同辆兰博基尼就应运而生于我之眼前了。

以方描述的现实状况被,如果是第一栽传统方式,“我而起来平辆德系、红色、敞篷、双宗的跑车”这个就是好计划啊仓储的一个接口,为什么?因为车库可以换掉,而这些业务用例一般不见面进行改动,车库中之“调度体系”根据指令是什么寻找汽车之为?答案是法的咬合,也就是储存中规的整合,我们于对实际作业场景设计的时,一般会提炼出此事情场景中的准则,这个呢是不可变的,根据指令来拓展对这些规则的构成,这个过车的切实可行体现就是是储存的落实,约束的凡聚合根对象。这种措施受,我个人觉得好处是好充分利用规约,仓储的求实调用统一管理,让调用者感觉不顶它们是哪行事的,因为它独自待传一个令过去,就好落想使的结果,唯一不好的地方便是:我心情不好,每天开始的汽车都非同等,这个就是设格外人了,因为我要规划不同之蕴藏接口来拓展对规则的做。

万一是亚栽方法,也就是是将“调度体系”的使用权交至温馨手里(第一种的是历程可作为是经过书记),这种措施的好和特别,我虽非多说了,我现利用的凡第一种植方式,主要发生零星个因:

  1. 提防 IQueryable 的滥用(领域服务大像 DAL)。
  2. 现行使场景中之询问比少,没必要。

齐同一首博文被贴出底是,发送短消息的应用层代码,发送的事情验证放在了应用层,以致吃
SendSiteMessageService.SendMessage 中只有生平等段落“return
true”代码,修改以后的圈子服务代码:

    public class SendSiteMessageService : ISendMessageService
    {
        public async Task<bool> SendMessage(Message message)
        {
            IMessageRepository messageRepository = IocContainer.Resolver.Resolve<IMessageRepository>();
            if (message.Type == MessageType.Personal)
            {
                if (System.Web.HttpContext.Current != null)
                {
                    if (await messageRepository.GetMessageCountByIP(Util.GetUserIpAddress()) > 100)
                    {
                        throw new CustomMessageException("一天内只能发送100条短消息");
                    }
                }
                if (await messageRepository.GetOutboxCountBySender(message.Sender) > 20)
                {
                    throw new CustomMessageException("1小时内只能向20个不同的用户发送短消息");
                }
            }
            return true;
        }
    }

代码就是这么,如果您当有题目,欢迎提出,我更拓展改动。

当即边还说一下领域服务中贮存的流,缘由是我眼前几乎天禁闭了刘标才的等同首博文:DDD领域让设计之领域服务,文中对仓储的注入方式是经构造函数,这种措施的弊端就是小圈子服务对仓储产生大赖关系,还有即使是如果世界服务受到注入了大半独仓储,调用这个世界服务被之之一一个方式,而以此点子就是利用了一个存储,那么当对之圈子服务拓展注入的时候,就务须把装有存储都使进行注入,这便没必要了。

化解地方的题材的道就是是,在以仓储的地方对那个进行分析,比如:IocContainer.Resolve<IMessageRepository>();,这样便好避了端的问题,我们尚可把囤积的流入放在
Bootstrapper 中,也就是项目启动之地方。

SELECT 某某某

面所探讨的都是储存的调用,而本者问题是储存的贯彻,这是有限种植不同之概念。

哎呀是“SELECT
某某某”?答案就是对准字段进行询问,场景为应用程序的特性优化。我明白你盼“SELECT”就悟出了作业脚本模式,不要想歪了哦,你眼中之贮存实现不必然是
ORM,也得以是传统的
ADO.NET,如果存储实现应用的凡数据库持久化机制,其实还高档的
ORM,到最后还见面变成为 SQL
代码,具体表现就是指向这些代码的优化,似乎不属世界让设计的范畴了,但不可否认,这是应用程序不能不考虑的。

应用程序中之属性问题

本人说一下本短消息项目受到存储的贯彻(常用场景):底层以的是
EntityFramework,为了重新好的懂得,我贴平段查询代码:

        protected override async Task<IEnumerable<TAggregateRoot>> FindAll(ISpecification<TAggregateRoot> specification, System.Linq.Expressions.Expression<Func<TAggregateRoot, dynamic>> sortPredicate, SortOrder sortOrder, int pageNumber, int pageSize)
        {
            var query = efContext.Context.Set<TAggregateRoot>()
                .Where(specification.GetExpression());
            int skip = (pageNumber - 1) * pageSize;
            int take = pageSize;

            if (sortPredicate != null)
            {
                switch (sortOrder)
                {
                    case SortOrder.Ascending:
                        return query.SortBy(sortPredicate).Skip(skip).Take(take).ToListAsync();
                    case SortOrder.Descending:
                        return query.SortByDescending(sortPredicate).Skip(skip).Take(take).ToListAsync();
                    default:
                        break;
                }
            }
            return query.Skip(skip).Take(take).ToListAsync();
        }

这种办法产生什么问题吗?至少在咱们召开片 DDD
示例的下,没有其余问题,为什么?因为你无实际去下,也尽管体会不顶片问题,前一段时间短消息页面加载慢,一个凡是数据库索引问题(详见:程序员眼中之
SQL
Server-执行计划使会自己何以创造索引?),还有一个即使是信息列表查询的时段,把消息表的有着字段都拿走出来了,这是截然没必要之,比如音内容就是不需要开展读取,但是咱于跟点代码执行的早晚,会发觉
EntityFramework 生成的 SQL 代码为 SELECT *。。。

走过的弯路

面这问题,至少从老数据库索引问题解决了,我就是直接郁闷着,也尝着用各种办法去解决,比如创建
IQueryable 的 Select 表达式,传入的是起定义的集合根属性,还有即使是扩张
Select 表达式,详细经过尽管非回顾了,我贴一下立以搜索时之片材料:

  • IQueryable C#
    Select
  • The entity cannot be constructed in a LINQ to Entities
    query
  • Cannot implicitly convert type
    ‘System.Collections.Generic.IEnumerable’ to
    ‘System.Collections.Generic.List’. An explicit conversion exists
    (are you missing a
    cast?)
  • Cannot implicitly convert type ‘System.Linq.IQueryable’ to
    ‘System.Collections.Generic.IEnumerable’
  • Cannot implicitly convert type ‘System.Collections.Generic.List’ to
    ‘System.Collections.Generic.IEnumerable
  • 利用Entity
    Framework时如留心的一部分性问题
  • 未记得了…

当 EntityFramework 底层,我们 Get 查询的早晚,一般还是回来
TAggregateRoot
聚合根集合对象,也就是说,你无法于脚进行点名属性查询,因为聚合根只有
ID 一个属性,唯一的办法就是传播
Expression<Func<TAggregateRoot, TAggregateRoot>> selector
表达式,select 两只范型约束为 TSource 和 TDest,这边我们片种档次且也
TAggregateRoot ,但是实施结果吗:“The entity or complex type … cannot
be constructed in a LINQ to Entities query.”,给我的训就是 Select 中之
TSource 和 TDest 不可知为同一品种(至少指定属性之情形下)。

自己的化解方案

EntityFramework 底层的持有查询返回路变更吧
IQueryable<TAggregateRoot>,仓储的询问返回路变更也
IEnumerable<MessageListDTO>,为什么是 MessageListDTO 而非是
Message?因为自身道消息列表的来得,就是对信息之扁平化处理,没必要是一个
Message
实体对象,虽然它是一个消息实体仓储,就好于从车库受取出一个所有汽车列表的单子,有必不可少将有汽车实体取得出来吧?很肯定没有必要,我们无非需要取出汽车的部分消息即可,我觉着这是承诺本着事情场景变化所必须要调动之,具体的兑现代码:

        public async Task<IEnumerable<MessageListDTO>> GetInbox(Contact reader, PageQuery pageQuery)
        {
            return await GetAll(new InboxSpecification(reader), sp => sp.ID, SortOrder.Descending, pageQuery.PageIndex, pageQuery.PageSize)
                 .Project().To<MessageListDTO>()
                 .ToListAsync();
        }

“Project().To()” 是啊东西?这是 AutoMapper 本着 IQueryable
表达式的一个扩大,详情请参考:热恋爱就容易,相处是:当 EntityFramework
爱上
AutoMapper,AutoMapper
扩展说明:Queryable
Extensions,简单的同样截代码就可以得实体和
DTO 之间的转账,我们再用 SQL Server Profiler 捕获生成的 SQL
代码,就会见发现,这虽是咱们想如果的,根据映射配置 Select 指定字段查询。

形容以终极

图片 1

针对“SELECT
某某某”这个实际用问题,以上才是自我的私实现方式,如果您出问号或有双重好之落实,欢迎指教。。。

相关文章