ORM映射框架总结--SQL 语句生成组件
2010-04-16 15:45 贺臣 阅读(7886) 评论(23) 编辑 收藏 举报之前说过ORM框架中有一个非常重要的组件,那就是SQL语句生成组件。SQL语句生成组件,顾名思义就是用于生成sql语句的一个组件。之前的Attribute 是用于修饰实体信息,而上一章讲的实体分析器分析了实体信息,在很大一部分程度上,以上做工作就是为这个SQL语句生成组件服务的。
该组件的核心接口是IDbFactory,它实现了接口IDisposable
目前该ORM支持SQL Server 数据库的sql语句生成,在后期过程中会逐步实现对Oracle,Mysql,Access 等数据库的支持
下面是该接口定义的方法图表:
2. 核心接口
核心接口定义源码
2 * 2010-2-2
3 *
4 * 情 缘
5 *
6 * IDbFactory 接口实现了IDisposable 接口,实现该
7 * 接口的类可以通过IDisposable 接口来释放对象占用
8 * 的内存。该接口的主要作用适用于根据实体对象来创
9 * 建SQL Server 数据库脚本。ORM 的主要作用是为了
10 * 实现对象化操作数据库,而此操作就是为了实现对象
11 * 化操作和数据库语句操作的过渡
12 *
13 * */
14 using System;
15 using System.Collections.Generic;
16 using System.Linq;
17 using System.Text;
18 using CommonData.Entity;
19 using System.Data;
20 using CommonData.Model.Core;
21
22 namespace CommonData.Data.Core
23 {
24 public interface IDbFactory : IDisposable
25 {
26 /// <summary>
27 /// 根据实体对象公共接口创建插入的sql语句
28 /// </summary>
29 /// <param name="entity">实体公共接口</param>
30 /// <param name="param">创建sql语句对应占位符参数</param>
31 /// <returns></returns>
32 string CreateInsertSql(IEntity entity, out IDataParameter[] param);
33
34 /// <summary>
35 /// 根据实体类型创建插入sql语句
36 /// </summary>
37 /// <param name="type">实体类型</param>
38 /// <param name="value">实体对象</param>
39 /// <param name="param">创建sql语句对应占位符参数</param>
40 /// <returns></returns>
41 string CreateInsertSql(Type type, object value, out IDataParameter[] param);
42
43 /// <summary>
44 /// 根据泛型类型创建插入sql语句
45 /// </summary>
46 /// <typeparam name="T">泛型类型</typeparam>
47 /// <param name="t">泛型实体类</param>
48 /// <param name="param">创建sql语句对应占位符参数</param>
49 /// <returns></returns>
50 string CreateInsertSql<T>(T t, out IDataParameter[] param) where T : IEntity;
51
52 /// <summary>
53 /// 根据实体对象公共接口创建修改的的sql语句
54 /// 该sql语句是根据主键列修改的
55 /// </summary>
56 /// <param name="entity">实体公共接口</param>
57 /// <param name="param">创建sql语句对应占位符参数</param>
58 /// <returns></returns>
59 string CreateUpdateSql(IEntity entity, out IDataParameter[] param);
60
61 /// <summary>
62 /// 根据实体对象类型创建修改的的sql语句
63 /// </summary>
64 /// <param name="type">实体类型</param>
65 /// <param name="value">实体对象</param>
66 /// <param name="param">创建sql语句对应占位符参数</param>
67 /// <returns></returns>
68 string CreateUpdateSql(Type type, object value, out IDataParameter[] param);
69
70 /// <summary>
71 /// 根据实体对象公共接口创建修改的的sql语句
72 /// 该sql语句是根据一个特定的属性作为修改条件的
73 /// </summary>
74 /// <param name="entity">实体公共接口</param>
75 /// <param name="param">创建sql语句对应占位符参数</param>
76 /// <param name="propertyName">属性名称</param>
77 /// <returns></returns>
78 string CreateUpdateSql(IEntity entity, out IDataParameter[] param, string propertyName);
79
80 /// <summary>
81 /// 根据实体对象类型创建修改的的sql语句
82 /// 该sql语句是根据一个特定的属性作为修改条件的
83 /// </summary>
84 /// <param name="type">实体类型</param>
85 /// <param name="value">实体对象</param>
86 /// <param name="param">创建sql语句对应占位符参数</param>
87 /// <param name="propertyName">属性名称</param>
88 /// <returns></returns>
89 string CreateUpdateSql(Type type, object value, out IDataParameter[] param, string propertyName);
90
91 /// <summary>
92 /// 根据实体对象公共接口创建修改的的sql语句
93 /// 该sql语句是根据多个特定的属性作为修改条件的
94 /// </summary>
95 /// <param name="entity">实体公共接口</param>
96 /// <param name="param">创建sql语句对应占位符参数</param>
97 /// <param name="propertyNames">属性名称</param>
98 /// <returns></returns>
99 string CreateUpdateSql(IEntity entity, out IDataParameter[] param, string[] propertyNames);
100
101 /// <summary>
102 /// 根据实体对象类型创建修改的的sql语句
103 /// 该sql语句是根据多个特定的属性作为修改条件的
104 /// </summary>
105 /// <param name="type">实体类型</param>
106 /// <param name="value">实体对象</param>
107 /// <param name="param">创建sql语句对应占位符参数</param>
108 /// <param name="propertyNames">属性名称</param>
109 /// <returns></returns>
110 string CreateUpdateSql(Type type, object value, out IDataParameter[] param, string[] propertyNames);
111
112 /// <summary>
113 /// 根据实体对象公共接口创建修改的的sql语句
114 /// 该sql语句是根据查询组建创建的
115 /// </summary>
116 /// <param name="entity">实体公共接口</param>
117 /// <param name="param">创建sql语句对应占位符参数</param>
118 /// <param name="component">查询条件组件</param>
119 /// <returns></returns>
120 string CreateUpdateSql(IEntity entity, out IDataParameter[] param, ConditionComponent component);
121
122 /// <summary>
123 /// 根据实体对象公共接口创建删除sql语句
124 /// 该sql语句是根据实体主键删除
125 /// </summary>
126 /// <param name="entity">实体公共接口</param>
127 /// <param name="param">创建sql语句对应占位符参数</param>
128 /// <returns></returns>
129 string CreateDeleteSql(IEntity entity, out IDataParameter[] param);
130
131 /// <summary>
132 /// 根据实体对象类型创建删除sql语句
133 /// 该sql语句是根据实体主键删除
134 /// </summary>
135 /// <param name="type">实体类型</param>
136 /// <param name="value">实体对象</param>
137 /// <param name="param">创建sql语句对应占位符参数</param>
138 /// <returns></returns>
139 string CreateDeleteSql(Type type,object value,out IDataParameter[] param);
140
141 /// <summary>
142 /// 根据实体对象公共接口的某个属性创建删除sql语句
143 /// 该sql语句是根据实体属性删除
144 /// </summary>
145 /// <param name="entity">实体公共接口</param>
146 /// <param name="param">创建sql语句对应占位符参数</param>
147 /// <param name="propertyName">实体属性名称</param>
148 /// <returns></returns>
149 string CreateDeleteSql(IEntity entity, out IDataParameter[] param, string propertyName);
150
151 /// <summary>
152 /// 根据实体对象类型的某个属性创建删除sql语句
153 /// 该sql语句是根据实体属性删除
154 /// </summary>
155 /// <param name="type">实体类型</param>
156 /// <param name="value">实体对象</param>
157 /// <param name="param">创建sql语句对应占位符参数</param>
158 /// <param name="propertyName">实体属性名称</param>
159 /// <returns></returns>
160 string CreateDeleteSql(Type type, object value, out IDataParameter[] param, string propertyName);
161
162 /// <summary>
163 /// 根据实体对象公共接口的多个属性创建删除sql语句
164 /// 该sql语句是根据实体多个属性删除
165 /// </summary>
166 /// <param name="entity">实体公共接口</param>
167 /// <param name="param">创建sql语句对应占位符参数</param>
168 /// <param name="propertyName">实体属性名称数组</param>
169 /// <returns></returns>
170 string CreateDeleteSql(IEntity entity, out IDataParameter[] param, string[] propertyNames);
171
172 /// <summary>
173 /// 根据实体对象类型的多个属性创建删除sql语句
174 /// 该sql语句是根据实体多个属性删除
175 /// </summary>
176 /// <param name="type">实体了姓</param>
177 /// <param name="value">实体对象</param>
178 /// <param name="param">创建sql语句对应占位符参数</param>
179 /// <param name="propertyNames">实体属性名称数组</param>
180 /// <returns></returns>
181 string CreateDeleteSql(Type type, object value, out IDataParameter[] param, string[] propertyNames);
182
183 /// <summary>
184 /// 根据实体对象公共接口的多个属性创建删除sql语句
185 /// 该sql语句使根据查询组建来创建的
186 /// </summary>
187 /// <param name="entity">实体公共接口</param>
188 /// <param name="param">创建sql语句对应占位符参数</param>
189 /// <param name="component">实体属性名称数组</param>
190 /// <returns></returns>
191 string CreateDeleteSql(IEntity entity, out IDataParameter[] param, ConditionComponent component);
192
193 /// <summary>
194 /// 根据实体的公共接口创建查询单行数据的sql语句
195 /// 该sql语句是根据数据库表的主键来查询的
196 /// </summary>
197 /// <param name="entity">实体公共接口</param>
198 /// <param name="param">创建sql语句对应占位符参数</param>
199 /// <returns></returns>
200 string CreateSingleSql(IEntity entity, out IDataParameter[] param);
201
202 /// <summary>
203 /// 根据实体的公共接口创建查询单行数据的sql语句
204 /// 该sql语句是根据实体的相应属性来查询
205 /// </summary>
206 /// <param name="entity">实体公共接口</param>
207 /// <param name="param">创建sql语句对应占位符参数</param>
208 /// <returns></returns>
209 string CreateSingleSql(IEntity entity, out IDataParameter[] param, string[] propertyNames);
210
211 /// <summary>
212 /// 根据实体类型创建查询单行数据的sql语句
213 /// 该sql语句是根据实体的相应属性来查询
214 /// </summary>
215 /// <param name="type">实体类型</param>
216 /// <param name="value">实体对象</param>
217 /// <param name="param">创建sql语句对应占位符参数</param>
218 /// <param name="propertyNames">属性名称数组</param>
219 /// <returns></returns>
220 string CreateSingleSql(Type type, object value, out IDataParameter[] param, string[] propertyNames);
221
222 /// <summary>
223 /// 根据实体的类型创建查询sql语句
224 /// </summary>
225 /// <param name="entityType">实体类型</param>
226 /// <returns></returns>
227 string CreateSingleSql(Type entityType);
228
229 /// <summary>
230 /// 根据实体的类型创建查询sql语句,
231 /// 该方法指定主键值
232 /// </summary>
233 /// <param name="type">实体类型</param>
234 /// <param name="pkPropertyValue">主键值</param>
235 /// <param name="param">创建sql语句对应占位符参数</param>
236 /// <returns></returns>
237 string CreateSingleSql(Type type, object pkPropertyValue,out IDataParameter[] param);
238
239 /// <summary>
240 /// 根据实体的类型创建查询该实体对象对应数据库表的所有数据的sql语句
241 /// 该sql语句用于查询所有数据,并转换为相应List<T> 集合
242 /// </summary>
243 /// <param name="type">实体的类型</param>
244 /// <returns></returns>
245 string CreateQuerySql(Type type);
246
247 /// <summary>
248 /// 根据实体的某个属性创建根据该属性字段查询数据的sql语句
249 /// 该sql语句是使用参数中属性对应字段作为条件查询的
250 /// </summary>
251 /// <param name="type">实体类型</param>
252 /// <param name="propertyName">属性名称</param>
253 /// <param name="value">属性值</param>
254 /// <param name="param">sql语句占位符参数</param>
255 /// <returns></returns>
256 string CreateQueryByPropertySql(Type type, string propertyName, object value, out IDataParameter[] param);
257
258 /// <summary>
259 /// 根据实体的某些属性创建根据该些属性字段查询数据的sql语句
260 /// 该sql语句是使用参数中属性对应字段作为条件查询的,并且该
261 /// 属性集合都是根据and条件组装的
262 /// </summary>
263 /// <param name="type">实体类型</param>
264 /// <param name="dic">属性-值集合</param>
265 /// <param name="param">sql语句占位符参数</param>
266 /// <returns></returns>
267 string CreateQueryByPropertySql(Type type, IDictionary<string, object> dic, out IDataParameter[] param);
268
269 /// <summary>
270 /// 根据实体的某些属性创建根据该些属性字段查询数据的sql语句
271 /// 该sql语句是使用参数中属性对应字段作为条件查询的,并且查
272 /// 询是根据查询组建来创建
273 /// </summary>
274 /// <param name="type">实体类型</param>
275 /// <param name="dic">属性-值集合</param>
276 /// <param name="param">sql语句占位符参数</param>
277 /// <param name="component">查询组建</param>
278 /// <returns></returns>
279 string CreateQueryByPropertySql(Type type, IDictionary<string, object> dic, out IDataParameter[] param, ConditionComponent component);
280
281 /// <summary>
282 /// 根据实体类型来创建该实体对应数据库表的聚合函数查询sql语句
283 /// 该方法创建的sql语句主要是用于查询数据行数
284 /// </summary>
285 /// <param name="type">实体类型</param>
286 /// <param name="converage">聚合函数枚举类型</param>
287 /// <returns></returns>
288 string CreateConverageSql(Type type, Converage converage);
289
290 /// <summary>
291 /// 根据实体类型来创建该实体对应数据库表的聚合函数查询sql语句
292 /// 该方法创建的sql语句主要是用于统计查询(最大值,最小值,求和,平均值,数据行数)
293 /// </summary>
294 /// <param name="type">实体类型</param>
295 /// <param name="converage">聚合函数枚举类型</param>
296 /// <param name="propertyName">聚合函数作用的属性名称</param>
297 /// <returns></returns>
298 string CreateConverageSql(Type type, Converage converage, string propertyName);
299
300 /// <summary>
301 /// 根据实体类型来创建该实体对应数据库表的聚合函数查询sql语句
302 /// 该方法创建的sql语句主要是用于统计查询(最大值,最小值,求和,平均值,数据行数),
303 /// 同时该sql是有条件查询的
304 /// </summary>
305 /// <param name="type">实体类型</param>
306 /// <param name="converage">聚合函数枚举类型</param>
307 /// <param name="propertyName">聚合函数作用的属性名称</param>
308 /// <param name="dic">查询条件属性键值</param>
309 /// <param name="component">查询条件组建对象</param>
310 /// <returns></returns>
311 string CreateConverageSql(Type type, Converage converage, string propertyName, IDictionary<string, object> dic, out IDataParameter[] param, ConditionComponent component);
312
313 /// <summary>
314 /// 根据占位符名称创建参数
315 /// </summary>
316 /// <param name="name">占位符名称</param>
317 /// <returns></returns>
318 IDataParameter CreateParameter(string name);
319
320 /// <summary>
321 /// 根据占位符和值创建参数
322 /// </summary>
323 /// <param name="name">占位符名称</param>
324 /// <param name="value">占位符的值</param>
325 /// <returns></returns>
326 IDataParameter CreateParameter(string name, object value);
327
328 /// <summary>
329 /// 根据占位符名称,类型和值创建参数
330 /// </summary>
331 /// <param name="name">占位符名称</param>
332 /// <param name="type">参数的类型</param>
333 /// <param name="value">参数的值</param>
334 /// <returns></returns>
335 IDataParameter CreateParameter(string name, DataType type, object value);
336
337 /// <summary>
338 /// 根据占位符的名称,类型和大小创建参数
339 /// </summary>
340 /// <param name="name">占位符名称</param>
341 /// <param name="type">参数类型</param>
342 /// <param name="size">参数值大小</param>
343 /// <returns></returns>
344 IDataParameter CreateParameter(string name, DataType type, int size);
345
346 /// <summary>
347 /// 根据占位符的名称,类型,大小和值创建参数
348 /// </summary>
349 /// <param name="name">占位符名称</param>
350 /// <param name="type">参数类型</param>
351 /// <param name="size">参数大小</param>
352 /// <param name="value">参数值</param>
353 /// <returns></returns>
354 IDataParameter CreateParameter(string name, DataType type, int size, object value);
355
356 /// <summary>
357 /// 根据占位符名称和类型创建参数
358 /// </summary>
359 /// <param name="name">占位符名称</param>
360 /// <param name="type">占位符类型</param>
361 /// <returns></returns>
362 IDataParameter CreateParameter(string name, DataType type);
363 }
364 }
365
该接口定了生成增删改查等sql语句的方法
CreateInsertSql() 方法主要用于生成插入语句,该方法被重载了3次,根据各种不同的情况来生成数据库插入sql语句。
CreateUpdateSql() 方法主要用于生成修改语句,该方法被重载了7次,根据各种不同的情况来生成数据的修改sql语句。
CreateDeleteSql() 方法主要用于生成删除语句,该方法同样被重载了7次,根据各种不同的情况来生成数据库的删除sql语句。
CreateSingleSql() 方法主要用于生成查询单个实体的sql语句,该方法被重载了5次,根据各种不同的情况来生成数据库的单数据行sql语句。
CreateQuerySql() 方法主要用于生成查询集合的sql语句,该方法只被重载了一次,从上面的源码中可以看出,该方法只有一个Type类型参数,此方法不做正在意义上查询使用,此方法在配合级联查询的时候能够起到重要的作用。
CreateQueryByPropertySql() 方法主要生成条件查询的sql语句,该方法被充值了3次,根据不同的情况来生成数据的集合查询的sql语句。注意该方法中引入了一个新的对象ConditionComponent,在后面的说明中对次类进行讲解。
CreateConverageSql() 方法主要用于生成查询聚合函数的sql语句,该方法同样被重载了3次,根据不同的情况来生成查询聚合函数的sql语句。该方法同样也引入了一个新的对象Converage,在后面的说明中对次类进行讲解。
CreateParameter() 方法主要用于生成sql语句占位符参数的,该方法被重载了6次。
以上是该核心接口定义的几组方法,用于生成sql语句。在程序设计的过程中,都采用的面向接口来编程,目前来说只支持sql server 数据的sql语句生成,但是考虑到后期的补充,觉得在使用接口编程就尤为重要。后期的工作就是根据不同的数据库去实现该接口中的所有方法。
3. 插入sql语句的生成
上面说到了插入sql语句被重载了3次,在很多情况下我们根据情况的不同生成sql语句所需的方式也不同,于是归纳成了三个重载方法,该三个重载放一般情况下是可以覆盖所有的生成插入语句的情况。
string CreateInsertSql(IEntity entity, out
IDataParameter[] param);
在上面的桥梁映射过程中,我们提到了一个接口IEntity,它是所有实体的父级接口,该接口并未定义任何方法,或许当时有人认为这是多此一举,不过现在大家应该明白了,这样定义是有缘由的。因为这个插入语句的生成要覆盖所有的实体类,于是我们必须抽象出来,抽象它们共同拥有的特性。在这里,这个IEntity 接口的重要性显得尤为突出。Param参数这是用于存储sql语句的占位符的,看到这个参数的修饰符out,说明参数传入本身是空的,也就是说在后期的实现过程中我们要动态的去给param赋值,参数名,参数值,以及数组的长度都是由entity 来决定。
string CreateInsertSql(Type type, object
value, out IDataParameter[]
param);
说到重载,这是面向对象编程的四要素之一,多态表现形式之一,另外一种是重写。重载其实要实现的功能是一样,只是表现形式不同而已,这里使用了Type,这里就是要插入的实体的类型,后面的object value 就是实体,紧接着的参数就不用多说了,上面的方法已经提到过。
string
CreateInsertSql<T>(T t, out IDataParameter[] param) where
T : IEntity
说到这个重载方法,大家一般都会很兴奋,为什么.我第一眼看到了<T> ,.net中的泛型。.net中的泛型我可以说真的是无懈可击,那各种形态的使用方式是如此的婀娜多姿,总是让程序员沉迷于其中的美妙,最起码我是这样的。后面的where T : IEntity 就是泛型约束了,泛型约束可以让我们的程序减少很多不必要的麻烦。因为泛型本身就是一种不确定的类型,我们规定了他使用的范围,这样能够减少它出错的可能性。
下面看看这个插入sql的生成核心代码:
2 /// 根据实体类型创建插入sql语句
3 /// </summary>
4 /// <param name="type">实体类型</param>
5 /// <param name="value">实体对象</param>
6 /// <param name="param">创建sql语句对应占位符参数</param>
7 /// <returns></returns>
8 public string CreateInsertSql(Type type, object value, out IDataParameter[] param)
9 {
10 if (value == null)
11 {
12 throw new NullReferenceException("the save entity is null");
13 }
14 StringBuilder sbColumn = new StringBuilder("");
15 StringBuilder sbValues = new StringBuilder("");
16 IDictionary<string, ColumnAttribute> dicColumn = EntityTypeCache.GetTableInfo(type).DicColumns;
17 if (dicColumn.Keys.Count > 0)
18 {
19 sbColumn.AppendFormat("insert into {0} (", EntityTypeCache.GetTableInfo(type).Table.Name);
20 sbValues.AppendFormat(" values (");
21 IList<IDataParameter> listParams = new List<IDataParameter>();
22 foreach (string key in dicColumn.Keys)
23 {
24 if (!dicColumn[key].AutoIncrement)
25 {
26 sbColumn.AppendFormat("{0},", dicColumn[key].Name);
27 sbValues.AppendFormat("{0},", "@" + dicColumn[key].Name);
28 }
29 if (EntityFactory.GetPropertyValue(value, key) == null)
30 {
31 listParams.Add(CreateParameter("@" + key, System.DBNull.Value));
32 }
33 else
34 {
35 listParams.Add(CreateParameter("@" + key, EntityFactory.GetPropertyValue(value, key)));
36 }
37 }
38 sbColumn.Replace(",", ")", sbColumn.Length - 1, 1);
39 sbValues.Replace(",", ")", sbValues.Length - 1, 1);
40 param = listParams.ToArray();
41 return sbColumn.ToString() + sbValues.ToString();
42 }
43 else
44 {
45 param = null;
46 return null;
47 }
48 }
当大家看到这里肯定也觉得,原理这个东西也不过如此。如果真是这样,那我也就替你高兴了,因为我要传达的意思你已经明白了,也就是说我写这边博文的传达的信息你收到了。一次完美的会话完成。
StringBuilder sbColumn = new StringBuilder("");
StringBuilder
sbValues = new StringBuilder("");
代码中定义了这两个类,不使用string的原因大家也知道。还有这里我们生成sql语句的规则就是sql语句插入的语法规则,这里不再多说。
4. 修改sql语句
生成修改sql语句的方法CreateUpdateSql()被重载了七次,其实生成sql语句的原理和上面生成插入sql语句的原理是一样的。关键在于如何去抽象一个多情况下处理方式。
string CreateUpdateSql(IEntity entity, out
IDataParameter[] param);
这个方法主要是根据实体的主键作为条件来修改
string CreateUpdateSql(IEntity entity, out
IDataParameter[] param, string[] propertyNames);
这个方法则是根据实体的过个属性来修改,各个条件以and方式来连接生成sql语句。在生成sql语句的过程中IEntity 必须包含这些数据,而且这些属性值必须存在,否则会抛出异常。与此类似的方法还有只有一个属性名来修改的,其实这个方法可以包括只有一个属性的修改方法,而这里重载一次就是为了使用方便。
string CreateUpdateSql(IEntity entity, out
IDataParameter[] param, ConditionComponent component);
这里我们不介绍其他东西了,主要看看ConditionComponent这个类。上面提到过如果多条件修改sql语句,各个属性都是通过and来连接的,如果我们修改使用 or 或者其他的方式怎么办,ConditionComponent可以用来解决此问题。
首先看看这个类的源码:
2 * 2010-3-1
3 *
4 * 情 缘
5 *
6 * 该类封装修改数据,条件查询数据的相关条件。
7 *
8 * */
9 using System;
10 using System.Collections.Generic;
11 using System.Linq;
12 using System.Text;
13
14 namespace CommonData.Model.Core
15 {
16 public class ConditionComponent
17 {
18 private IDictionary<string, SearchComponent> dicComponent = null;
19
20 /// <summary>
21 /// 用于存储属性查询类型
22 /// </summary>
23 public IDictionary<string, SearchComponent> DicComponent
24 {
25 get { return dicComponent; }
26 set { dicComponent = value; }
27 }
28
29 private static ConditionComponent component;
30
31 /// <summary>
32 /// 私有构造方法,禁止外部类构造此类的实例
33 /// 使用私有构造方式主要实现单例模式
34 /// </summary>
35 private ConditionComponent()
36 {
37
38 }
39
40 /// <summary>
41 /// 构造ConditionComponent的实例,当实例不存在是则创建该对象
42 /// 这个是单例模式的实现
43 /// </summary>
44 /// <returns></returns>
45 public static ConditionComponent Instance()
46 {
47 if (component == null)
48 {
49 component = new ConditionComponent();
50 component.DicComponent = new Dictionary<string, SearchComponent>();
51 }
52 return component;
53 }
54
55 /// <summary>
56 /// 添加属性查询类型
57 /// </summary>
58 /// <param name="propertyName">属性名称</param>
59 /// <param name="component">查询类型</param>
60 /// <returns></returns>
61 public ConditionComponent AddComponent(string propertyName,SearchComponent component)
62 {
63 if (component == null)
64 {
65 ConditionComponent.component = Instance();
66 }
67 ConditionComponent.component.DicComponent.Add(propertyName, component);
68 return ConditionComponent.component;
69 }
70
71 }
72 }
73
这个类其实就相当于一个存储器。
private IDictionary<string,
SearchComponent> dicComponent = null;
而dicComponent就是用于存储相应的数据的,而且是以键值对的方式存储,这样便于后面使用过程中的查找。而这里又出现了一个心得类SearchComponent,这个是查询条件组建。下面在讲解。这里定义了这个类的一个单例模式,也就是说只能允许一个对象的存在每次。我需要在每次天际参数的同时这个对象仍然存在,并且保存信息。
public ConditionComponent
AddComponent(string propertyName,SearchComponent component)
{
if
(component == null)
{
ConditionComponent.component
= Instance();
}
ConditionComponent.component.DicComponent.Add(propertyName,
component);
return
ConditionComponent.component;
}
这个方法每次给ConditionComponent添加一个参数,然后又返回这个对象。
下面看看上面的提到的SearchComponent源码:
2 * 2010-3-1
3 *
4 * 情 缘
5 *
6 * 该类是一个枚举类型,定义了元素查询条件。
7 * 该枚举值都一一对应数据库中的各种查询条
8 * 件。
9 *
10 * */
11 using System;
12 using System.Collections.Generic;
13 using System.Linq;
14 using System.Text;
15
16 namespace CommonData.Model.Core
17 {
18 public enum SearchComponent
19 {
20 /// <summary>
21 /// 对应数据库中的 "="
22 /// </summary>
23 Equals,
24
25 /// <summary>
26 /// 对应数据库中的 "!="
27 /// </summary>
28 UnEquals,
29
30 /// <summary>
31 /// 对应数据库中的 ">"
32 /// </summary>
33 Greater,
34
35 /// <summary>
36 /// 对应数据库中的 ">="
37 /// </summary>
38 GreaterOrEquals,
39
40 /// <summary>
41 /// 对应数据库中的 "<"
42 /// </summary>
43 Less,
44
45 /// <summary>
46 /// 对应数据库中的 "<="
47 /// </summary>
48 LessOrEquals,
49
50 /// <summary>
51 /// 对应数据库中的 "like"
52 /// </summary>
53 Like,
54
55 /// <summary>
56 /// 对应数据库中的 "in"
57 /// </summary>
58 In,
59
60 /// <summary>
61 /// 对应数据库中的 "between and"
62 /// </summary>
63 Between,
64
65 /// <summary>
66 /// 对应数据库中的 "order by asc"
67 /// </summary>
68 OrderAsc,
69
70 /// <summary>
71 /// 对应数据库中的 "order by desc"
72 /// </summary>
73 OrderDesc,
74
75 /// <summary>
76 /// 对应数据库中的 "group by"
77 /// </summary>
78 GroupBy,
79
80 /// <summary>
81 /// 对应数据库中的 "or"
82 /// </summary>
83 Or
84 }
85 }
86
这里的代码大家一看也就明白了,为什么叫查询组件了,它其实就是封装了一些查询可能出现的情况。代码非常简单,这里不再过多讲解。
下面是生成修改语句的方法实现,这个方法的代码包含了查询组件的运用:
2 /// 根据实体对象公共接口创建修改的的sql语句
3 /// 该sql语句是根据查询组建创建的
4 /// </summary>
5 /// <param name="entity">实体公共接口</param>
6 /// <param name="param">创建sql语句对应占位符参数</param>
7 /// <param name="component">查询条件组件</param>
8 /// <returns></returns>
9 public string CreateUpdateSql(IEntity entity, out IDataParameter[] param, ConditionComponent component)
10 {
11
12 StringBuilder sbColumn = new StringBuilder("");
13 StringBuilder sbValues = new StringBuilder("");
14 IList<IDataParameter> listParams = new List<IDataParameter>();
15 sbColumn.AppendFormat("update {0} set ", EntityTypeCache.GetTableInfo(entity).Table.Name);
16 sbValues.Append(" where 1=1 ");
17 foreach (string propertyName in EntityTypeCache.GetTableInfo(entity).DicProperties.Keys)
18 {
19 //包含则作为条件
20 if (component.DicComponent.Keys.Contains(propertyName))
21 {
22 switch (component.DicComponent[propertyName])
23 {
24 case SearchComponent.Equals:
25 sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name, "=", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name);
26 break;
27 case SearchComponent.UnEquals:
28 sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name, "!=", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name);
29 break;
30 case SearchComponent.Between:
31 break;
32 case SearchComponent.Greater:
33 sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name, ">", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name);
34 break;
35 case SearchComponent.GreaterOrEquals:
36 sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name, ">=", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name);
37 break;
38 case SearchComponent.GroupBy:
39 break;
40 case SearchComponent.In:
41 break;
42 case SearchComponent.Less:
43 sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name, "<", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name);
44 break;
45 case SearchComponent.LessOrEquals:
46 sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name, "<=", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name);
47 break;
48 case SearchComponent.Like:
49 break;
50 case SearchComponent.Or:
51 break;
52 case SearchComponent.OrderAsc:
53 break;
54 case SearchComponent.OrderDesc:
55 break;
56 }
57 listParams.Add(CreateParameter("@" + EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name, EntityFactory.GetPropertyValue(entity, propertyName) == null ? DBNull.Value : EntityFactory.GetPropertyValue(entity, propertyName)));
58 }
59 else //判断主键和唯一列,主键和唯一列不能被修改
60 {
61 if (EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].IsPrimaryKey ||
62 EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].IsUnique ||
63 EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].AutoIncrement)
64 {
65
66 }
67 else
68 {
69 sbColumn.AppendFormat("{0}=@{1},", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name, EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name);
70 listParams.Add(CreateParameter("@" + EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name, EntityFactory.GetPropertyValue(entity, propertyName) == null ? DBNull.Value : EntityFactory.GetPropertyValue(entity, propertyName)));
71 }
72 }
73 }
74 sbColumn.Remove(sbColumn.Length - 1, 1);
75 param = listParams.ToArray();
76 return sbColumn.ToString() + sbValues.ToString();
77 }
5. 聚合函数的使用
这里不做过多的降解了,都是前篇一律的,不过有个特殊的地方,那就是刚才提到过来的Converage。
先看看这个枚举的源码:
2 * 2010-2-26
3 *
4 * 情 缘
5 *
6 * 该类是一个枚举类型,定义了数据库聚合函数
7 * 操作的各种情况。该枚举值可以在使用时候来
8 * 区分sql执行那个聚合函数
9 *
10 * */
11 using System;
12 using System.Collections.Generic;
13 using System.Linq;
14 using System.Text;
15
16 namespace CommonData.Model.Core
17 {
18 public enum Converage
19 {
20 /// <summary>
21 /// 聚合函数取最小值
22 /// </summary>
23 Min,
24
25 /// <summary>
26 /// 聚合函数取最大值
27 /// </summary>
28 Max,
29
30 /// <summary>
31 /// 聚合函数取和
32 /// </summary>
33 Sum,
34
35 /// <summary>
36 /// 聚合函数取所有数据行
37 /// </summary>
38 Count,
39
40 /// <summary>
41 /// 聚合函数取所有非空数据行
42 /// </summary>
43 CountNotNll,
44
45 /// <summary>
46 /// 聚合函数取平均值
47 /// </summary>
48 Avg,
49 }
50 }
51
该代码中定义了最和函数取最大值和最小值,聚合函数求和,聚合函数查询行数,聚合函数求平均值。
下面是聚合函数sql语句生成的代码实现:
2 /// 根据实体类型来创建该实体对应数据库表的聚合函数查询sql语句
3 /// 该方法创建的sql语句主要是用于统计查询(最大值,最小值,求和,平均值,数据行数),
4 /// 同时该sql是有条件查询的
5 /// </summary>
6 /// <param name="type">实体类型</param>
7 /// <param name="converage">聚合函数枚举类型</param>
8 /// <param name="propertyName">聚合函数作用的属性名称</param>
9 /// <param name="dic">查询条件属性键值</param>
10 /// <param name="component">查询条件组建对象</param>
11 /// <returns></returns>
12 public string CreateConverageSql(Type type, Converage converage, string propertyName, IDictionary<string, object> dic, out IDataParameter[] param, ConditionComponent component)
13 {
14 StringBuilder sbValues = new StringBuilder();
15 if (string.IsNullOrEmpty(propertyName))
16 {
17 converage = Converage.Count;
18 }
19 if (Converage.Avg == converage)
20 {
21 sbValues.AppendFormat("select avg({0}) from {1} where 1=1 ", EntityTypeCache.GetTableInfo(type).DicColumns[propertyName].Name, EntityTypeCache.GetTableInfo(type).Table.Name);
22 }
23 else if (Converage.Max == converage)
24 {
25 sbValues.AppendFormat("select max({0}) from {1} where 1=1 ", EntityTypeCache.GetTableInfo(type).DicColumns[propertyName].Name, EntityTypeCache.GetTableInfo(type).Table.Name);
26 }
27 else if (Converage.Min == converage)
28 {
29 sbValues.AppendFormat("select min({0}) from {1} where 1=1 ", EntityTypeCache.GetTableInfo(type).DicColumns[propertyName].Name, EntityTypeCache.GetTableInfo(type).Table.Name);
30 }
31 else
32 {
33 sbValues.AppendFormat("select count(*) from {0} where 1=1 ", EntityTypeCache.GetTableInfo(type).Table.Name);
34 }
35
36 IList<IDataParameter> listParams = new List<IDataParameter>();
37 foreach (string key in dic.Keys)
38 {
39 switch (component.DicComponent[key])
40 {
41 case SearchComponent.Equals:
42 sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name, "=", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name);
43 break;
44 case SearchComponent.UnEquals:
45 sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name, "!=", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name);
46 break;
47 case SearchComponent.Between:
48 break;
49 case SearchComponent.Greater:
50 sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name, ">", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name);
51 break;
52 case SearchComponent.GreaterOrEquals:
53 sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name, ">=", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name);
54 break;
55 case SearchComponent.GroupBy:
56 break;
57 case SearchComponent.In:
58 break;
59 case SearchComponent.Less:
60 sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name, "<", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name);
61 break;
62 case SearchComponent.LessOrEquals:
63 sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name, "<=", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name);
64 break;
65 case SearchComponent.Like:
66 break;
67 case SearchComponent.Or:
68 break;
69 case SearchComponent.OrderAsc:
70 break;
71 case SearchComponent.OrderDesc:
72 break;
73 }
74 listParams.Add(CreateParameter("@" + EntityTypeCache.GetTableInfo(type).DicColumns[key].Name, dic[key]));
75 }
76 param = listParams.ToArray();
77 return sbValues.ToString();
78 }
6. 参数占位符
在这个生成sql语句的组建方法中,我们不断使用了IDataParameter 这个对象,这个对象或许我们看了很陌生,我们用得非常少.(纠正一下是接口),在查询sql Server 数据的时候我们经常使用的一个类 SqlParameter,看到这两个名称如此相似,我们应该也想到了他们是什么关系。因为我们不确定是使用什么数据库,可能是Sql Server,可能是Oracle,还有肯能是Access,所以使用IDataParameter 是比较保险的一种方式。在这个方法中我们也看到了一个新的类型—DataType,它定义了数据库中常用25中类型于.net中数据类型的对应列表。
2 * 2010-1-28
3 *
4 * 情 缘
5 *
6 * 用于描述SQL数据库类型和.NET 中数据类型的转化关系
7 * 注意: 这里的数据类型并不是所有的都能直接转化,有
8 * 时候需要通过特定的规则进行强制性转化。这里描述的
9 * 都是SQL Server 数据的类型。
10 * */
11
12 using System;
13
14
15 namespace CommonData.Model.Core
16 {
17 public enum DataType
18 {
19 /// <summary>
20 /// 对应.NET中的数据类型 Int64
21 /// </summary>
22 Bigint,
23
24 /// <summary>
25 /// 对应.NET中的数据类型 Int32
26 /// </summary>
27 Int,
28
29 /// <summary>
30 /// 对应.NET中的数据类型 Int16
31 /// </summary>
32 Smallint,
33
34 /// <summary>
35 /// 对应.NET中的数据类型 System.Byte
36 /// </summary>
37 Tinyint,
38
39 /// <summary>
40 /// 对应.NET中的数据类型 bool
41 /// </summary>
42 Bit,
43
44 /// <summary>
45 /// 对应.NET中的数据类型 System.Decimal
46 /// </summary>
47 Decimal,
48
49 /// <summary>
50 /// 对应.NET中的数据类型 System.Decimal
51 /// </summary>
52 Numeric,
53
54 /// <summary>
55 /// 对应.NET中的数据类型 System.Decimal
56 /// </summary>
57 Money,
58
59 /// <summary>
60 /// 对应.NET中的数据类型
61 /// </summary>
62 Smallmoney,
63
64 /// <summary>
65 /// 对应.NET中的数据类型 System.Double
66 /// </summary>
67 Float,
68
69 /// <summary>
70 /// 对应.NET中的数据类型 System.Single
71 /// </summary>
72 Real,
73
74 /// <summary>
75 /// 对应.NET中的数据类型 System.DateTime
76 /// </summary>
77 Datetime,
78
79 /// <summary>
80 /// 对应.NET中的数据类型 System.DateTime
81 /// </summary>
82 Smalldatetime,
83
84 /// <summary>
85 /// 对应.NET中的数据类型 String
86 /// </summary>
87 Char,
88
89 /// <summary>
90 /// 对应.NET中的数据类型 String
91 /// </summary>
92 Varchar,
93
94 /// <summary>
95 /// 对应.NET中的数据类型 String
96 /// </summary>
97 Text,
98
99 /// <summary>
100 /// 对应.NET中的数据类型 String
101 /// </summary>
102 Nchar,
103
104 /// <summary>
105 /// 对应.NET中的数据类型 String
106 /// </summary>
107 Nvarchar,
108
109 /// <summary>
110 /// 对应.NET中的数据类型 String
111 /// </summary>
112 Ntext,
113
114 /// <summary>
115 /// 对应.NET中的数据类型 System.Byte[]
116 /// </summary>
117 Binary,
118
119 /// <summary>
120 /// 对应.NET中的数据类型 System.Byte[]
121 /// </summary>
122 Varbinary,
123
124 /// <summary>
125 /// 对应.NET中的数据类型 System.Byte[]
126 /// </summary>
127 Image,
128
129 /// <summary>
130 /// 对应.NET中的数据类型 System.DateTime
131 /// </summary>
132 Timestamp,
133
134 /// <summary>
135 /// 对应.NET中的数据类型 System.Guid
136 /// </summary>
137 Uniqueidentifier,
138
139 /// <summary>
140 /// 对应.NET中的数据类型 Object
141 /// </summary>
142 Variant
143
144 }
145 }
146
以上数据类型都是在程序设计中比较常用的,而且在我们在程序设计的时候,往往都会遇到类型不是完全匹配的时候,在后面的章节中将特殊的讲解一个数据类型与数据类型的不同。在使用CreateParameter() 创建占位符参数的时候,我们也要转化一下类型。之前我们使用SqlParameter 的时候,很多人就是new SqlParameter("@name","qingyuan") 这样对应。就默认为string 类型可以直接转化为sql中的nvarchar,varchar等类型,其实不然。中间还有一个转化过程。
2 /// 数据库类型的转化
3 /// </summary>
4 /// <param name="type">程序中的类型</param>
5 /// <returns></returns>
6 private SqlDbType ConvertType(DataType type)
7 {
8 SqlDbType sqlType = SqlDbType.BigInt;
9 switch (type)
10 {
11 case DataType.Bigint:
12 sqlType = SqlDbType.BigInt;
13 break;
14 case DataType.Binary:
15 sqlType = SqlDbType.Binary;
16 break;
17 case DataType.Bit:
18 sqlType = SqlDbType.Bit;
19 break;
20 case DataType.Char:
21 sqlType = SqlDbType.Char;
22 break;
23 case DataType.Datetime:
24 sqlType = SqlDbType.DateTime;
25 break;
26 case DataType.Decimal:
27 sqlType = SqlDbType.Decimal;
28 break;
29 case DataType.Float:
30 sqlType = SqlDbType.Float;
31 break;
32 case DataType.Image:
33 sqlType = SqlDbType.Image;
34 break;
35 case DataType.Int:
36 sqlType = SqlDbType.Int;
37 break;
38 case DataType.Money:
39 sqlType = SqlDbType.Money;
40 break;
41 case DataType.Nchar:
42 sqlType = SqlDbType.NChar;
43 break;
44 case DataType.Ntext:
45 sqlType = SqlDbType.NText;
46 break;
47 case DataType.Numeric:
48 sqlType = SqlDbType.Decimal;
49 break;
50 case DataType.Nvarchar:
51 sqlType = SqlDbType.NVarChar;
52 break;
53 case DataType.Real:
54 sqlType = SqlDbType.Float;
55 break;
56 case DataType.Smalldatetime:
57 sqlType = SqlDbType.SmallDateTime;
58 break;
59 case DataType.Smallint:
60 sqlType = SqlDbType.SmallInt;
61 break;
62 case DataType.Smallmoney:
63 sqlType = SqlDbType.SmallMoney;
64 break;
65 case DataType.Text:
66 sqlType = SqlDbType.Text;
67 break;
68 case DataType.Timestamp:
69 sqlType = SqlDbType.Timestamp;
70 break;
71 case DataType.Tinyint:
72 sqlType = SqlDbType.TinyInt;
73 break;
74 case DataType.Uniqueidentifier:
75 sqlType = SqlDbType.UniqueIdentifier;
76 break;
77 case DataType.Varbinary:
78 sqlType = SqlDbType.VarBinary;
79 break;
80 case DataType.Varchar:
81 sqlType = SqlDbType.VarChar;
82 break;
83 case DataType.Variant:
84 sqlType = SqlDbType.Variant;
85 break;
86
87 }
88 return sqlType;
89 }
90
所以说做程序不能只看到表面现象,只有深入的去了解他们的原理,知识的使用才能游刃有余。即使千变万化,也是万变不离其宗。
说到这篇文章,我只能说我只写到了很肤浅的一部分。明天要去参加老赵的一个演讲,我想大家都应该这道他的主题是什么了,在文章的结尾我想说,.net 的确有他的奥妙之处。或许我们是觉得.net入门非常简单,这些我承认。说.net 是傻瓜式编程,其实是不是傻瓜式编程这个不是由语言决定的,关键在于你怎么去使用这个东西。C# 的确是一门非常优美的语言,我只能以这种言辞来表达。各种语言编程都有他的好处和坏处,不能从单一角度出发,只有你深入的了解了你就会发现它的美。
(注: ORM涉及内容比较多,后续期待,有兴趣的可以与本人探讨)