所有需要进行数据访问的操作都须依赖Model提供的服务。简单地说,Model负责通过数据库、AD(Active Directory)、Web Service及其他方式取得数据,或者将用户数据输入的数据保存到数据库、AD、Web Service等中。

一、Model的任务

    Model的独立性很高,所以VS方案中有多个要开发的项目,一般会将Model独立成一个项目,好让Model项目在不同的项目之间共享。

二、创建基础数据模型

    使用MVC开发MVC项目时,不妨好好利用VS开发工具带来的便利。尤其是开发繁琐的Model任务时,若使用内置化开发工具,能有效提升整体开发效率。使用Entity Framwork开发数据模型的界面,通过"模型浏览器"窗口,可以方便地浏览所有数据库与实体对象的对应,并能通过可拖拽的可视化工具开发与定义模型之间的关系。

  2.1 用LINQ to SQL自动创建数据模型

    Step01:选择mvc项目下的"Models"文件夹,单击鼠标右键,在弹出的快捷菜单中一次选取"添加"—"新建项目"选项。

    Step02:在"添加新项目"窗口中选择"数据"模板,再选择"LIBQ to SQL类"选项,输入文件名称,创建dbml文件。

    Step03:在"视图"下拉列表中选择"服务器资源管理器"选项,并在"服务器资源管理器"窗口中新建数据连接。选选中"数据连接"选项,单击鼠标右键,在弹出的快捷惨淡中依次选择"数据连接"—"加入数据连接"选项。

    Step04:将定义好的数据库连接打开,并将要运用子啊MVC的数据表拖拽到DBML的设计视图。

    Step05:VS2010会自动产生所有与SQL Server数据库对应的实体对象。

    基本上已经创建完所有MVC需要的数据模型了。在创建完的后自动生成的类文件中,可以看到许多通过VS自动产生的类,这些文件内容是所有与数据库表格对应的.NET类。

  2.2 用Entity Framwork自动创建数据模型

    即选中MVC项目的Models文件夹,单击鼠标右键,选择"添加"—"新建项目"选项。然后选择"数据"模板中的ADO.NET 实体数据模型。然后按照步骤和实际表格情况选择。(略)

    如果希望Entity Framework能正确处理默认值字段,就必须手动编辑edmx文件的xml代码,并将这些字段逐一修正。

    Step01:在"解决方案..."选择"Model1.edmx"文件并单击鼠标右键,选择打开方式,用不同的打开方式打开文件。

    Step02:选择"XML(文字)编辑器"打开。

    Step03:打开后,找到SSDL程序段,并找到每一个EntityType段。

    Step04:在含有默认值的字段<Property>标签中加上"StoreGeneratePattern="Computed""

  2.3 手动创建数据模型

    在MVC中手动创建模型,其实跟创建一般的C#类没有什么不同,范例如下:

1     Public class MessageViewModel
2     {
3          public int TotalPage { get; set; }
4          public int TotalPage { get; set; }
5          public IEnumerable<Message> Messages { get; set; }
6     }

三、扩充基础数据模型

TIP :虽然通过工具产生的数据模型类别还是可以手动修改,但是通常不会去修改这些内容,否则下次再通过工具修改数据模型定义时,又要重新生成程序代码,并覆盖我们先前自定义的部分。

  3.1 定义Model的Metadata

    Metadata用于定义数据模型的相关属性,例如显示名称、数据长度及数据格式验证等。

    System.ComponentModel.DataAnnotatis命名空间的类提供了验证属性,如图:

属性名称

描  述

StringLength

字符串字段所允许的最大长度

Required

必填字段

RegularExpression

字段内容必须符合所指定的规则表达式

Range

数字字段必须符合的范围

    以下建一个简单的会员数据模型类范例。

    利用System.ComponentModel.DataAnnotatis命名空间为每个字段加上批注。每个会员都有姓名、E-mail及表情图3个字段:姓名必填,用Required属性;E-mail必须符合正确格式,用Regular Expression属性验证;表情图需从限定的3个图示中挑选一个,在数据库里以int格式来进行定义。所有用Range属性验证只能为1~3的整数。范例代码如下:

1    public class Member
2     {
3         [Required]
4         public string Name{get;set;}
5         [RegularExpression(@"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)| (([\w-]+\.)+))([a-zA-Z]{2,4})$",ErrorMessage="请输入正确的Email格式")]
6         public string Email{get;set;}
7         [Range(1,3,ErrorMessage="请选择代表图示")]
8         public Int32 EmotionIcon{get;set;}
9     }

    上述定义方式不适用于LINQ to SQL环境。因为在LINQ to SQL环境所有数据模型的类都由VS自动产生。不会去手动修改生成的代码,而是通过分类的方式来延伸这个类的辅助信息。部分类代码:

1     namespace MvcGuestbook.Models
2     {
3          public partial class Member
4          {
5          }
6     }

    在部分类中直接写上同名的属性(Property)时,必须通过DataAnnotations命名空间提供的MetadataType属性来克服这个限制,这样才能在不分类中加上个字段的属性(Attribute)。

TIP :由于只有方法、类、结构或接口可以被声明为partial,因此不能再部分类中为现有的属性(Property)应用额外属性(attribute)。

    这种特殊写法可以参考以下范例,要先在不分类上应用一个MetadataType属性,并导入一个用来设定Metadata的对象类。这个Metadata的对象类可以直接在数据模型部分类里声明,并设定为私用类(Private Class),示例如下:

    最后,完成的程序代码如下:

View Code
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Web;
 5 using System.ComponentModel.DataAnnotations;
 6 using System.ComponentModel;
 7 
 8 namespace MvcApplication1.Models
 9 {
10      [MetadataType(typeof(MemberMetadata))]
11     public partial class Member
12     {
13         private class MemberMetadata
14         {
15             public int ID { get; set; }
16 
17             [Required(ErrorMessage = "请输入账号")]
18             [StringLength(50, ErrorMessage = "请勿输入超过50个字")]
19             [DisplayName("账号")]
20             public string Account { get; set; }
21 
22             [Required(ErrorMessage = "请输入密码")]
23             [StringLength(50, ErrorMessage = "请勿输入超过50个字")]
24             [DisplayName("密码")]
25             public string Password { get; set; }
26 
27             [Required(ErrorMessage = "请输入昵称")]
28             [StringLength(50, ErrorMessage = "请勿输入超过50个字")]
29             [DisplayName("昵称")]
30             public string NickName { get; set; }
31 
32             [Required(ErrorMessage = "请输入中文名")]
33             [StringLength(50, ErrorMessage = "请勿输入超过50个字")]
34             [DisplayName("中文姓名")]
35             public string CHName { get; set; }
36 
37             [Required(ErrorMessage = "请输入Email")]
38             [StringLength(255, ErrorMessage = "请勿输入超过255个字")]
39             [DisplayName("Email")]
40             [RegularExpression(@"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)| (([\w-]+\.)+))([a-zA-Z]{2,4})$", ErrorMessage = "请输入正确的Email格式")]
41             public string Email { get; set; }
42 
43             public bool IsAdmin { get; set; }
44 
45             [Required(ErrorMessage = "请选择代表图标")]
46             [Range(1, 3, ErrorMessage = "输入的值必须介于1到3之间")]
47             [DisplayName("代表图标")]
48             public int EmotionIcon { get; set; }
49 
50             public DateTime CreateTime { get; set; }
51         }
52     }
53 }

NOTE :采用上述方法只是为了使用MetadataType属性来扩充个字段的属性(Attribute),而对于这些MetadataType属性中所定义的属性(Property),其所定义的类并不重要,重要的是这些属性(Property)名称要与数据模型类中定义的属性(Property)名称一样—就算你将所有字段都定义成object类也没关系。

  3.2 自定义Metadata属性

    前面已经用RegularExpression
属性来验证E-mail字段,但如果有大量使用需要,程序代码就会显得有些复杂,可以视需要来自定义验证属性。以验证E-mail属性。以E-mail字段为例,可以继承RegularExpressionAttribute类,并实现另一个验证属性,示例如下:

1    public class EmailAttribute : RegularExpressionAttribute
2     {
3         public EmailAttribute() :
4             base(@"^([\w-\.]+)@((\[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.))([a-zA-Z]{2,4})$") { }
5     }

    如此一来,就可以使用Email属性来声明字段的验证规则了,示例如下:

1      [Email(ErrorMessage = "请输入正确的Email.")]
2       public string Email { get; set; }

四、实现库模式

    库模式(Repository pattern)是专门用于访问数据的一种样式(Pattern),其设计方式很简繁:首先定义接口(Interface),看你希望将什么样的接口提供给访问数据库的类(如Controller),接着再实现该接口。接口与类(Class)的切割将有助于开发单元测试,也可让测试驱动开发(Test Driven Development,TDD)进行得较为顺利。以会员数据为例来说明如何实现库样式,主要有3个步骤。

    Step01:创建接口,定义可操作的方法。

    Step02:创建类,实现接口。

    Step03:在Controller中以接口来操作这个类。

    范例:会员数据

    Step01:创建ImemberRepository接口,并将其作为访问会员数据接口,示例如下:

1     public interface IMemberRepository
2     {
3         IQueryable<Member> FindAllMembers();
4         Member GetMemberById(int id);
5         Member GetMemberByAccount(string account);
6         void Add(Member Member);
7         bool Delete(int id);
8         void Save();
9     }

    Step02:实现IMemberRepository接口,示例如下。

 1     public class MemberRepository : IMemberRepository
 2     {
 3         protected MvcApplication1.Models.GuestbookEntities db = new Models.GuestbookEntities();
 4         //protected MvcGuestBookDataContext db = new MvcGuestBookDataContext();
 5         IQueryable<Member> IMemberRepository.FindAllMembers()
 6         {
 7             return db.Member;
 8         }
 9         Member IMemberRepository.GetMemberById(int id)
10         {
11             return db.Member.Where(p => p.ID == id).FirstOrDefault();
12         }
13         Member IMemberRepository.GetMemberByAccount(string account)
14         {
15             return db.Member.Where(p => p.Account == account).FirstOrDefault();
16         }
17         void IMemberRepository.Add(Member Member)
18         {
19             db.Member.InsertOnSubmit(Member);
20         }
21         bool IMemberRepository.Delete(int id)
22         {
23             var m = db.Member.FirstOrDefault(p => p.ID == id);
24             if (m != null)
25             {
26                 db.Member.DeleteOnSubmit(m);
27                 return true;
28             }
29             else
30             {
31                 return false;
32             }
33         }
34         void IMemberRepository.Save()
35         {
36             db.SubmitChanges();
37         }
38     }

    Step03 :在Controller访问数据时,可定义一个IMemberRepository接口类的对象,让该Controller能使用这些对象来访问数据,并新建Controller类的构造符,让该Controller类可以先创建MemberRepository类的实体,程序范例如下。

 1    public class MemberController : Controller
 2     {
 3         IMemberRepository _r;
 4         public MemberController()
 5             : this(new MemberRepository())
 6         { }
 7         public MemberController(IMemberRepository r)
 8         {
 9             _r = r;
10         }
11     }
posted on 2012-11-26 10:50  Eleanore Lee  阅读(5121)  评论(0编辑  收藏  举报