MVC+UnitOfWork+Repository+EF
UnitOfWork+Repository模式简介:
每次提交数据库都会打开一个连接,造成结果是:多个连接无法共用一个数据库级别的事务,也就无法保证数据的原子性、一致性。解决办法是:在Repository的CRUD操作基础上再包装一层,提供统一的入口,让服务层调用。同一个UnitOfWork实例对象下所有的Repository都共同一个数据库上下文对象(ps:EF用的是DbContext),也就是共用一个事物。提交数据库时,只要有一个操作失败,那么所有的操作都被视为失败。
项目结构:

关键代码:
AggregateRoot.cs:

1 using System;
2 using System.Collections.Generic;
3
4 namespace CMS.Domain.Core
5 {
6 /// <summary>
7 /// 表示聚合根类型的基类型。
8 /// </summary>
9 public abstract class AggregateRoot : IAggregateRoot
10 {
11 #region 方法
12
13 public virtual IEnumerable<BusinessRule> Validate()
14 {
15 return new BusinessRule[] { };
16 }
17
18 #endregion
19
20 #region Object 成员
21
22 public override bool Equals(object obj)
23 {
24 if (obj == null)
25 return false;
26
27 if (ReferenceEquals(this, obj))
28 return true;
29
30 IAggregateRoot ar = obj as IAggregateRoot;
31
32 if (ar == null)
33 return false;
34
35 return this.Id == ar.Id;
36 }
37
38 public override int GetHashCode()
39 {
40 return this.Id.GetHashCode();
41 }
42
43 #endregion
44
45 #region IAggregateRoot 成员
46
47 public Guid Id
48 {
49 get;
50 set;
51 }
52
53 #endregion
54 }
55 }

Channel.cs:

1 using CMS.Domain.Core;
2
3 namespace CMS.Domain.Entities
4 {
5 public class Channel : AggregateRoot
6 {
7 public string Name
8 {
9 get;
10 set;
11 }
12
13 public string CoverPicture
14 {
15 get;
16 set;
17 }
18
19 public string Desc
20 {
21 get;
22 set;
23 }
24
25 public bool IsActive
26 {
27 get;
28 set;
29 }
30
31 public int Hits
32 {
33 get;
34 set;
35 }
36 }
37 }

IUnitOfWork.cs:

1 using System;
2
3 namespace CMS.Domain.Core.Repository
4 {
5 /// <summary>
6 /// 工作单元
7 /// 提供一个保存方法,它可以对调用层公开,为了减少连库次数
8 /// </summary>
9 public interface IUnitOfWork : IDisposable
10 {
11 #region 方法
12
13 IRepository<T> Repository<T>() where T : class, IAggregateRoot;
14
15 void Commit();
16
17 #endregion
18 }
19 }

UnitOfWork.cs:

1 using CMS.Common;
2 using CMS.Domain.Core;
3 using CMS.Domain.Core.Repository;
4 using System;
5 using System.Collections;
6 using System.Collections.Generic;
7
8 namespace CMS.Infrastructure
9 {
10 public class UnitOfWork : IUnitOfWork, IDisposable
11 {
12 #region 变量
13
14 private bool _disposed;
15 private readonly IDbContext _dbContext;
16 private Hashtable _repositories;
17
18 #endregion
19
20 #region 构造函数
21
22 public UnitOfWork(IDbContext dbContext)
23 {
24 this._dbContext = dbContext;
25 this._repositories = new Hashtable();
26 }
27
28 #endregion
29
30 #region 方法
31
32 public virtual void Dispose(bool disposing)
33 {
34 if (!this._disposed)
35 if (disposing)
36 this._dbContext.Dispose();
37
38 this._disposed = true;
39 }
40
41 #endregion
42
43 #region IUnitOfWork 成员
44
45 public IRepository<T> Repository<T>() where T : class, IAggregateRoot
46 {
47 var typeName = typeof(T).Name;
48
49 if (!this._repositories.ContainsKey(typeName))
50 {
51 52
53 var paramDict = new Dictionary<string, object>();
54 paramDict.Add("context", this._dbContext);
55
56 //Repository接口的实现统一在UnitOfWork中执行,通过Unity来实现IOC,同时把IDbContext的实现通过构造函数参数的方式传入
57 var repositoryInstance = UnityConfig.Resolve<IRepository<T>>(paramDict);
58
59 if (repositoryInstance != null)
60 this._repositories.Add(typeName, repositoryInstance);
61 }
62
63 return (IRepository<T>)this._repositories[typeName];
64 }
65
66 public void Commit()
67 {
68 this._dbContext.SaveChanges();
69 }
70
71 #endregion
72
73 #region IDisposable 成员
74
75 public void Dispose()
76 {
77 this.Dispose(true);
78
79 GC.SuppressFinalize(this);
80 }
81
82 #endregion
83 }
84 }

BaseRepository.cs:

1 using CMS.Domain.Core;
2 using CMS.Domain.Core.Repository;
3 using System;
4 using System.Collections.Generic;
5 using System.Data.Entity;
6 using System.Linq;
7 using System.Linq.Expressions;
8
9 namespace CMS.Infrastructure
10 {
11 public class BaseRepository<T> : IRepository<T> where T : class, IAggregateRoot
12 {
13 #region 变量
14
15 private readonly DbContext _db;
16 private readonly IDbSet<T> _dbset;
17
18 #endregion
19
20 #region 构造函数
21
22 public BaseRepository(IDbContext context)
23 {
24 this._db = (DbContext)context;
25 this._dbset = this._db.Set<T>();
26 }
27
28 #endregion
29
30 #region IRepository 成员
31
32 public void Add(T item)
33 {
34 this._dbset.Add(item);
35 }
36
37 public void Remove(T item)
38 {
39 this._dbset.Remove(item);
40 }
41
42 public void Modify(T item)
43 {
44 this._db.Entry(item).State = EntityState.Modified;
45 }
46
47 public T Get(Expression<Func<T, bool>> filter)
48 {
49 return this._dbset.Where(filter).SingleOrDefault();
50 }
51
52 public IEnumerable<T> GetAll()
53 {
54 return this._dbset.ToList();
55 }
56
57 public IEnumerable<T> GetPaged<KProperty>(int pageIndex, int pageSize, out int total, Expression<Func<T, bool>> filter, Expression<Func<T, KProperty>> orderBy, bool ascending = true, string[] includes = null)
58 {
59 pageIndex = pageIndex > 0 ? pageIndex : 1;
60
61 var result = this.GetFiltered(filter, orderBy, ascending, includes);
62
63 total = result.Count();
64
65 return result.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
66 }
67
68 public IEnumerable<T> GetFiltered<KProperty>(Expression<Func<T, bool>> filter, Expression<Func<T, KProperty>> orderBy, bool ascending = true, string[] includes = null)
69 {
70 var result = filter == null ? this._dbset : this._dbset.Where(filter);
71
72 if (ascending)
73 result = result.OrderBy(orderBy);
74 else
75 result = result.OrderByDescending(orderBy);
76
77 if (includes != null && includes.Length > 0)
78 {
79 foreach (var include in includes)
80 {
81 result = result.Include(include);
82 }
83 }
84
85 return result.ToList();
86 }
87
88 #endregion
89 }
90 }

IDbContext.cs:

1 namespace CMS.Infrastructure
2 {
3 public interface IDbContext
4 {
5 #region 方法
6
7 int SaveChanges();
8
9 void Dispose();
10
11 #endregion
12 }
13 }

CMSDbContext.cs:

1 using CMS.Infrastructures.Mapping;
2 using System.Data.Entity;
3 using System.Data.Entity.ModelConfiguration.Conventions;
4
5 namespace CMS.Infrastructure
6 {
7 public class CMSDbContext : DbContext, IDbContext
8 {
9 #region 构造函数
10
11 public CMSDbContext()
12 : base("SqlConnectionString")
13 {
14
15 }
16
17 #endregion
18
19 #region DbContext 重写
20
21 protected override void OnModelCreating(DbModelBuilder modelBuilder)
22 {
23 modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
24
25 modelBuilder.Configurations.Add(new ChannelEntityConfiguration());
26 }
27
28 #endregion
29 }
30 }

UnityConfig.cs:

1 using Microsoft.Practices.Unity;
2 using Microsoft.Practices.Unity.Configuration;
3 using System;
4 using System.Collections.Generic;
5
6 namespace CMS.Common
7 {
8 public class UnityConfig
9 {
10 #region 属性
11
12 public static IUnityContainer Container
13 {
14 get
15 {
16 return container.Value;
17 }
18 }
19
20 #endregion
21
22 #region 方法
23
24 private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(
25 () =>
26 {
27 var container = new UnityContainer();
28
29 RegisterTypes(container);
30
31 return container;
32 });
33
34 private static void RegisterTypes(IUnityContainer container)
35 {
36 container.LoadConfiguration();
37 }
38
39 public static T Resolve<T>(IDictionary<string, object> paramDict = null)
40 {
41 var list = new ParameterOverrides();
42
43 if (paramDict != null && paramDict.Count > 0)
44 {
45 foreach (var item in paramDict)
46 {
47 list.Add(item.Key, item.Value);
48 }
49 }
50
51 return Container.Resolve<T>(list);
52 }
53
54 #endregion
55 }
56 }

ChannelApplcationService.cs:

1 using AutoMapper;
2 using CMS.Domain.Core.Repository;
3 using CMS.Domain.Entities;
4 using CMS.DTO;
5 using Microsoft.Practices.Unity;
6
7 namespace CMS.Applcation
8 {
9 public class ChannelApplcationService
10 {
11 #region 属性
12
13 [Dependency]
14 public IUnitOfWork UnitOfWork { get; set; }
15
16 #endregion
17
18 #region 方法
19
20 public Response<bool> Add(ChannelDTO dto)
21 {
22 var resp = new Response<bool>();
23 var channel = Mapper.Map<Channel>(dto);
24
25 using (this.UnitOfWork)
26 {
27 var channelAddRepository = this.UnitOfWork.Repository<Channel>();
28
29 channelAddRepository.Add(channel);
30
31 this.UnitOfWork.Commit();
32 }
33
34 resp.Result = true;
35
36 return resp;
37 }
38
39 #endregion
40 }
41 }

序列图:

心得体会:
1. Repository的CRUD操作只能作用于继承了AggregateRoot基类的DomainObject(ps:以实际项目情况为准,可以做适当的妥协)。
2. DomainObject中涉及到集合类型(如IList,ISet等)的聚合属性需要加“virtual”关键字,让ORM框架识别做Lazyload处理。
3. 各自独立的业务逻辑写在对应的DomainObject方法中,方法体内只能处理自身以及内部聚合对象的数据和状态等信息,被聚合的对象不建议里面再有方法,只需定义相关属性即可(ps:涉及到对外通知、发布消息等场景以DomainEvent的方式处理,关于DomainEvent的概念和使用会开新章进行简述)。
4. 把需要多个DomainObject交互和协调的业务逻辑放到DomainService中(ps:在Applcation Layer中调用。另外DomainService是否能调用Repository对象我一直很困惑,因为看过有代码是这么写的,但又有人不建议这么做......)。
5. 在AggregateRoot基类中定义验证BusinessRule的虚方法,供子类重写,并在统一的地方执行(比如Applcation Layer)
6. 定义DomainException,用来封装Domain Layer层的异常信息,对上层(Applcation Layer)暴露。
7. Applcation Layer代码的主要作用(可用WCF、WebAPI或直接Dll引用等方式对上层(UI Layer)暴露)
- 接收UI Layer传递的DTO对象。
 - 通过AutoMapper组件转换成对应的DomainObject,并调用其方法(ps:内部业务逻辑的封装)。
 - 调用Repository对象来实现CRUD操作(ps:这时数据还只是在内存中)。
 - 调用UnitOfWork的Commit方法来实现数据的真正提交(ps:事物级别的)。
 
所以可以看出Applcation Layer主要用来处理业务的执行顺序,而不是关键的业务逻辑。
Applcation Layer如果用WCF或WebAPI的方式对外暴露有个好处,可以针对其作负载均衡,坏处是额外增加了IIS的请求开销。
8. DTO和DomainObject区别
DTO(ps:为了简单起见,这里把DTO和ViewModel放在一块说了):
- 根据实际业务场景加上Required、StringLength等验证特性,结合MVC框架的内部验证机制,可在Controller层做到数据的有效性验证(ps:永远都不要轻易相信浏览器端提交的数据,即使已经有了js脚本验证......)。
 - 负责View数据的展现和表单提交时数据的封装。
 - 负责把数据从UI Layer传递到Applcation Layer,里面只能有属性,而且是扁平的,结构简单的属性。
 
DomainObject:通俗点说就是充血模型,包括属性和行为,在DDD整个框架设计体系中占非常重要的地位,其涵盖了整个软件系统的业务逻辑、业务规则、聚合关系等方面。(ps;如果业务很简单,可以只有属性)
9. UI Layer:自我学习UnitOfWork+Repository以来,一直用的是MVC框架做前台展现,选择的理由:1. Unity4MVC的IOC功能非常强大,2. 天生支持AOP的思想,3. 更加传统、原始的Web处理方式,4. Areas模块对插件化设计的支持,5. Ajax、ModelBuilder、JSON、验证,6. 整个Http访问周期内提供的各种扩展点等。
MVC+UnitOfWork+Repository+EF的更多相关文章
- MVC+UnitOfWork+Repository+EF 之我见
		
UnitOfWork+Repository模式简介: 每次提交数据库都会打开一个连接,造成结果是:多个连接无法共用一个数据库级别的事务,也就无法保证数据的原子性.一致性.解决办法是:在Reposito ...
 - MVC中使用EF(2):实现基本的CRUD功能
		
MVC中使用EF(2):实现基本的CRUD功能 By Tom Dykstra |July 30, 2013 Translated by litdwg Contoso University示例网站 ...
 - MVC UnitOfWork EntityFramework架构
		
MVC UnitOfWork EntityFramework架构,网站速度慢的原因总结! 最近参考使用了郭明峰的一套架构来做新的项目架构,这套架构看起来还是不错的,先向小郭同学的分享精神致敬! (郭同 ...
 - ASP.NET MVC 5 with EF 6 上传文件
		
参考 ASP.NET MVC 5 with EF 6 - Working With Files Rename, Resize, Upload Image (ASP.NET MVC) ASP ...
 - 解析ASP.NET Mvc开发之EF延迟加载
		
目录: 1)从明源动力到创新工场这一路走来 2)解析ASP.NET WebForm和Mvc开发的区别 3)解析ASP.NET Mvc开发之查询数据实例 ------------------------ ...
 - MVC 中使用EF
		
EF 1)简单查询 后台代码 using MvcApplication18.Models; using System; using System.Collections.Generic; using ...
 - MVC中使用EF(1):为ASP.NET MVC程序创建Entity Framework数据模型
		
为ASP.NET MVC程序创建Entity Framework数据模型 (1 of 10) By Tom Dykstra |July 30, 2013 Translated by litdwg ...
 - mvc+webapi+dapper+ef codefirst项目搭建
		
首先项目是mvc5+webapi2.0+orm数据处理(dapper)+ef动态创建数据库. 1.项目框架层次结构: mvc项目根据不同的业务和功能进行不同的区域划分[今后项目维护起来方便],mode ...
 - .NET Core2.0 MVC中使用EF访问数据
		
使用环境:Win7+VS2017 一.新建一个.NET Core2.0的MVC项目 二.使用Nuget添加EF的依赖 输入命令:Install-Package Microsoft.EntityFram ...
 
随机推荐
- myeclipse 8.5-10.0 安装 svn 方法(转)
			
方法总结 方法一:在线安装 1.打开HELP->MyEclipse Configuration Center.切换到SoftWare标签页. 2.点击Add Site 打开对话框 ...
 - Linux核心regulator建筑和准备
			
电源引入的物种 (百度百科)LDO这是low dropout regulator,这意味着低压差线性稳压器.它相比于传统的线性调节器.传统的线性稳压器.例如78xx系列芯片需要输入电压比输出电压高2v ...
 - mac已安装xctool而简单的执行xctool打包
			
先安装brew,brew是一个包管理工具,有了它我们就能够非常方便的安装xctool了,brew安装命令例如以下: curl -LsSf http://github.com/mxcl/homebrew ...
 - 解决IE下Ajax请求无效
			
在做web开发是,大多时候都会使用FireFox作为调试的浏览器.上面携带的FireBug用来调试JavaScript实在是太方便了,绝大多数的问题都能够通过它跟踪调试出来.但是,当项目发布时,不能仅 ...
 - 移动开发 Native APP、Hybrid APP和Web APP介绍
			
高速区分定义: Native App 以基于智能手机本地操作系统如IOS.Android.WP并使用原生程式(SDK)编写执行的须要用户安装使用的第三方应用程序; Web APP 以HTML+JS+C ...
 - kendoui仪表板和直方图 演示样本
			
到那个时刻kendeodui我相信大家一定不陌生.该js在绘画方面的好成绩. 现在来看看 它的仪表盘和直方图效果: html和js代码例如以下: <!DOCTYPE html> <h ...
 - 开展.net mvc3遇到怪事+解
			
发展到今天.net mvc3遇到怪事. 使用Firefox浏览器.打开index页,求index该控制器是很多次,代码查询数据库的多个运行.server减速. 而且没有刷新页面,随着时间的推移有十二请 ...
 - PHP于Post和Get得到的数据写入到文件中
			
有时Post要么Get越过那我们不知道什么样的形状数据,它可以是JSON格风格或只是简单地通过数据.这一次,我们能够把他写的文字,传过来的数据是什么格式了. $val = ""; ...
 - ASP.NET MVC+EF框架+EasyUI实现权限管理系列(1)-框架搭建
			
原文:ASP.NET MVC+EF框架+EasyUI实现权限管理系列(1)-框架搭建 ASP.NET MVC+EF框架+EasyUI实现权限管系列 (开篇) 前言:这篇博客开始我们便一步一步的来实现这 ...
 - Debug和Release的不同
			
1. 变量. 大家都知道,debug跟release在初始化变量时所做的操作是不同的,debug是将每个字节位都赋成0xcc(注1),而release的赋值近似于随机(我想是直接从内存中分配的,没有 ...