浅谈C#中的延迟加载(3)——还原模型的业务规则
啊~~最近的业余时间都用在修改自己的博客上面了,主要是这段时间在网站的留言板上发现很多外国的垃圾广告,于是做了个“IP黑名单”的功能,留言和文章评论也都加了验证码,顺便把后台的代码整理了一下,希望新加的验证码不会对大家留言和发评论造成不便!
今天在博客园上有朋友留言说怎没有写完整,不好意思啊,这段时间有空的时候都在改自己的博客了。555~
文章依旧是在自己的博客转过来的,原文地址:http://www.youguanbumen.net/Article.aspx?id=74
【原文内容】
上一篇文章讲到把实体类中需要实现延迟加载的属性声明为virtual,然后继承实体类做一个子类,在子类里面实现该属性,配合使用委托来实现比较完美的延迟加载(原本的”模型层“依旧保持在最底层用于贯穿三层结构,同时又可以实现在实体类的属性里面访问到比他高层的”数据访问层“)。文章的最后依旧出现杯具,原因是在对模型的属性实现延迟加载之前,这个属性可能由于我们业务的需要,它并不单单是作为一个存储和读取的功能使用,而是在其get或者set的访问器中都包含这或许复杂或许简单的逻辑代码。
举例:考虑一下这个情景,我们有一个叫做任务单的实体类,其中有两个属性,一个叫做”任务名”,一个叫做“发布时间”,现在有这样的业务规定,任务名称可以为空,但如果任务名称为空的话我们要读取“发布时间”生成一个任务名来代替掉这个空值(例如叫做“Issue20110120191345”),当然这个例子有点牵强,主要是我想不出什么很具体的实例,但是在实际开发中这种情况肯定是有的并且其中的逻辑代码有可能复杂到你难以想象。
沿用前面两篇文章的例子,我模拟了这一现象,对“文章”实体类做了点修改,增加了一个名为GetCategoryRecord的字符串型属性,它的作用基本上可以从字面上看出来,叫做“Category属性get访问器调用记录”。于是“文章“类(基类)修改如下:
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
|
namespace Model { // 文章实体类 public class Article { public int ArticleID { get ; set ; } public string Title { get ; set ; } public string Cotnent{ get ; set ; } public DateTime CreateTime { get ; set ; } public int CategoryID { get ; set ; } /// <summary> /// 所属分类 /// </summary> protected Model.ArticleCategory _category; /// <summary> /// 所属分类 /// </summary> public virtual Model.ArticleCategory Category { get { GetCategoryRecord += "获取分类;" ; return _category; } } /// <summary> /// Category属性get访问器调用记录 /// </summary> public string GetCategoryRecord { get ; set ; } } } |
这里我们关心那两个有写注释的属性,并且出现了一个保护字段_category(这个尤其重要,起到和子类的联通作用)。可以看到现在有这样的业务规则了:Category属性被get一次就会往GetCategoryRecord属性中做点记录。于是我们在设计代码的时候立刻会想到要是继承Model.Article类的基类要是重写这个属性的话势必要保持这个业务不变,否则在实现延迟加载之后肯定会丢掉一些之前设计好的业务逻辑了。修改继承它的子类如下:
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
|
namespace DModel { /// <summary> /// 文章 /// </summary> public class Article : Model.Article { /// <summary> /// 所属分类 /// </summary> public override Model.ArticleCategory Category { get { if ( base ._category == null ) { if (CategoryLazyLoader != null ) { base ._category = CategoryLazyLoader(CategoryID); } else { base ._category = null ; } } return base .Category; } } /// <summary> /// 文章分类延时加载器(委托) /// </summary> public Func< int , Model.ArticleCategory> CategoryLazyLoader { get ; set ; } } } |
这里可以看到DModel.Article类的Category属性中对基类的保护字段base._cateogry进行了操作,最后返回的是基类Category。粗看起来似乎有点乱,但是理清一下思路其实如下:基类的Category属性通过返回_category字段的方式返回值,也就是说数据是存在_category字段而不是属性中,但是_category字段怎么才会有值呢,那就是在子类里面通过调用委托拿来的,而这个属性在子类里面不是直接返回的,而是调用基类来返回,这样一来,调用到子类的Category属性的get访问器的时候,先对基类的_categoty字段赋值,然后调用基类的Category属性执行了一些逻辑代码,最后成功地把(已经被赋值的)基类的_categoty字段给返回去。而这一切都是在前面我们实现好的延迟加载的基础上完成的。总结成几个字就是:子类负责延时加载,基类赋值数据存储和返回!呵呵,是不是觉得很简单呢^^
其实这一篇讲的情况不是针对延迟加载这个技术来讲的,在我们的开发过程中经常会遇到这种实现了业务代码的实体类属性,拥有这种属性的模型通常被叫做“充血模型”,如果模型的属性都是简单的get和set的话通常叫做“贫血模型”(当然可能有其他叫法 哈~)。这篇文章是在没啥内容,算是对前两篇文章的一个补充吧,希望没有浪费你的时间哈^_^。
PS:不知道有哪位朋友对反垃圾留言比较有办法呢?听说wp上面很多垃圾留言,也出现了很多反垃圾留言的插件,在asp.net中大家一般是怎么做的呢?希望有了解的朋友和我交流一下,方式不限!