用IBatisNet作为持久层工具,有一个很好的好处就是很方便地使用它本身的缓存模型,可以控制在数据修改后缓存过期,但它的限制也是相当明显的,数据缓存和数据的更新操作必须在同一个应用程序域当中,当我在一台机器上缓存数据,而在另一台机器上修改数据(或者直接修改数据表)时就无法通知缓存过期了。这一个很棘手的问题,到底要不要使用缓存呢?在Asp.Net 2.0 当中有提供一个运行Sql Server的数据库更新通知缓存过期策略,(实现Cache SqlDependency,请参阅SQL Cache Dependency With SQL Server 2000),我可以将数据读取到UI层,然后再UI层再将数据缓存到HttpContext.Cache当中,再设置一个SqlDependency依赖,将它与指定的表做更新依赖。显然这样的做法是可行的,但如果就这样使用在当前的项目中,变得得不偿失。由于当前的DAL分为接口和实现,再结合Castle的IOC容器,实现组件的松耦合,可以随时改变接口的实现部分,尽管说做到改变数据库还是一个理论上的意愿,但是在UI层就将系统与Sql Server数据绑死(SqlDependency只支持Sql Server),显然与系统整体结构互相矛盾。最好的办法是在数据访问层直接就实现数据的缓存功能,这样数据是否实现缓存,如何缓存就对数据访问者来说就是透明的。还有一个需要考虑的问题是,对于数据访问层来说,它应该是独立于程序的架构(B/S,C/S),也就是对于一个程序来说,不管是C/S还是B/S结构的,应该可以使用同一个的数据访问层,这边就还需要考虑到HttpContext.Cache的使用问题了。不过这倒不是很难解决的问题,HttpContext.Cache的时候判断一下对象是否为空就行了。
缓存KEY的生成也是一个大问题,Ibatisnet的缓存模型,可是做到同一个查询语句根据参数的值的来缓存数据,比如:这个查询的条件为1,而下次的查询条件仍为1时就会先去缓存读取数据,而如果为2时就会直接去数据库查询数据。在Ibatisnet中有一个专门的CacheKey类来做为缓存的key,而HttpContext.Cache只接收string类型的缓存KEY,在这边就必须根据Ibatisnet动态生成的Sql语句和参数值进行拼接做为缓存的key。为了保证数据一定时间不用后及时过期,不浪费太多内存,还应该缓每个缓存项添加相对时间过期策略。
下面贴出对Ibatisnet的查询接口的适配方法。做个笔记。
1public class MapperAdapter
2 {
3 public SqlMapper GetMapper()
4 {
5 return IBatisNet.DataMapper.Mapper.Instance();
6 }
7 private HttpContext BsContext = HttpContext.Current;
8
9 private SqlMapSession m_sqlSession;
10 public MapperAdapter()
11 {
12 m_sqlSession = new SqlMapSession(GetMapper().DataSource);
13 }
14
15 /**//// <summary>
16 /// Queries for list.
17 /// </summary>
18 /// <param name="statementName">语句名称.</param>
19 /// <param name="parameterObject">参数.</param>
20 /// <param name="p_strTableName">缓存过期依赖表</param>
21 /// <returns></returns>
22 public GenericObjectListWrapper<T> QueryForList<T>(string statementName, object parameterObject, string p_strTableName)
23 where T : DataObjectBase
24 {
25 return QueryForList<T>(statementName, parameterObject, -1, -1, p_strTableName);
26 }
27
28 /**//// <summary>
29 /// Queries for list.
30 /// </summary>
31 /// <param name="statementName">Name of the statement.</param>
32 /// <param name="parameterObject">The parameter object.</param>
33 /// <param name="skipResult">The skip result.</param>
34 /// <param name="maxResult">The max result.</param>
35 /// <param name="p_strTableName">过期依赖表名</param>
36 /// <returns></returns>
37 public GenericObjectListWrapper<T> QueryForList<T>(string statementName, object parameterObject,
38 int skipResult, int maxResult, string p_strTableName)
39 where T : DataObjectBase
40 {
41 string cacheKey = GetCacheKey("QueryForList", statementName, parameterObject);
42 GenericObjectListWrapper<T> m_list = Cache_Get(cacheKey) as GenericObjectListWrapper<T>;
43 if (m_list == null)
44 {
45 m_list = new GenericObjectListWrapper<T>(GetMapper().QueryForList(statementName, parameterObject, skipResult, maxResult) as ObjectList);
46 Cache_Add(cacheKey, m_list, p_strTableName);
47 }
48 return m_list;
49 }
50
51 public IDictionary QueryForDictionary(string statementName, object parameterObject, string keyProperty, string p_strTableName)
52 {
53 return QueryForMap(statementName, parameterObject, keyProperty, p_strTableName);
54 }
55 /**//// <summary>
56 /// Queries for dictionary.
57 /// </summary>
58 /// <param name="statementName">Name of the statement.</param>
59 /// <param name="parameterObject">The parameter object.</param>
60 /// <param name="keyProperty">The key property.</param>
61 /// <param name="valueProperty">.</param>
62 /// <param name="p_strTableName">过期依赖表名.</param>
63 /// <returns></returns>
64 public IDictionary QueryForDictionary(string statementName, object parameterObject, string keyProperty, string valueProperty,
65 string p_strTableName)
66 {
67 return QueryForMap(statementName, parameterObject, keyProperty, valueProperty, p_strTableName);
68 }
69 /**//// <summary>
70 /// Queries for map.
71 /// </summary>
72 /// <param name="statementName">Name of the statement.</param>
73 /// <param name="parameterObject">The parameter object.</param>
74 /// <param name="keyProperty">The key property.</param>
75 /// <param name="p_strTableName">过期依赖表名.</param>
76 /// <returns></returns>
77 public IDictionary QueryForMap(string statementName, object parameterObject, string keyProperty, string p_strTableName)
78 {
79 return QueryForMap(statementName, parameterObject, keyProperty, null);
80 }
81
82 /**//// <summary>
83 /// Queries for map.
84 /// </summary>
85 /// <param name="statementName">Name of the statement.</param>
86 /// <param name="parameterObject">The parameter object.</param>
87 /// <param name="keyProperty">The key property.</param>
88 /// <param name="valueProperty">The value property.</param>
89 /// <param name="p_strTableName">过期依赖表名.</param>
90 /// <returns></returns>
91 public IDictionary QueryForMap(string statementName, object parameterObject, string keyProperty,
92 string valueProperty, string p_strTableName)
93 {
94 string cacheKey = GetCacheKey("QueryForMap", statementName, parameterObject);
95 IDictionary dictionary = Cache_Get(cacheKey) as IDictionary;
96 if (dictionary == null)
97 {
98 dictionary = GetMapper().QueryForMap(statementName, parameterObject, keyProperty, valueProperty);
99 Cache_Add(cacheKey, dictionary, p_strTableName);
100 }
101 return dictionary;
102 }
103
104 public T QueryForObject<T>(string statementName, object parameterObject, string p_strTableName)
105 where T : DataObjectBase
106 {
107 string cacheKey = GetCacheKey("QueryForObject", statementName, parameterObject);
108 T dataObject = Cache_Get(cacheKey) as T;
109 if (dataObject == null)
110 {
111 dataObject = GetMapper().QueryForObject(statementName, parameterObject) as T;
112 Cache_Add(cacheKey, dataObject, p_strTableName);
113 }
114 return dataObject;
115 }
116
117 /**//// <summary>
118 /// Queries for object.
119 /// </summary>
120 /// <param name="statementName">Name of the statement.</param>
121 /// <param name="parameterObject">The parameter object.</param>
122 /// <param name="resultObject">The result object.</param>
123 /// <param name="p_strTableName">过期依赖表名.</param>
124 /// <returns></returns>
125 public T QueryForObject<T>(string statementName, object parameterObject, T resultObject, string p_strTableName)
126 where T : DataObjectBase
127 {
128 string cacheKey = GetCacheKey("QueryForObject", statementName, parameterObject);
129 resultObject = Cache_Get(cacheKey) as T;
130 if (resultObject == null)
131 {
132 resultObject = GetMapper().QueryForObject(statementName, parameterObject, resultObject) as T;
133 Cache_Add(cacheKey, resultObject, p_strTableName);
134 }
135 return resultObject;
136 }
137 Cache Help#region Cache Help
138 /**//// <summary>
139 /// Gets the cache key.生成缓存KEY
140 /// </summary>
141 /// <param name="p_strExecuteMethodName">Name of the P_STR execute method.</param>
142 /// <param name="statementName">Name of the statement.</param>
143 /// <param name="parameterObject">The parameter object.</param>
144 /// <returns></returns>
145 private string GetCacheKey(string p_strExecuteMethodName, string statementName, object parameterObject)
146 {
147 IMappedStatement mappedStatement = GetMapper().GetMappedStatement(statementName);
148 RequestScope requestScope = mappedStatement.Statement.Sql.GetRequestScope(parameterObject, m_sqlSession);
149
150 //
151 // 通过这个方法生成一个DbCommand,并且给所有的SQL参数指定值。如果没有调用此方法,requestScope.IDbCommand属性为空
152 //
153 mappedStatement.PreparedCommand.Create(requestScope, m_sqlSession, mappedStatement.Statement, parameterObject);
154 PreparedStatement preparedStatement = requestScope.PreparedStatement;
155
156 StringBuilder cacheKey = new StringBuilder(p_strExecuteMethodName + ":" + mappedStatement.Statement.Id + ";");
157 cacheKey.Append(" SQL:" + preparedStatement.PreparedSql);
158 cacheKey.Append(" Parameters:");
159 foreach (object obj in requestScope.IDbCommand.Parameters)
160 {
161 DbParameter parameter = (DbParameter)obj;
162 cacheKey.Append(string.Format("{0}={1};", parameter.ParameterName, parameter.Value));
163 }
164 return cacheKey.ToString();
165 }
166
167 protected virtual object Cache_Get(string p_strKey)
168 {
169 if (BsContext != null)
170 {
171 return BsContext.Cache.Get(p_strKey);
172 }
173 return null;
174 }
175
176 protected virtual void Cache_Add(string p_strKey, object p_value, string p_strTableName)
177 {
178 if (BsContext != null && p_value != null)
179 {
180 SqlCacheDependency m_sqlDependency = new SqlCacheDependency("CommonSqlDependency", p_strTableName);
181 BsContext.Cache.Insert(p_strKey, p_value, m_sqlDependency, DateTime.MaxValue, TimeSpan.Parse("00:30:00"));
182 }
183 }
184 #endregion
185 }