TinyFrame升级之七:重构Repository和Unit Of Work
首先,重构的想法来源于以下文章:Correct use of Repository and Unit Of Work patterns in ASP.NET MVC,因为我发现在我的框架中,对UnitOfWork使用了错误的设计方法,同时感谢一下文章:Generically Implementing the Unit of Work & Repository Pattern with Entity Framework in MVC & Simplifying Entity Graphs,我的重构设计参考了它。
下面来说说我的具体重构方式。
在原来的代码中,我的Repository<T>泛型继承自IRepository<T>并且在增删改查代码中做了如下处理:
1: public virtual void Insert(T entity)
2: {
3: try
4: {
5: if (entity == null)
6: throw new ArgumentException("实体类为空");
7: DbSet.Add(entity);
8: context.SaveChanges();
9: }
10: catch (DbEntityValidationException dbex)
11: {
12: var msg = string.Empty;
13: foreach(var validationErrors in dbex.EntityValidationErrors)
14: foreach(var validateionError in validationErrors.ValidationErrors)
15: msg+=string.Format("Property:{0} Error:{1}",validateionError.PropertyName,validateionError.ErrorMessage);
16:
17: var fail = new Exception(msg,dbex);
18: throw fail;
19: }
20: }
最关键的地方是,我添加了“Context.SaveChanges”方法,这就直接导致UnitOfWork的规则失效。UnitOfWork出现的本身是为了提供事务提交支持。这样直接在Repository中提交,直接导致UnitOfWork功能废弃。
还有个地方就是在前台,通过Autofac注册完毕后,我是这么用的:
1: public BookService(IUnitOfWork unitOfWork
2: , IBook bookRepository
3: , IBookType bookTypeRepository
4: , IBookPlace bookPlaceRepository
5: , ICacheManager cacheManager
6: , ILoggerService logger
7: )
8: {
9: this.unitOfWork = unitOfWork;
10: this.bookRepository = bookRepository;
11: this.bookTypeRepository = bookTypeRepository;
12: this.bookPlaceRepository = bookPlaceRepository;
13: this.cacheManager = cacheManager;
14: this.logger = logger;
15: }
16:
17: private readonly IUnitOfWork unitOfWork;
18: private readonly IBook bookRepository;
19: private readonly IBookType bookTypeRepository;
20: private readonly IBookPlace bookPlaceRepository;
21: private readonly ICacheManager cacheManager;
22: private readonly ILoggerService logger;
这样做的话,当以后我们有表删除或者新增的时候,我们不得不维护这样的列表。这完全不符合OO设计原则。
但是如果引入UnitOfWork的话,内部利用Hashtable等实现对Respository的指向,那么在界面我们只要这样写就可以了:
1: public BookService(
2: IUnitOfWork unitOfWork
3: , ICacheManager cacheManager
4: , ILoggerService logger
5: )
6: {
7: this.unitOfWork = unitOfWork;
8: this.cacheManager = cacheManager;
9: this.logger = logger;
10: }
11:
12: private readonly IUnitOfWork unitOfWork;
13: private readonly ICacheManager cacheManager;
14: private readonly ILoggerService logger;
使用的时候,直接这样实例化就行了:
1: var bookPlaceRepository = unitOfWork.Repository<BookPlace>();
这样做完全不用顾虑有新表的添加删除了。代码根本就不用动。
所以,综上两点,UnitOfWork的引入为了解决以下问题:
1.提供全局事务支持。
2.提供对Repository模型的指向。以便于松耦合绑定。
下面是代码重构部分:
IUnitOfWork接口部分:
1: using System;
2: using TinyFrame.Data.DataRepository;
3: using TinyFrame.Data.DomainModel;
4:
5: namespace TinyFrame.Unitofwork
6: {
7: public interface IUnitOfWork
8: {
9: void Commit();
10: IRepository<T> Repository<T>() where T : class;
11:
12: void Dispose(bool disposing);
13: void Dispose();
14: }
15: }
实现部分比较简单,利用Hashtable来保存对Repository的指向:
1: using System;
2: using System.Data.Entity;
3: using TinyFrame.Data.DataRepository;
4: using TinyFrame.Data.DomainModel;
5: using TinyFrame.Data.DataContext;
6: using System.Collections;
7:
8: namespace TinyFrame.Unitofwork
9: {
10: public class UnitOfWork : IUnitOfWork
11: {
12: public UnitOfWork(IDbContext dbContext)
13: {
14: this.dbContext = dbContext;
15: }
16:
17: private readonly IDbContext dbContext;
18: private bool disposed;
19: private Hashtable repositorys;
20:
21: public void Commit()
22: {
23: dbContext.SaveChanges();
24: }
25:
26: public IRepository<T> Repository<T>() where T:class
27: {
28: if (repositorys == null)
29: repositorys = new Hashtable();
30:
31: var type = typeof(T).Name;
32:
33: if (!repositorys.ContainsKey(type))
34: {
35: var repositoryType = typeof(Repository<>);
36: var repositoryInstance = Activator.CreateInstance(repositoryType.MakeGenericType(typeof(T)), dbContext);
37: repositorys.Add(type, repositoryInstance);
38: }
39: return (IRepository<T>)repositorys[type];
40: }
41:
42: #region Dispose method
43: public virtual void Dispose(bool disposing)
44: {
45: if (!disposed)
46: if (disposing)
47: dbContext.Dispose();
48: disposed = true;
49: }
50:
51: public void Dispose()
52: {
53: Dispose(true);
54: GC.SuppressFinalize(this);
55: }
56: #endregion
57: }
58: }
第17行,保持对DbContext的引用,以便于进行提交操作。
第26行,Repository<T>泛型方法,以便于动态返回仓储模型。
需要注意的是,在Repository<T>的实现中,不要再增删改查里面再添加 DbContext.SaveChanges方法,首先是没意义,其次是完全不符合Repository和UnitOfWork的做法。
最后附图一张,表明我对Repository和UnitOfWork的理解:
本章源码下载:
TinyFrame升级之七:重构Repository和Unit Of Work的更多相关文章
- MVC3+EF4.1学习系列(八)-----利用Repository and Unit of Work重构项目
项目最基础的东西已经结束了,但是现在我们的项目还不健全 不利于测试 重复性代码多 层与层之间耦合性高 不利于扩展等问题.今天的这章 主要就是解决这些问题的.再解决这些问题时,自己也产生了很多疑 ...
- 在Entity Framework 4.0中使用 Repository 和 Unit of Work 模式
[原文地址]Using Repository and Unit of Work patterns with Entity Framework 4.0 [原文发表日期] 16 June 09 04:08 ...
- 转载Repository 和Unit of work的使用说明
利用Repository and Unit of Work重构项目 文章索引和简介 项目最基础的东西已经结束了,但是现在我们的项目还不健全 不利于测试 重复性代码多 层与层之间耦合性高 不利于 ...
- TinyFrame升级之一:框架概览
由于之前的TinyFrame多于简单,并且只是说明原理,并无成型的框架出来,所以这次我把之前的知识进行了汇总,然后做出了这一版的TinyFrame框架. 整个框架的结构如下: TinyFrame.Da ...
- Using the Repository and Unit Of Work Pattern in .net core
A typical software application will invariably need to access some kind of data store in order to ca ...
- TinyFrame升级之三:逻辑访问部分
在上一篇,我们打造了自己的数据访问部分,这篇,我们准备讲解如何打造逻辑访问部分. 在上一篇中,我们利用Repository模式构建了基于泛型的操作合集.由于这些操作的合集都是原子性的操作,也就是针对单 ...
- TinyFrame升级之四:IOC容器
在这个框架中,我们使用Autofac作为IOC容器,来实现控制反转,依赖注入的目的. 在程序加载的时候,我需要将系统中所有用到的接口与之对应的实现进行装载.由于用户交互部分是在TinyFrame.We ...
- TinyFrame升级之九:实现复杂的查询
本章我们主要讲解如何实现一个复杂的查询.由于目前TinyFrame框架已经投入到了实际的项目生产中,所以我很乐意将项目中遇到的任何问题做以记录并备忘. 这章中,我们提到的查询界面如下所示: 其中,涉及 ...
- TinyFrame升级之十:WCF Rest Service注入IOC的心
由于在实际开发中,Silverlight需要调用WebService完成数据的获取,由于之前我们一直采用古老的ASMX方式,生成的代理类不仅难以维护,而且自身没有提供APM模式的调用方式,导致在Sin ...
随机推荐
- C#编写抽奖问题
输入每个人的中奖号码,进行滚动显示 //清屏 //随即一个索引 // 根据索引打印 //等待0.1秒 Console.Write("请输入参与者人数:&q ...
- TSQL--临时表和表变量
1. 临时表适用数据量较大的情况,因为临时表可以建立索引 2. 表变量适用于数据较小的情况,表变量只能在定义时创建约束(PRIMARY KEY/UNIQUE)从而间接建立索引 3. 临时表是事务性的, ...
- PL/SQL远程备份和恢复Oracle数据库
(转自:http://blog.csdn.net/huchunfu/article/details/25165901) 在客户端远程备份的文件保存在数据库所在主机上,不会直接拷贝到客户端.—————— ...
- Rac grid用户启停监听报错无权限
[grid@max1 ~]$ lsnrctl stop LSNRCTL for Linux: Version 11.2.0.3.0 - Production on 04-NOV-2016 00:20: ...
- 密码校验正则表达式(java 环境)
密码校验需求: 1) 密码控制只能输入字母.数字.特殊符号(~!@#$%^&*()_+[]{}|\;:'",./<>?)2) 长度 6-16 位,必须包括字母.数字.特殊 ...
- 烂泥:【解决】修改LVM卷组名重启系统后,无法进入进入系统
本文由秀依林枫提供友情赞助,首发于烂泥行天下. 一台服务器系统已经安装完毕,但是LVM的卷组vg使用的是默认的VolGroup名称,使用起来感觉不舒服,打算把这个卷组名称修改为vg. 先来查看系统中有 ...
- 烂泥:CentOS6.5光盘以及ISO镜像文件的使用
本文由秀依林枫提供友情赞助,首发于烂泥行天下. 学习CentOS有一段时间了,在平时使用过程中.系统镜像以及光盘使用的比较多,这篇文章就从实用的角度介绍相关有关光盘与镜像文件的使用. 因为物理机相关的 ...
- Session Sticky About Nginx
Nginx以前对session 保持支持不太好,主要采用ip_hash把同一来源的客户(同一C段的IP)固定指向后端的同一台机器,ip_hash有个缺点是不能实现很好的负载均衡:直到nginx的扩展模 ...
- [转]How to add new table in NopCommerce
本文转自:http://www.tech-coder.com/2015/07/how-to-add-new-table-in-nopcommerce.html Hey guys I am back a ...
- 以最简单的登录为例,诠释JS面向对象的简单实例
JavaScript,是前端开发人员必须会的一门技术,从JS演变出来的有很多框架,先说说几个热门的框架吧: JQuery:这个技术必须会,如果不会,那一定要会查api,知道怎么写,要看得懂英文文档,这 ...