冬Blog

醉心技术、醉心生活
  博客园  :: 首页  :: 新随笔  :: 订阅 订阅  :: 管理

上一篇中,我们分析了实体类的基类Entity,这一篇中,我们就分析一下基于该类的实体类。

Business

nest_files每一个实体类都会有两个文件组成,我们以BlogClass为例,该类包含两个文件:BlogClass.cs和BlogClass.designer.cs,这非常类似VS自己生成的代码,更方便的是,VS还会自动把这两个文件折叠起来,如图。

这两个文件中,BlogClass.designer.cs包含所有的生成代码:成员、属性等,而BlogClass.cs则只包含一个类的定义,供我们填写代码使用。

BlogClass.designer.cs的代码如下。

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Data.Linq;
   4:  using System.Linq;
   5:  using System.Text;
   6:   
   7:  using DongBlog.Common;
   8:   
   9:  namespace DongBlog.Business.Blogs
  10:  {
  11:      /// <summary>
  12:      /// 日志分类
  13:      /// </summary>
  14:      public partial class BlogClass
  15:      {
  16:          #region ID和时间戳
  17:   
  18:          private int _ID = NEW_ENTITY_ID;
  19:          private byte[] _TimeStamp = new byte[] { };
  20:   
  21:          /// <summary>
  22:          /// 取得ID
  23:          /// </summary>
  24:          public override int ID
  25:          {
  26:              get { return _ID; }
  27:          }
  28:          /// <summary>
  29:          /// 取得时间戳
  30:          /// </summary>
  31:          public override byte[] TimeStamp
  32:          {
  33:              get { return _TimeStamp; }
  34:          }
  35:   
  36:          #endregion
  37:   
  38:          #region 成员
  39:   
  40:          private string _Name;
  41:          private string _Description;
  42:   
  43:          #endregion
  44:   
  45:          #region 属性
  46:   
  47:          /// <summary>
  48:          /// 取得或设置名称
  49:          /// </summary>
  50:          public string Name
  51:          {
  52:              get { return _Name; }
  53:              set { _Name = value; }
  54:          }
  55:          /// <summary>
  56:          /// 取得或设置描述
  57:          /// </summary>
  58:          public string Description
  59:          {
  60:              get { return _Description; }
  61:              set { _Description = value; }
  62:          }
  63:   
  64:          #endregion
  65:      }
  66:  }

从代码里可以看到,完全就是对实体XML的翻译,需要特别指出的是,其中实现了Entity定义的ID和TimeStamp这两个抽象属性。

BlogClass.cs的代码如下:

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Data.Linq;
   4:  using System.Linq;
   5:  using System.Text;
   6:   
   7:  using DongBlog.Common;
   8:   
   9:  namespace DongBlog.Business.Blogs
  10:  {
  11:      /// <summary>
  12:      /// 日志分类
  13:      /// </summary>
  14:      public partial class BlogClass : Entity<BlogClass>
  15:      {
  16:      }
  17:   
  18:      /// <summary>
  19:      /// 日志分类的业务外观 
  20:      /// </summary>
  21:      public static class BlogClassExtension
  22:      {
  23:      }
  24:  }
 

呃……这个更简单了,完全就是个空的。因为它的用处就是让我们填写自己的代码,现在假设我们有这么一个功能“设置日志分类名称时,如果分类的描述为空,则将分类的描述设置为其名称”,那么我们可以这么干:从BlogClass.desinger.cs文件中,将Name属性的定义Ctrl+X,Ctrl+V过来,然后改改,修改后的代码如下:

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Data.Linq;
   4:  using System.Linq;
   5:  using System.Text;
   6:   
   7:  using DongBlog.Common;
   8:   
   9:  namespace DongBlog.Business.Blogs
  10:  {
  11:      /// <summary>
  12:      /// 日志分类
  13:      /// </summary>
  14:      public partial class BlogClass : Entity<BlogClass>
  15:      {
  16:          /// <summary>
  17:          /// 取得或设置名称
  18:          /// </summary>
  19:          public string Name
  20:          {
  21:              get { return _Name; }
  22:              set
  23:              {
  24:                  _Name = value;
  25:   
  26:                  if (string.IsNullOrEmpty(_Description))
  27:                      _Description = value;
  28:              }
  29:          }
  30:      }
  31:   
  32:      /// <summary>
  33:      /// 日志分类的业务外观 
  34:      /// </summary>
  35:      public static class BlogClassExtension
  36:      {
  37:      }
  38:  }

这个代码很好理解,值得一提的是,当描述实体的XML修改后,重新生成实体代码时,代码生成器会判断BlogClass.cs中是否包含了Name属性,如果包含了该属性,则在BlogClass.designer.cs中,就不会再生成了。这是自己写代码生成而不用通用代码生成器的好处——我的地盘我做主。:)

另外一个和实体相关的类是Extension类,顾名思义,该类包含的都是对已有类型的扩展方法,利用C#3.0的扩展方法,可以写出非常优雅的代码。例如,我们需要查询某一个分类的所有Blog,给出的是该分类的ID,我们就可以在BlogExtension类中这么写(代码位于\DongBlog.Business\Blogs\Blog.cs):

   1:  /// <summary>
   2:  /// 日志的业务外观 
   3:  /// </summary>
   4:  public static class BlogExtension
   5:  {
   6:      /// <summary>
   7:      /// 根据日志分类取得日志
   8:      /// </summary>
   9:      /// <param name="query">日志查询</param>
  10:      /// <param name="blogClassID">日志分类ID</param>
  11:      /// <returns>该分类下的日志</returns>
  12:      public static List<Blog> GetBlogsByClassID(this IQueryable<Blog> query, int blogClassID)
  13:      {
  14:          if (query == null)
  15:              throw new ArgumentNullException("query");
  16:   
  17:          return query
  18:              .Where(b => b.BlogClassID == blogClassID)
  19:              .OrderByDescending(b => b.UpdateDateTime)
  20:              .ToList();
  21:      }
  22:  }

上面的查询方法使用Linq实现的,我们还顺手做了一个根据更新时间的排序。有了这个方法,我们以后就可以用这样的方式取得某一个分类的Blog:database.GetDataAccess<Blog>().GetBlogByClassID(1);该方式以后还会进一步简化为:database.Blogs.GetBlogByClassID(1)。

最后说明一点,以上代码都是可以测试的,因为我们所有的方法都是使用接口作为参数的,所以我们可以用脚手架(Mock类)伪实现相应的接口,以便对不同输入和环境下的代码进行自动化测试,具体方法就不展开了。

下一篇文章我们将分析数据访问的设计与实现。

代码下载