Spiga

BlogEngine.Net架构与源代码分析系列part2:业务对象——共同的父类BusinessBase

2008-11-05 15:29 by GUO Xingwang, 8812 visits, 收藏, 编辑

上篇文章朋友的评论给了我很大的动力写这个系列的文章,看来大家都比较关注这个系列。为了后续文章做一个铺垫,我在这篇文章的前半部分讲解一下BlogEngine.Net的整体架构,后半部分主要是对于BusinessBase类的分析。

     下载源代码以后打开解决方案,我们发现从项目的组织结构上BlogEngine.Net分成两个项目:一个是BlogEngine.Core,顾名思意,它就是BlogEngine.Net的核心逻辑层。所有的业务逻辑和一些功能都在这个项目中体现,实际上这个核心业务层中也有数据访问的一部分,那就是Provider模式。在BlogEngine.Net中,关系数据库或XML等的作用只有一个,那就是存储数据,BlogEngine.Net的业务对象的ID生成是由核心层控制的,而不是用数据存储部分生成的,因为这样可以支持更多的数据源。它不同于很多其他业务系统,数据库里面可能有很多存储过程,触发器,函数等来完成一定的业务运算和数据处理。在BlogEngine.Net中,我们甚至可以使用一个.txt文件来自己开发一个Provider给BlogEngine.Net使用,方法很简单,只要实现BlogProvider(BlogEngine.Net提供),MembershipProvider和RoleProvider就可以了,实际上BlogEngine.Net也在很大程度上利用了.Net本身的经典模型。另外的一个项目是一个站点,主要就是具体的Web实现,但是具体的功能都是调用核心层来完成的。

     实际上刚开始看BlogEngine.Net的源代码时我也很难入手,不知道从哪里看起,找不到入口的地方。其实也难怪,官方提供的资料大都是关于使用和开发扩展的,社区里找到的东西也不是自己最想要的。研究了一段时间以后我发现整个BlogEngine.Net都在围绕这BusinessBase这个基类展开,其它的类都是为它提供服务或接收它的消息,例如Provider,Extension等。BusinessBase是所有业务类的基类,里面封装了很多业务类共有的特征。它的子类有:

AuthorProfile:用户的Profile的封装。

Page:这个类实际上是对应着BlogEngine.Net中的一篇静态文章,page和post具体区别不是很重要,感兴趣的朋友可以参照一下官方提供的说明。

Post:在BlogEngine.Net应用最多的一个类,代表作者提交的一篇文章。

Category:文章分类,一篇文章可以属于多个分类,分类之上还可以有父分类。

     下图是他们的继承关系:

     

 图中的IPublishable接口我会在以后的文章中做详细的讲解。

 从BusinessBase的原型

BusinessBase原型

我们可以看出:

1.BusinessBase是一个泛型类。这个泛型设计的很好,Type用来标识具体的子类类型,Key主要是子类对象的唯一ID,这个ID在AuthorProfile是String类型,而在Page等其它类中是Guid类型,所以定义基类时才会采取这种泛型设置。

2.BusinessBase实现的接口也是微软推荐的业务对象应该实现的接口。

IDataErrorInfo用来标识对象内部的错误信息,这个接口的实现主要用来定义某个属性的验证规则。
INotifyPropertyChanged用户通知客户端数据发生了改变。主要是对象内部数据发生改变时,对于一些绑定控件数据的同步更新。
IChangeTracking如果对象内部数据发生改变,它用来完成接收了数据的改变,包括更新数据存储等。
IDisposable这个就不用解释了吧,做.Net都知道。

     从以上我们可以看出凡是BusinessBase的子类对象都具有当修改数据时,通过属性的改变对外发出属性改变的通知,并实现INotifyPropertyChanged来通知绑定控件,实现IChangeTracking来更新数据的存储。我们再看一下源代码发现BlogEngine.Net将所有业务对象的数据验证交给了Validation模型来处理,这一点运用的很巧妙,统一了验证模型,我很推荐。

Validation

     那么,BusinessBase的子类都需要做什么呢?它们需要重写数据存储的操作方法(通过Provider的调用完成,主要是DataSelect等),这也是面向接口编程所提倡的。对于内部数据的处理BusinessBase使用了IsNew,IsChanged和IsDeleted统一了编程模型,并定义了一个SaveAction枚举来实现统一的处理与通知消息的封装。

     BusinessBase提供了两个事件在内部数据进行存储前后触发——保存前事件和保存后事件,用来给外部提供访问点,有点类似于Asp.Net的管道事件的东西,不过这些事件都是类的事件,并非属于某个对象。这样外部可以对于保存前的事件进行处理,也可以对于保存后的事件处理,这种模型很有利于扩展。例如我们可以很轻松的纪录业务日志,而且纪录的很统一。此外,BusinessBase重写了相等的方法或操作符,用于两个对象的排序和比较,这都是业务对象所共有的特性。

     那么,对于派生类从这个基类继承之后我们要实现什么呢,无非就是自己的数据存储方法,数据检验规则,自己的ID类型,还需要大家注意的就是每个派生类都提供了一个静态属性用来提取整个对象列表,还可以根据具体的查询信息获得对象列表,不过他们都属于类的方法,也就是静态方法。对于结构和关系比较复杂的派生类,例如Post包含了Comment列表及其相应的方法用来表示和操作对象之间的关系。此外基类的MarkOld方法用于标识这个对象已经经过了处理,不需要在处理了,子类可以重写这个方法用于提供自己的实现。其余的一些方法或属性都是具体类中所需要的,这个很好分析。

     例如,对于客户端程序我们只要这样就可以完成数据操作:

添加文章:

Code

修改文章:

Code

删除文章:

Code

      很明显,BlogEngine.Net采用了面向对象的设计方法,事件和继承得到了很广泛的应用,此外它更好的运用了.Net平台自身提供的模型解决问题,例如Provider模型,一些接口规范等。此外我们可以看到,BlogEngine.Net在内存中有很多对象,以空间换取时间的处理方法在这样的系统中还是比较可靠的。

     做一些总结是很有必要的。

     上一篇:BlogEngine.Net架构与源代码分析系列part1:开篇介绍

     下一篇:BlogEngine.Net架构与源代码分析系列part3:数据存储——基于Provider模式的实现

 

     返回到目录

【作者】:GUO Xingwang
【来源】:http://thriving-country.cnblogs.com/ 
     本文版权归作者和博客园共同所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
Add your comment

28 条回复

  1. #1楼 OctoberOne      2008-11-04 22:30
    沙发!顶!!!支持!!
     回复 引用 查看   
  2. #2楼 yibin      2008-11-04 22:39
    分析得不错
     回复 引用 查看   
  3. #3楼 真见      2008-11-04 22:45
    学习
     回复 引用 查看   
  4. #4楼 Q.Lee.lulu      2008-11-05 08:30
    这里应该说说业务对象的状态的维护....
     回复 引用 查看   
  5. #5楼[楼主] GUO Xingwang      2008-11-05 09:09
    @Q.Lee.lulu
    这里面我觉得就是业务对象的状态维护了,包括属性修改时的处理,填充对象等!业务对象的状态的维护没有说得很清楚!
     回复 引用 查看   
  6. #6楼 Kevin-moon      2008-11-05 10:52
    支持下
     回复 引用 查看   
  7. #7楼 T2噬菌体      2008-11-05 11:54
    支持!!!
     回复 引用 查看   
  8. #8楼 Sam Lin      2008-11-05 14:20
    学习+跟进
     回复 引用 查看   
  9. #9楼 Aspgreener      2008-11-05 14:50
    小事一箩筐,让你笑翻了^_^ http://www.xiaoshiyiluokuang.com
     回复 引用 查看   
  10. #10楼 sunbird69      2008-11-05 16:54
    支持
     回复 引用 查看   
  11. #11楼 书生多命贱      2008-11-06 16:56
    "BusinessBase提供了两个事件在内部数据进行存储前后触发——保存前事件和保存后事件"
    我不太了解事件,楼主能说下BusinessBase 里的例子吗?
     回复 引用 查看   
  12. #12楼[楼主] GUO Xingwang      2008-11-06 17:13
    @书生多命贱
    就是
    public static event EventHandler<SavedEventArgs> Saved;
    public static event EventHandler<SavedEventArgs> Saving;
    这两个东西,当BusinessBase的子类的对象在保存前后分别触发,那么外部就可以监听并提供处理程序,例如:
    Post.Saved += new EventHandler<SavedEventArgs>(Post_Saved);

    private static void Post_Saved(object sender, SavedEventArgs e)
    {
    // TODO Something
    }
     回复 引用 查看   
  13. #13楼 书生多命贱      2008-11-06 22:13
    多谢楼主!!!多谢!!!
     回复 引用 查看   
  14. #14楼 lulu      2008-11-08 15:53
    IDataErrorInfo, INotifyPropertyChanged, IChangeTracking 这三个接口具体有什么用处,在代码中应如何使用,能给个demo吗
     回复 引用 查看   
  15. #15楼[楼主] GUO Xingwang      2008-11-08 21:52
    @lulu
    对于IDataErrorInfo, INotifyPropertyChanged, IChangeTracking 都在System.ComponentModel下,属于构建业务系统中对象要实现的接口。既然是接口,我想肯定是某个地方调用的统一一个标准,这个实际上完成了绑定控件,DataSource等的一些自动处理,比如有以下情景:
    我们使用WinForm做个一个显示客户信息的界面,使用了下面代码:
    Customer实现了这三个接口
    aDataSource关联了某个List<Customer>
    aDataGrid.DataSource=aDataSource;
    那么当List<Customer>中的某个Customer信息出现错误时aDataGrid就会按照
    IDataErrorInfo接口的方式显示,当修改aDataGrid时,会按照IChangeTracking的规则标识修改数据成功等!当程序的其他部分修改了Customer时,那么aDataSource会使用INotifyPropertyChanged获得这个通知,同时更新aDataGrid。
     回复 引用 查看   
  16. #16楼 老夫子系      2008-11-13 15:58
    IDataErrorInfo, INotifyPropertyChanged, IChangeTracking 这三个接口具体有什么用处,在代码中应如何使用?
    上面楼主说的是WinForm中,那在WebForm中,能启到什么作用呢?
     回复 引用 查看   
  17. #17楼[楼主] GUO Xingwang      2008-11-13 16:18
    @老夫子系
    从目前来看WebForm中似乎真的没有什么太大的作用,但是这是业务类型应该实现的接口,也是微软推荐的,BlogEngine.Net的这中业务对象也不一定必须要在WebForm或WinForm下能运行,所以我觉得也没必要说它有很重要的作用我们才去加入!
     回复 引用 查看   
  18. #18楼 老夫子系      2008-11-13 17:00
    嗯。楼主说的对!
     回复 引用 查看   
  19. #19楼 天启      2008-12-16 21:07
    文章写得很不错,全文收藏至OneNote,经典评论也收藏了!
     回复 引用 查看   
  20. #20楼 天启      2008-12-16 21:09
    --引用--------------------------------------------------
    GUO Xingwang: @书生多命贱
    就是
    public static event EventHandler&lt;SavedEventArgs&gt; Saved;
    public static event EventHandler&lt;SavedEventArgs&gt; Saving;
    这两个东西,当BusinessBase的子类的对象在保存前后分别触发,那么外部就可以监听并提供处理程序,例如:
    Post.Saved += new EventHandler&lt;SavedEventArgs&gt;(Post_Saved);

    private static void Post_Saved(object sender, SavedEventArgs e)
    {
    // TODO Something
    }

    --------------------------------------------------------
    Z-blog(一个优秀的Asp博客程序)的插件就是采用这种机制,当然,具体实现方式不一样。
     回复 引用 查看   
  21. #21楼 xuefly      2009-01-08 18:09
    Code
    嘿嘿 发现BlogEngine开发组的一个笔误:第9行和第11行有问题,还没往下面看,不过这段代码隐含意思DataSelect(KEY id)可以返回null,如果instance在第8行被指向了null则第9行肯定要出错了——“未将对象引用设置到对象的实例”。笔误^_^
     回复 引用 查看   
  22. #22楼 yqpeng      2009-04-03 14:53
    这个BusinessBase基类是参照CSLA.net中做的,还有BlogEngine很多地方参照了CSLA.net框架
     回复 引用 查看   
  23. #23楼 红民      2009-09-10 09:14
    @yqpeng
    22楼说的对,刚才分析代码发现和csla.net很相像,一搜索原来确实这样。楼主的文章也不错,有时间了也看看。
     回复 引用 查看   
  24. #24楼 三桂      2009-09-17 12:50
    继续下一篇:)
     回复 引用 查看   
  25. #25楼 SanLiKingDom      2011-01-13 09:38
    这个必须得顶啊,虽然现在有了2.0版本了,还是跟着楼主的节奏学习一下,感谢楼主的无私分享
     回复 引用 查看   
  26. #26楼 Leo-Li      2011-07-04 22:30
    挺不错的,跟着楼主的思路,看代码都轻松多了,总有点拨开乌云的感觉,虽然现在都2011年了,咱也得留言顶下下.........
     回复 引用 查看   
  27. #27楼 cy凌云      2011-09-26 16:14
    @OctoberOne
    恩,very good!
     回复 引用 查看   
  28. #28楼 cy凌云      2011-09-26 16:16
    引用Kevin-moon:支持下

    引用GUO Xingwang:@书生多命贱
    <br/>就是
    <br/>public static event EventHandler<SavedEventArgs> Saved;
    <br/>public static event EventHandler<SavedEventArgs> Saving;
    <br/>这两个东西,当BusinessBase的子类的对象在保存前后分别触发,那么外部就可以监听并提供处理程序,例如:
    <br/>Post.Saved += new EventHandler<SavedEventArgs>(Post_Saved);
    <br/>
    <br/>private stat...
     回复 引用 查看