上一篇中,我们分析了实体类的基类Entity,这一篇中,我们就分析一下基于该类的实体类。
每一个实体类都会有两个文件组成,我们以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类)伪实现相应的接口,以便对不同输入和环境下的代码进行自动化测试,具体方法就不展开了。
下一篇文章我们将分析数据访问的设计与实现。