最近在研究DDD,同时也下载了一些基于DDD做的成熟案例用来学习,有一些吧,过于成熟,顺便就从里面取了取别的经,比如这个ByteartRetail项目,里面对数据的操作狠花了我一些时间
展开看看
其实有个问题很明显,同为基于DDD进行的项目架构设计,不同人设计的项目分层、命名和里面放的东西都不相同,看样子DDD这玩意,纯粹就是一个思维方式,所以也只能从各种实现里面找自己最能懂的来实现适合自己的架构方式了。很抱歉,因为找示例的时候找得太多,就忘了每个项目的来处了,不过大多是在StackOverflow里面别人推荐的,你们可以去找找看。
上面这个项目它实现了用A.And(B).And(C).Or(D)这种方式来动态生成lambda表达式,这个不在本篇讨论范围内,看一看它是怎么用Repository模式和Unit Of Work模式来做数据层的
因为用的EF框架,所以我们直接看EntityFrameworkRepository,对外就是用的它的方法。
这个图我简化了一些,但骨干都有了,首先,IRepository容器接口定义了基本的增删改查接口(复杂查询、分页都可以做在里面)
其次,IUnitOfWork接口按unit of work思想把数据操作和提交分离,听起来很高深,但实现上,不过是在所有的数据操作里面不去SaveChanges(),而是通过标记状态的方式改变数据(由IRepositoryContext定义),最后统一由Commit方法来提交更改。出于各司其职的需要,所以把简单的事变成了三个接口,分别处理:增删改的对外接口、增删改的标记动作、事务的提交和回滚,这三件事
于是,分别有了Repository和RepositoryContext这两个类,它们实现的哪个接口见上图
接下来,IEntityFrameworkRepositoryContext接口主要是为了针对Entity Framework做实现,所以加了一个DbContext的属性,也仅仅只是加了一个属性。这样,EntityFrameworkRepositoryContext实现IEntityFrameworkRepositoryContext的同时,继承Repository,就拥有了对DbContext对象进行标识增删改和提交、回滚的能力了。
到此,我们再把Repository类扩展一下,用EntityFrameworkRepository来继承之,从构造函数传入上面的包含了DbContext对象的EFcontext类(接口),把标识和提交的能力都带了进来,最终一个符合unit of work模式的具有操作DbContext对象的数据层就诞生了。
思路搞清了,我就简化了一下,如下:
这一次,我把Repository变为了最终暴露的类。但是实现的时候碰到了一个难题。示例程序中,DbContext对象居然是直接在EFRepositoryContext内部直接new出来的:
private readonly ThreadLocal<ByteartRetailDbContext> localCtx = new ThreadLocal<ByteartRetailDbContext>(() => new ByteartRetailDbContext());
这样,上面的红字我标出了它把DbContext带进去的方式,是把dbcontext实例化了,然后整个对象变成构造函数传进去,我不行啊,这个DbContext很可能不止有一个啊,那么就只有把DbContext本身变成RepositoryContext的构造函数(而不是示例中的repositorycontext)了,然后Repository继承它。最终形成上图。
这种方式有一个缺点,就是最终的容器不是同其接口一一对应的,而是扩展了别的功能,结果就是在外部使用的时候必须用Repository实例,而不能用接口,以实现依赖注入(DI)的功能,管它呢,先告一段落吧
试用一下
//测试服务接口 public interface Itest { string outstirng(string msg); } //测试服务实现 public string outstirng() { var u = new Repository<user>(new myEntities());//DbContext由此传入 user us = u.GetEntity(m => m.id == 2); us.addr = "update" + DateTime.Now.ToString("ddfff"); u.Update(us); us = u.GetEntity(m => m.id == 1); u.Delete(us); u.Commit();//一次提交,不提交不生效 us = u.GetEntity(m => m.id == 2); return us.addr; }
在MVC4项目里测试,用了SimpleInjector做IOC的注入:
//注入 public static void Initialize() { var container = new Container(); InitializeContainer(container); container.RegisterMvcControllers(Assembly.GetExecutingAssembly()); container.RegisterMvcAttributeFilterProvider(); container.Verify(); DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container)); } private static void InitializeContainer(Container container) { container.Register<Itest, impltest>(); }
Controller:
public class TestController : Controller { Itest svr; public TestController(Itest service) { svr = service; } public string Index() { return svr.outstirng(); } }
顺利得到结果