承接上一篇BLL层代码实现继续,我们接着实现缓存的功能。为了减少数据库的交互,我们希望已经查询得到的数据下次需求再显示时,能直接从缓存取出显示,而减少再一次的从数据库获取的过程,从而更好更快地显示到UI,同时也降低了后台数据库系统的负载。这就需要通过缓存实现。经常听到的一句话更好的概括了这点。(以空间换时间)。缓存仅仅是针对查询的数据,如果查询的内容没有变动,用缓存是最佳的。但是,后台的数据一旦真正改动(如增/删/改)如果显示缓存数据,就会存在数据过期的问题。
以往的做法就是对访问频率高的数据进行缓存,并设置一个合理的缓存有效时间,过了这段时间,缓存的数据将失效,重新从后台获取并进入下一拨的缓存时间段。这种做法终究不是完美的,因为体现地就是不及时,如果缓存时间段有效,即使数据变动,仍是获取缓存数据。如果缓存时间过期,后台数据就算未变化,也仍旧造成重新对数据库的访问。
当然在SQL Server2000及其后续版本实现了当后台监视的数据表数据发生变化时,可以主动发起通知让缓存失效。(即使当时缓存期还未过时。),但是缺陷:当被监视的表的任何数据改变都会缓存失效。而且数据库的缓存方面配置也较为麻烦。虽然SQL Server2005增加了行级别的缓存依赖跟踪,但查询方面的限制很严格。关于这方面可以参考设计体系结构相应部分内容。
综上所述:我们需要当数据变动的时候,缓存才失效。数据查询的内容如果没有变化,就用缓存。对于增/删/改的操作很显然会使数据变动从而引起缓存无效。对于Web(UI),应用程序缓存自然是Cache。我们完全可以在BLL层(封装实现了对应的数据访问操作)去控制缓存。
这上述7个方法中,GetXXX方法用于检索数据(若有缓存,则从缓存取出),其余方法涉及数据变动,自然让对应GetXXX方法中的缓存失效。
先统一一下Cache的Key的命名问题。
这里有个自己定义的规范,由于属于民意调查模块,所以”Polls”命名,GetXXX方法的数据如果为单条记录,对应的命名规则为
Polls_poll_{ID},如果是集合则Polls_Polls,但是仔细发现GetPolls方法由两个布尔参数来控制,我们则以它们的值后续街上如:
Pollls_polls_true_true等方式。对于民意调查,还有一个默认的问卷,将会自动显示在UI上。我们这里还需要增加一个静态方法返回默认的Poll对象(该对象的IsCurrent为1)。对应的缓存编码规则为Polls_poll_current。当前的默认的民意调查的Id编码为Polls_poll_current_id。
是否启用缓存需要查看web.config对应的配置,这个在民意调查模块的自定义设置节已经详细讲述。我们先实现GetXXX系列的方法,如果配置中启用了缓存,并且对应key的缓存对象不为空,则GetXXX检索的数据直接从缓存中获取(注意类型转换),如果缓存对象不存在,则从后台数据库访问并以key为键缓存检索的数据。
改动如下:
由于默认的民意调查访问频率很高,而且为了访问这两个静态方法GetCurrentPollID()和GetCurrentPoll()更加方便,特改造成两个静态属性。
接着改写剩下的引起数据变化的后4个静态方法,先看ArchivePoll方法的改动:
可以看到Remove的选项比较多,这样写容易遗漏。但是仔细发现却分为两组前缀(Polls_polls)和(Polls_poll),这样我们编写一个方法实现当前缀字母相同时的key都会被Remove。(这里遗漏了清空缓存Cache.Remove(“Polls_poll_”+pollId)
显然,方法ClearCacheKeyItems方法对于域对象PollOption同样需要,会考虑放到所共有的父类BasePoll,其实这里只是演示了一个模块,如果有其他的模块(如购物模块Store,则也有相应的该模块的父类BaseStore),所以会有更高一层的父类,我们这里定义为BizObject。
后续的方法接着更改如下:
Poll域对象的静态方法基本上编写完成,实际上为了客户端调用的方便,我们也会需要增加一些实例方法。
因此,我们接着增加实例方法Update和Archive,代码如下:
仔细检查一下,在设置缓存的时候,可以允许设置缓存的时间段。所以Cache的添加方法有三种,直接Cache[key]=object(最简单,但不灵活)。Cache.Add方法(最复杂有七个参数)。Cache.Insert方法(有多个重载,根据需要设置相应参数)。仔细想想缓存的添加和是否启用缓存的判断都可以放到父类BasePoll中。
重构如下:
接下来实现域对象PollOption的缓存部分代码:
比起原来的静态方法增加了一个GetPollId方法,因为PollOption关联Poll,当选项被更改的时候,对应的Poll信息缓存也需要清除,
所以后台相应增加了存储过程GetPollID。
其中方法GetPollId的方法用来 获取选项关联的问卷的主键值。
参考代码如下:
BizObject.cs
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 using System;
2 using System.Collections.Generic;
3 using System.Web;
4 using System.Web.Caching;
5 using System.Collections;
6 namespace BLL
7 {
8 public abstract class BizObject
9 {
10 protected static Cache Cache
11 {
12 get { return HttpContext.Current.Cache; }
13 }
14 protected static void ClearCacheKeyItems(string prefix)
15 {
16 IDictionaryEnumerator items = Cache.GetEnumerator();//获得缓存的集合
17 List<string> clearKeys = new List<string>();
18 while (items.MoveNext())
19 {
20 if (items.Key.ToString().ToLower().StartsWith(prefix.ToLower()))
21 clearKeys.Add(items.Key.ToString());
22 }
23 foreach (string clearKey in clearKeys) Cache.Remove(clearKey);
24
25 }
26 }
27 }
BasePoll.cs
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 using System;
2 using System.Collections.Generic;
3 using System.Web;
4 using MyWebSite;
5 namespace BLL
6 {
7 public class BasePoll:BizObject
8 {
9 private int _id = 0;
10 public int Id
11 {
12 get { return _id; }
13 set { _id = value; }
14 }
15
16 private string _addedBy = "";
17 public string AddedBy
18 {
19 get { return _addedBy; }
20 set { _addedBy = value; }
21 }
22
23 private DateTime _addedDate = DateTime.Now;
24 public DateTime AddedDate
25 {
26 get { return _addedDate; }
27 set { _addedDate = value; }
28 }
29
30 protected static bool EnableCache
31 { get { return Globals.Settings.Polls.EnableCaching; } }
32
33 protected static int Duration
34 { get { return Globals.Settings.Polls.CacheDuration; } }
35
36 protected static void AddCache(string key, object data)
37 {
38 if (EnableCache && data != null)
39 Cache.Insert(key, data, null, DateTime.Now.AddSeconds(Duration), TimeSpan.Zero);
40 }
41 }
42 }
Poll.cs
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 namespace BLL
2 {
3 #region 业务域对象
4 /*包含了属性和方法(实例方法和静态方法),属性涵盖了DAL层简单实体对象的所有属性,
5 * 根据需要会增加自己特有的属性(关联父对象或子对象集合引用。)
6 * 业务域对象的封装使得UI使用起来更加快捷方便。(若是Web版UI,通过ObjectDataSource可以很好的与之交互)
7 */
8 #endregion
9 public class Poll : BasePoll
10 {
11
12 #region 属性(简单实体对象所拥有)
13 private string _questionText = "";
14 public string QuestionText
15 {
16 get { return _questionText; }
17 set { _questionText = value; }
18 }
19
20 private bool _isCurrent = false;
21 public bool IsCurrent
22 {
23 get { return _isCurrent; }
24 set { _isCurrent = value; }
25 }
26
27 private bool _isArchived = false;
28 public bool IsArchived
29 {
30 get { return _isArchived; }
31 set { _isArchived = value; }
32 }
33
34 private DateTime _archivedDate = DateTime.MinValue;
35 public DateTime ArchivedDate
36 {
37 get { return _archivedDate; }
38 set { _archivedDate = value; }
39 }
40
41 private int _votes = 0;//增加了总票数
42 public int Votes
43 {
44 get { return _votes; }
45 set { _votes = value; }
46 }
47 #endregion
48
49 #region 属性(业务关联特有增加的)(如该民意问题的选项集合)
50 private List<PollOption> _options = null;
51 public List<PollOption> Options //注意该特有属性不在构造函数中赋值(以免不必要的增加数据访问负载)
52 {
53 get
54 {
55 if (_options == null)
56 _options = PollOption.GetPollOptions(Id);//按需加载
57
58 return _options;
59 }
60 }
61 #endregion
62 public Poll(int id, string addedBy, DateTime addedDate, string questionText,
63 bool isCurrent, bool isArchived, DateTime archivedDate, int votes)
64 {
65 this.Id = id;
66 this.AddedBy = addedBy;
67 this.AddedDate = addedDate;
68 this.QuestionText = questionText;
69 this.IsCurrent = isCurrent;
70 this.IsArchived = isArchived;
71 this.ArchivedDate = archivedDate;
72 this.Votes = votes;
73 }
74
75 #region 静态方法(实例方法一般都调用相应的静态方法)
76 //先编写静态方法--基本就是对DAL的PollsProvider对应方法的封装,不过需要注意实体对象转换成域对象
77 private static Poll ChangeToPoll(PollDetail obj)
78 {
79 return new Poll(obj.Id, obj.AddedBy, obj.AddedDate, obj.QuestionText, obj.IsCurrent,
80 obj.IsArchived, obj.ArchivedDate, obj.Votes);
81 }
82 private static List<Poll> ChangeToPollCollection(List<PollDetail> objs)
83 {
84 List<Poll> results = new List<Poll>();
85 foreach (PollDetail obj in objs)
86 results.Add(ChangeToPoll(obj));
87 return results;
88 }
89
90 public static int CurrentPollID { get { return GetcurrentPollId(); } }
91
92 public static Poll CurrentPoll { get { return GetCurrentPoll(); } }
93
94 #region 调用PollsProvider对应方法
95 public static int GetcurrentPollId()
96 {
97 string key = "Polls_poll_current_id";
98 if (EnableCache && Cache[key] != null) return (int)Cache[key];
99 int currentId= PollsProvider.Instance.GetcurrentPollId();
100 if (EnableCache) AddCache(key, currentId); return currentId;
101 }
102
103 public static Poll GetCurrentPoll()
104 {
105 string key = "Polls_poll_current";
106 if (EnableCache && Cache[key] != null) return (Poll)Cache[key];
107 Poll poll= GetPoll(GetcurrentPollId());
108 if (EnableCache) AddCache(key, poll); return poll;
109 }
110 public static Poll GetPoll(int pollId)
111 {
112 string key="Polls_poll_"+pollId;
113 if (EnableCache && Cache[key] != null) return (Poll)Cache[key];
114 Poll poll= ChangeToPoll(PollsProvider.Instance.GetPoll(pollId));
115 if (EnableCache) AddCache(key, poll); return poll;
116 }
117 public static List<Poll> GetPolls(int includeActive, int includeArchived)
118 {//首先判断是否启用了缓存设置
119 string key=string.Format("Polls_polls_{0}_{1}",includeActive,includeArchived);
120 if (EnableCache && Cache[key] != null) return (List<Poll>)Cache[key];
121 List<Poll> polls= ChangeToPollCollection(PollsProvider.Instance.GetPolls(includeActive,includeArchived));
122 if (EnableCache) AddCache(key, polls); return polls;
123 }
124
125 public static bool ArchivePoll(int pollId)
126 { //民意调查问题归档(原来没有归档的变成归档的),影响了GetPolls方法的所有情形
127 //影响了Polls_poll_{id}
128 //如果pollId为GetcurrentPollId(),则影响 当前的默认民意调查(Polls_poll_current_id/Polls_poll_current)
129 if (EnableCache)
130 {
131 ClearCacheKeyItems("Polls_polls");
132 ClearCacheKeyItems("Polls_poll_" + pollId);
133 if (pollId == CurrentPollID) ClearCacheKeyItems("Polls_poll_current");
134 }
135 return PollsProvider.Instance.ArchivePoll(pollId);
136 }
137
138 public static bool DeletePoll(int pollId)
139 {
140 if (EnableCache)
141 {
142 ClearCacheKeyItems("Polls_polls");
143 ClearCacheKeyItems("Polls_poll_" + pollId);
144 if (pollId == CurrentPollID) ClearCacheKeyItems("Polls_poll_current");
145 }
146 return PollsProvider.Instance.DeletePoll(pollId); }
147
148 public static int InsertPoll(string addedBy, DateTime addedDate, string questionText,
149 bool isCurrent)
150 { // 刚添加的问卷只能是激活状态
151 if (EnableCache)
152 {
153 ClearCacheKeyItems("Polls_polls_true");
154 if (isCurrent) ClearCacheKeyItems("Polls_poll_current");
155 }
156 return PollsProvider.Instance.InsertPoll(addedBy, addedDate, questionText, isCurrent);
157 }
158 public static bool UpdatePoll(int pollId, string questionText, bool isCurrent)
159 { //只能更改非归档的问卷
160 if (EnableCache)
161 {
162 ClearCacheKeyItems("Polls_polls_true");
163 ClearCacheKeyItems("Polls_poll_" + pollId);
164 if (isCurrent) ClearCacheKeyItems("Polls_poll_current");
165 }
166 return PollsProvider.Instance.UpdatePoll(pollId, questionText, isCurrent);
167 }
168 #endregion
169 #endregion
170
171 #region 实例方法
172 public bool Delete()
173 {
174 bool success = Poll.DeletePoll(Id);
175 if (success) Id = 0;
176 return success;
177 }
178 public bool Update()
179 {
180 return Poll.UpdatePoll(Id, QuestionText, IsCurrent);
181 }
182 public bool Archive()
183 {
184 bool success= Poll.ArchivePoll(Id);
185 if (success) { IsCurrent = false ; IsArchived =true; ArchivedDate = DateTime.Now; }
186 return success;
187 }
188 #endregion
189 }
190 }
PollOption.cs
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public class PollOption:BasePoll
2 {
3 #region 封装简单实体对象的属性
4
5 private string _optionText = "";
6 public string OptionText
7 {
8 get { return _optionText; }
9 set { _optionText = value; }
10 }
11
12 private int _votes = 0;
13 public int Votes
14 {
15 get { return _votes; }
16 set { _votes = value; }
17 }
18
19 private int _pollId = 0;
20 public int PollId
21 {
22 get { return _pollId; }
23 set { _pollId = value; }
24 }
25
26 private double _percentage = 0;
27 public double Percentage
28 {
29 get { return _percentage; }
30 set { _percentage = value; }
31 }
32 #endregion
33
34 private Poll _poll;//关联的问卷信息
35 public Poll Poll
36 {
37 get { if (_poll == null) _poll = Poll.GetPoll(PollId); return _poll; }
38 }
39
40 public PollOption(int id, string optionText, int votes, int pollid,
41 string addedBy, DateTime addedDate, double percentage)
42 {
43 this.Id = id;
44 this.OptionText = optionText;
45 this.Votes = votes;
46 this.PollId = pollid;
47 this.AddedBy = addedBy;
48 this.AddedDate = addedDate;
49 this.Percentage = percentage;
50 }
51
52 private static PollOption ChangeToPollOption(PollOptionDetail obj)
53 {
54 return new PollOption(obj.Id, obj.OptionText, obj.Votes, obj.PollId,
55 obj.AddedBy, obj.AddedDate, obj.Percentage);
56 }
57 private static List<PollOption> ChangeToPollOptionCollection(List<PollOptionDetail> objs)
58 {
59 List<PollOption> results = new List<PollOption>();
60 foreach (PollOptionDetail obj in objs)
61 results.Add(ChangeToPollOption(obj));
62 return results;
63 }
64 public static PollOption GetPollOption(int optionId)
65 {
66 string key = "Polls_option_" + optionId;
67 if (EnableCache && Cache[key] != null) return (PollOption)Cache[key];
68 PollOption option = ChangeToPollOption(PollsProvider.Instance.GetPollOption(optionId));
69 if (EnableCache) AddCache(key, option);
70 return option;
71 }
72 public static List<PollOption> GetPollOptions(int pollId)
73 {
74 string key = "Polls_options_" + pollId;
75 if (EnableCache && Cache[key] != null) return (List<PollOption>)Cache[key];
76 List<PollOption> options =
77 ChangeToPollOptionCollection(PollsProvider.Instance.GetPollOptions(pollId));
78 if (EnableCache) AddCache(key, options);
79 return options;
80 }
81 public static int GetPollId(int optionId)
82 {
83 string key = "Polls_option_" + optionId;//查找关联的问卷信息,从而获取其主键
84 if (EnableCache && Cache[key] != null) return ((PollOption)Cache[key]).PollId;
85
86 key = "Polls_GetPollId_" + optionId;
87 if (EnableCache && Cache[key] != null) return (int)Cache[key];
88
89 int pollId= PollsProvider.Instance.GetPollID(optionId);
90 if (EnableCache) AddCache(key,pollId );
91 return pollId;
92 }
93
94 public static bool DeletePollOption(int optionId){
95 if (EnableCache)
96 {
97 ClearCacheKeyItems("Polls_option_" + optionId);//清空该选项
98 ClearCacheKeyItems("Polls_options_"+GetPollId(optionId));//清空Poll对应的选项集合
99 ClearCacheKeyItems("Polls_poll_" + GetPollId(optionId));//清空Poll
100 }
101 return PollsProvider.Instance.DeletePollOption(optionId);
102 }
103 public static int InsertPollOption(string optionText, int pollId, string addedby,
104 DateTime addedDate)
105 {
106 if (EnableCache)
107 {
108 ClearCacheKeyItems("Polls_options_"+pollId );
109 ClearCacheKeyItems("Polls_poll_" + pollId);//不要忘记清空与之关联的Poll的缓存
110 }
111 return PollsProvider.Instance.InsertPollOption(optionText, pollId, addedby, addedDate);
112 }
113 public static bool UpdatePollOption(int optionId, string optionText)
114 {
115 if (EnableCache)
116 {
117 ClearCacheKeyItems("Polls_option_" + optionId);
118 ClearCacheKeyItems("Polls_options_" + GetPollId(optionId));
119 ClearCacheKeyItems("Polls_poll_" + GetPollId(optionId));
120 }
121 return PollsProvider.Instance.UpdatePollOption(optionId, optionText);
122 }
123
124 public static bool InsertVote(int optionId)
125 {
126 if (EnableCache)
127 {
128 ClearCacheKeyItems("Polls_option_" + optionId);
129 ClearCacheKeyItems("Polls_options_" + GetPollId(optionId));
130 ClearCacheKeyItems("Polls_poll_" + GetPollId(optionId));
131 }
132 return PollsProvider.Instance.InsertVote(optionId);
133 }
134
135 public bool Delete()
136 { return DeletePollOption(Id); }
137
138 public bool Update()
139 { return UpdatePollOption(Id, OptionText); }
140
141 public bool Vote()
142 { return InsertVote(Id); }
143 }