在上一篇博客【.Net设计模式系列】仓储(Repository)模式 ( 一 ) 中,通过各位兄台的评论中,可以看出在设计上还有很多的问题,在这里特别感谢 @横竖都溢 @ 浮云飞梦 2位兄台对博文中存在的问题给予指出,并提供出好的解决方案,同时也感谢其他园友的支持。欢迎各位园友对博文中出现的错误或者是设计误区给予指出,一方面防止“误人子弟”,另一方面则可以让大家共同成长。
对于上一篇博客,只是给大家提供了一种对于小型项目数据访问层的一种实现方式,通过Sql语句和传递参数来实现CRUD。并未达到真正意义上的解耦。特此在本篇继续完善。
在进行数据库添加、修改、删除时,为了保证事务的一致性,即操作要么全部成功,要么全部失败。例如银行A、B两个账户的转账业务。一方失败都会导致事务的不完整性,从而事务回滚。而工作单元模式可以跟踪事务,在操作完成时对事务进行统一提交。
理论参考:http://martinfowler.com/eaaCatalog/unitOfWork.html
首先,讲解下设计思想:领域层通过相应的库实现泛型仓储接口来持久化聚合类,之后在抽象库中将对泛型仓储接口提供基础实现,并将对应的实体转化为SQl语句。这样领域层就不需要操作Sql语句即可完成CRUD操作,同时使用工作单元对事务进行统一提交。
1)定义仓储接口,包含基本的CRUD操作及其重载不同的查询
1 public interface IRepository
2 {
3 ///
4 /// 插入对象
5 ///
6 ///
7 int Insert(T entity);
8
9 ///
10 /// 更新对象
11 ///
12 ///
13 ///
14 int Update(T entity, Expression> express);
15
16 ///
17 /// 删除对象
18 ///
19 ///
20 int Delete(Expression> express = null);
21
22 ///
23 /// 查询对象集合
24 ///
25 ///
26 ///
27 List QueryAll(Expression> express = null);
28
29 ///
30 /// 查询对象集合
31 ///
32 ///
33 ///
34 ///
35 ///
36 ///
37 ///
38 List QueryAll(int index,int pagesize,List orderFields, Expression> express = null);
39
40 ///
41 /// 查询对象集合
42 ///
43 ///
44 ///
45 ///
46 List
其次,对仓储接口提供基本实现,这里由于使用了Lambda表达式,所以就需要进行表达式树的解析(这里我希望园友能自己去研究)。
1 public abstract class BaseRepository : IRepository
2 where T:class,new()
3 {
4 private IUnitOfWork unitOfWork;
5
6 private IUnitOfWorkContext context;
7
8 public BaseRepository(IUnitOfWork unitOfWork, IUnitOfWorkContext context)
9 {
10 this.unitOfWork = unitOfWork;
11 this.context = context;
12 }
13
14 Lazy builder = new Lazy();
15
16 public string tableName {
17 get
18 {
19 TableNameAttribute attr= (TableNameAttribute)typeof(T).GetCustomAttribute(typeof(TableNameAttribute));
20 return attr.Name;
21 }
22 }
23
24 ///
25 /// 插入对象
26 ///
27 ///
28 public virtual int Insert(T entity)
29 {
30 Func, int> excute = (propertys, condition, parameters) =>
31 {
32 List names = new List();
33 foreach (PropertyInfo property in propertys)
34 {
35 if (property.GetCustomAttribute(typeof(IncrementAttribute)) == null)
36 {
37 string attrName = property.Name;
38 object value = property.GetValue(entity);
39 names.Add(string.Format("@{0}", attrName));
40 parameters.Add(attrName, value);
41 }
42 }
43 string sql = "Insert into {0} values({1})";
44 string combineSql = string.Format(sql, tableName, string.Join(",", names), builder.Value.Condition);
45 return unitOfWork.Command(combineSql, parameters);
46 };
47 return CreateExcute(null, excute);
48 }
49
50 ///
51 /// 修改对象
52 ///
53 ///
54 ///
55 public virtual int Update(T entity, Expression> express)
56 {
57
58 Func, int> excute = (propertys, condition, parameters) =>
59 {
60 List names = new List();
61 foreach (PropertyInfo property in propertys)
62 {
63 if (property.GetCustomAttribute(typeof(IncrementAttribute)) == null)
64 {
65 string attrName = property.Name;
66 object value = property.GetValue(entity);
67 names.Add(string.Format("{0}=@{1}", attrName, attrName));
68 parameters.Add(attrName, value);
69 }
70 }
71 string sql = "update {0} set {1} where {2}";
72 string combineSql = string.Format(sql, tableName, string.Join(",", names), builder.Value.Condition);
73 return unitOfWork.Command(combineSql, parameters);
74 };
75 return CreateExcute(express, excute);
76 }
77 ///
78 /// 删除对象
79 ///
80 ///
81 public virtual int Delete(Expression> express = null)
82 {
83 Func, int> excute = (propertys, condition, parameters) =>
84 {
85 string sql = "delete from {0} {1}";
86 string combineSql = string.Format(sql, tableName, condition);
87 return unitOfWork.Command(combineSql, parameters);
88 };
89 return CreateExcute(express, excute);
90 }
91
92 ///
93 /// 查询对象集合
94 ///
95 ///
96 ///
97 public virtual List QueryAll(Expression> express = null)
98 {
99 Func, List> excute = (propertys, condition, parameters) =>
100 {
101 string sql = "select {0} from {1} {2}";
102 string combineSql = string.Format(sql, string.Join(",", propertys.Select(x => x.Name)), tableName, condition);
103 return context.ReadValues(combineSql, parameters);
104 };
105 return CreateExcute>(express, excute);
106 }
107
108 ///
109 /// 查询对象集合(分页)
110 ///
111 ///
112 ///
113 ///
114 ///
115 ///
116 ///
117 public virtual List QueryAll(int index,int pagesize,List orderFields,Expression> express = null)
118 {
119 Func, List> excute = (propertys, condition, parameters) =>
120 {
121 if (orderFields == null) { throw new Exception("排序字段不能为空"); }
122 string sql = "select * from (select {0} , ROW_NUMBER() over(order by {1}) as rownum from {2} {3}) as t where t.rownum >= {4} and t.rownum < {5}";
123 string combineSql = string.Format(sql, string.Join(",", propertys.Select(x => x.Name)),string.Join(",", orderFields), tableName, condition, (index - 1) * pagesize + 1, index * pagesize);
124 return context.ReadValues(combineSql, parameters);
125 };
126 return CreateExcute>(express, excute);
127 }
128
129 ///
130 /// 查询对象集合
131 ///
132 ///
133 ///
134 ///
135 public virtual List
接下来,定义工作单元,所有的添加、删除、修改操作都会被储存到工作单元中。
1 public interface IUnitOfWork
2 {
3 ///
4 /// 命令
5 ///
6 ///
7 ///
8 ///
9 int Command(string commandText, IDictionary parameters);
10
11 ///
12 /// 事务的提交状态
13 ///
14 bool IsCommited { get; set; }
15
16 ///
17 /// 提交事务
18 ///
19 ///
20 void Commit();
21
22 ///
23 /// 回滚事务
24 ///
25 void RollBack();
26 }
接下来是对工作单元的实现,其内部维护了一个命令集合,存储Sql语句。
1 public class UnitOfWork:IUnitOfWork
2 {
3 ///
4 /// 注入对象
5 ///
6 private IUnitOfWorkContext context;
7
8 ///
9 /// 维护一个Sql语句的命令列表
10 ///
11 private List commands;
12
13 public UnitOfWork(IUnitOfWorkContext context)
14 {
15 commands = new List();
16 this.context = context;
17 }
18
19 ///
20 /// 增、删、改命令
21 ///
22 ///
23 ///
24 ///
25 public int Command(string commandText, IDictionary parameters)
26 {
27 IsCommited = false;
28 commands.Add(new CommandObject(commandText, parameters));
29 return 1;
30 }
31
32 ///
33 /// 提交状态
34 ///
35 public bool IsCommited{ get; set; }
36
37 ///
38 /// 提交方法
39 ///
40 ///
41 public void Commit()
42 {
43 if (IsCommited) { return ; }
44 using (TransactionScope scope = new TransactionScope())
45 {
46 foreach (var command in commands)
47 {
48 context.ExecuteNonQuery(command.command, command.parameters);
49 }
50 scope.Complete();
51 IsCommited = true;
52 }
53 }
54
55 ///
56 /// 事务回滚
57 ///
58 public void RollBack()
59 {
60 IsCommited = false;
61 }
62 }
最后定义工作单元对事务提交处理的上下文及其实现,同仓储模式中的代码。
1 public interface IUnitOfWorkContext
2 {
3
4 ///
5 /// 注册新对象到上下文
6 ///
7 ///
8 ///
9 int ExecuteNonQuery(string commandText, IDictionary parameters = null);
10
11 ///
12 /// 查询对象集合
13 ///
14 ///
15 ///
16 ///
17 /// 自定义处理
18 ///
19 List ReadValues(string commandText, IDictionary parameters = null, Func load = null) where T : class, new();
20
21 ///
22 /// 查询对象集合
23 ///
24 ///
25 ///
26 ///
27 ///
28 ///
29 List ReadValues(string commandText, Type type, IDictionary parameters = null, Action setItem = null);
30
31 ///
32 /// 查询对象
33 ///
34 ///
35 ///
36 ///
37 ///
38 ///
39 T ExecuteReader(string commandText, IDictionary parameters = null, Func load = null) where T : class,new();
40
41 ///
42 /// 查询数量
43 ///
44 ///
45 ///
46 ///
47 object ExecuteScalar(string commandText, IDictionary parameters = null);
48 }
最后实现。
1 public abstract class UnitOfWorkContext : IUnitOfWorkContext,IDisposable
2 {
3 ///
4 /// 数据库连接字符串标识
5 ///
6 public abstract string Key { get; }
7
8 private SqlConnection connection;
9
10 private SqlConnection Connection
11 {
12 get
13 {
14 if (connection == null)
15 {
16 ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings[Key];
17 connection = new SqlConnection(settings.ConnectionString);
18 }
19 return connection;
20 }
21 }
22
23 ///
24 /// 注册新对象到事务
25 ///
26 ///
27 ///
28 public int ExecuteNonQuery(string commandText, IDictionary parameters = null)
29 {
30 Func excute = (commend) =>
31 {
32 return commend.ExecuteNonQuery();
33 };
34 return CreateDbCommondAndExcute(commandText, parameters, excute);
35 }
36
37 ///
38 /// 查询对象集合
39 ///
40 ///
41 ///
42 ///
43 /// 自定义处理
44 /// 泛型实体集合
45
46 public List ReadValues(string commandText, IDictionary parameters = null, Func load = null) where T : class,new()
47 {
48 Func> excute = (dbCommand) =>
49 {
50 List result = new List();
51 using (IDataReader reader = dbCommand.ExecuteReader())
52 {
53 while (reader.Read())
54 {
55 if (load == null)
56 {
57 load = (s) => { return s.GetReaderData(); };
58 }
59 var item = load(reader);
60 result.Add(item);
61 }
62 return result;
63 }
64 };
65 return CreateDbCommondAndExcute(commandText, parameters, excute);
66 }
67
68 ///
69 /// 查询对象集合
70 ///
71 ///
72 ///
73 ///
74 ///
75 public List ReadValues(string commandText, Type type, IDictionary parameters = null, Action setItem = null)
76 {
77 Func> excute = (dbCommand) =>
78 {
79 var result = new List();
80
81 using (IDataReader dataReader = dbCommand.ExecuteReader())
82 {
83 while (dataReader.Read())
84 {
85 var item = dataReader.GetReaderData(type);
86 if (setItem != null)
87 {
88 setItem(item);
89 }
90 result.Add(item);
91 }
92 }
93 return result;
94 };
95 return CreateDbCommondAndExcute>(commandText, parameters,
96 excute);
97 }
98
99 ///
100 /// 查询对象
101 ///
102 ///
103 ///
104 ///
105 ///
106 ///
107 public T ExecuteReader(string commandText, IDictionary parameters = null, Func load = null) where T : class,new()
108 {
109 Func excute = (dbCommand) =>
110 {
111 var result = default(T);
112 using (IDataReader reader = dbCommand.ExecuteReader())
113 {
114 while (reader.Read())
115 {
116 if (load == null)
117 {
118 load = (s) => { return s.GetReaderData(); };
119 }
120 result = load(reader);
121 }
122 return result;
123 }
124 };
125 return CreateDbCommondAndExcute(commandText, parameters, excute);
126 }
127
128 ///
129 /// 查询数量
130 ///
131 ///
132 ///
133 ///
134 public object ExecuteScalar(string commandText, IDictionary parameters = null)
135 {
136 Func excute = (dbCommand) =>
137 {
138 return dbCommand.ExecuteScalar();
139 };
140 return CreateDbCommondAndExcute(commandText, parameters, excute);
141 }
142
143 ///
144 /// 创建命令并执行
145 ///
146 ///
147 ///
148 ///
149 ///
150 ///
151 private TValue CreateDbCommondAndExcute(string commandText,
152 IDictionary parameters, Func excute)
153 {
154 if (Connection.State == ConnectionState.Closed) { Connection.Open(); };
155 using (SqlCommand command = new SqlCommand())
156 {
157 command.CommandType = CommandType.Text;
158 command.CommandText = commandText;;
159 command.Connection = Connection;
160 command.SetParameters(parameters);
161 return excute(command);
162 }
163 }
164
165 ///
166 /// 关闭连接
167 ///
168 public void Dispose()
169 {
170 if (connection != null)
171 {
172 Connection.Dispose();//非托管资源
173 }
174 }
175 }
在调用方法时需要注意,一个事务涉及多个聚合时,需要保证传递同一工作单元,并在方法的最后调用Commit() 方法。
1 public class Services : IService
2 {
3 private IMemberRespository member;
4
5 private IUnitOfWork unitOfWork;
6
7 public Services(IMemberRespository member, IUnitOfWork unitOfWork)
8 {
9 this.member = member;
10 this.unitOfWork = unitOfWork;
11 }
12
13 ///
14 /// 测试用例
15 ///
16 public void Demo()
17 {
18
19 member.Test();
20
21 unitOfWork.Commit();
22 }
23 }
该实现中并未实现对多表进行的联合查询,使用Lambda的方式去多表查询,有点自己写一个ORM的性质,由于Lz能力有限,顾有需求的园友可以自行扩展或者使用ORM,若有实现自行扩展的园友,望指教。
至此,既实现对数据访问层和领域层解耦,如果园友对我的比较认可,欢迎尝试去使用,在使用中遇到什么问题或有什么好的意见,也希望及时反馈给我。若某些园友不太认可我的设计,也希望批评指出。
源码网盘地址:链接:http://pan.baidu.com/s/1hqXJ3GK 密码:o0he