用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的时候判断一下对象是否为空就行了。
1
public 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
#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
}
缓存KEY的生成也是一个大问题,Ibatisnet的缓存模型,可是做到同一个查询语句根据参数的值的来缓存数据,比如:这个查询的条件为1,而下次的查询条件仍为1时就会先去缓存读取数据,而如果为2时就会直接去数据库查询数据。在Ibatisnet中有一个专门的CacheKey类来做为缓存的key,而HttpContext.Cache只接收string类型的缓存KEY,在这边就必须根据Ibatisnet动态生成的Sql语句和参数值进行拼接做为缓存的key。为了保证数据一定时间不用后及时过期,不浪费太多内存,还应该缓每个缓存项添加相对时间过期策略。
下面贴出对Ibatisnet的查询接口的适配方法。做个笔记。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185
