最近在研究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)的功能,管它呢,先告一段落吧
試用一下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
//測試服務接口
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的注入:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//注入
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:
1
2
3
4
5
6
7
8
9
10
11
12
|
public
class
TestController : Controller
{
Itest svr;
public
TestController(Itest service)
{
svr = service;
}
public
string
Index()
{
return
svr.outstirng();
}
}
|
順利得到結果