在以前的2篇文章中,个人突发奇想的仿效Java中,以?替代参数,然而在应对参数重复的情况下,需要重复填写参数,实在是挺麻烦的。
因为最近在学习和使用NHibernate,对于Hql中使用[:参数名]的方式可以解决重复参数的问题。
因为在参数键值传递的时候使用的是Hashtable,因此我们需要通过正则匹配【":\w+"】,并截取得到对应的键,获取对应的值。
代码如下:
1 Regex regMark = new Regex(@":\w+");
2 sql = regMark.Replace(sql, s =>
3 {
4 string mark = s.Value.Substring(1), paramName = string.Empty;
5 if (columns.ContainsKey(mark))
6 {
7 //获取参数名并创建DbParameter
8 }
9 else
10 {
11 throw new NullReferenceException("不存在对应的标记键值!");
12 }
13 return paramName;
14 });
因为参数可能会出现重复,出现重复的时候,通过对应的[:参数名],需要获取DbParameter以及Sql中的@参数名,并且我们通过Hashtable传入的值可能是数组,因此我选择使用泛型字典IDictionary<string, IDictionary<string, DbParameter>>存储。
代码如下:
1 /// <summary>
2 /// 转换标记
3 /// </summary>
4 /// <param name="sql">sql语句</param>
5 /// <param name="columns">标记值Hashtable</param>
6 /// <returns></returns>
7 public DbParameter[] CastMark(ref string sql, Hashtable columns)
8 {
9 Regex regMark = new Regex(@":\w+");
10 sql = regMark.Replace(sql, s =>
11 {
12 string mark = s.Value.Substring(1), paramName = string.Empty;
13 if (this.dicMark.ContainsKey(mark))
14 {
15 paramName = this.ExistMark(mark);
16 }
17 else
18 {
19 if (columns.ContainsKey(mark))
20 {
21 paramName = this.NotExistMark(mark, columns[mark]);
22 }
23 else
24 {
25 throw new NullReferenceException("不存在对应的标记键值!");
26 }
27 }
28 return paramName;
29 });
30 //返回DbParameter数组
31 }
32
33 /// <summary>
34 /// 存在标记参数
35 /// </summary>
36 /// <param name="mark">标记</param>
37 /// <returns></returns>
38 string ExistMark(string mark)
39 {
40 IDictionary<string, T> dicParam = this.dicMark[mark];
41 string paramName = string.Empty;
42 if (dicParam.Count > 1)
43 {
44 paramName = string.Join(",", dicParam.Keys.ToArray());
45 }
46 else
47 {
48 paramName = dicParam.First().Key;
49 }
50 return paramName;
51 }
52
53 /// <summary>
54 /// 不存在标记参数
55 /// </summary>
56 /// <param name="mark">标记</param>
57 /// <param name="value">参数值</param>
58 /// <returns></returns>
59 string NotExistMark(string mark, object value)
60 {
61 string paramName = string.Empty;
62 IDictionary<string, DbParameter> dicParam = new Dictionary<string, DbParameter>();
63 if (value.GetType().IsArray)
64 {
65 IList values = value as IList;
66 int length = values.Count;
67 string[] names = new string[length];
68 for (int i = 0; i < length; i++)
69 {
70 names[i] = this.GetParamName();
71 dicParam.Add(names[i], this.CreateDbParam(names[i], values[i]));
72 }
73 paramName = string.Join(",", names);
74 }
75 else
76 {
77 paramName = this.GetParamName();
78 dicParam.Add(paramName, this.CreateDbParam(paramName, value));
79 }
80 dicMark.Add(mark, dicParam);
81 return paramName;
82 }
83
84 /// <summary>
85 /// 创建Db参数
86 /// </summary>
87 /// <param name="paramName">参数名</param>
88 /// <param name="value">参数值</param>
89 /// <returns></returns>
90 DbParameter CreateDbParam(string paramName, object value)
91 {
92 //工厂模式,根据对应参数创建不同的DbParameter派生类
93 }
94
95 /// <summary>
96 /// 获取参数名
97 /// </summary>
98 /// <returns></returns>
99 string GetParamName()
100 {
101 return "@Param" + index++;
102 }
到这里我们已经解决了大部分的问题了,但是每次都通过传入的参数来创建对应的DbParameter派生类也是一件挺麻烦的事情,我选择使用泛型去解决这个问题。
1 public class MarkParameter<T> where T : DbParameter, new()
这样在类内部就可以使用T来表示DbParameter的派生类了,因此一些方法就需要修改一下了。
代码如下:
1 /// <summary>
2 /// 标记字典
3 /// </summary>
4 IDictionary<string, IDictionary<string, T>> dicMark = new Dictionary<string, IDictionary<string, T>>();
5
6
7 /// <summary>
8 /// 转换标记
9 /// </summary>
10 /// <param name="sql">sql语句</param>
11 /// <param name="columns">标记值Hashtable</param>
12 /// <returns></returns>
13 public T[] CastMark(ref string sql, Hashtable columns)
14 {
15 //代码省略
16 }
17
18 /// <summary>
19 /// 创建Db参数
20 /// </summary>
21 /// <param name="paramName">参数名</param>
22 /// <param name="value">参数值</param>
23 /// <returns></returns>
24 T CreateDbParam(string paramName, object value)
25 {
26 T param = new T();
27 param.ParameterName = paramName;
28 param.Value = value;
29 return param;
30 }
到这里,我们就已经将一些主要的问题都解决掉了,完成了自己的sql参数替换了。