使用ASP.NET MVC 3、Razor和Entity Framework Code First技术开发Web应用程序 – Part 1
ASP.NET MVC 3 是一个框架,它通过利用模型视图控制器 (MVC) 模式来开发可测试性和可维护性很高的 Web 应用程序。该框架鼓励开发人员明确地分离应用程序各职责之间的任务 – 使用视图的 UI 逻辑、使用控制器的用户输入处理以及使用模型的域逻辑。通过使用诸如测试驱动的开发 (TDD) 等技术轻松测试 ASP.NET MVC 应用程序。
本文演示如何使用ASP.NET MVC 3、Razor和 EF Code First 技术开发web应用程序,其中包括使用Unity 2.0依赖注入、Repository模式等等。可访问http://efmvc.codeplex.com 下载本范例应用程序源码。

定义业务Model
为范例web应用程序创建业务Model。
Category 类
|
1
2
3
4
5
6
7
8
9
|
public class Category{public int CategoryId { get; set; }[Required(ErrorMessage = "Name Required")][StringLength(25, ErrorMessage = "Must be less than 25 characters")]public string Name { get; set;}public string Description { get; set; }public virtual ICollection<Expense> Expenses { get; set; }} |
Expense 类
|
1
2
3
4
5
6
7
8
9
10
|
public class Expense{public int ExpenseId { get; set; }public string Transaction { get; set; }public DateTime Date { get; set; }public double Amount { get; set; }public int CategoryId { get; set; }public virtual Category Category { get; set; }} |
上面定义了2个业务实体 – Category和Expense。每一个Category包含一个Expense事务列表,每一个Expense事务应该属于一个Category。我 们将开发一个Expense跟踪应用程序,并更新Expense事务为特定的Category。本文将专注于Category实体的CRUD操作,下一篇 将专注于Expense 实体。
上述实体是非常简单的POCO类(Plain Old CLR Object),引用了System.ComponentModel.DataAnnotations命名空间,在Category上类标注了验证属性。
using System.ComponentModel.DataAnnotations;
为Entity Framework创建Context类
上面创建了业务Model,为了应用Entity Framework Code First,下面创建一个Context类。第一步需要下载EF Code First,并添加对EntityFramework.dll 的引用。另外,也可使用NuGet下载并添加对EF Code First的引用。
|
1
2
3
4
5
6
|
public class MyFinanceContext : DbContext{public MyFinanceContext() : base("MyFinance") { }public DbSet<Category> Categories { get; set; }public DbSet<Expense> Expenses { get; set; }} |
MyFinanceContext类继承DbContext,可连接Model类到数据库。MyFinanceContext 类使用DbSet<TEntity> (TEntity 是一个POCO类),分别映射Category和Expense 类到数据库表Categories和Expenses。当在第一次运行应用程序时,将自动创建数据库。EF Code First在Web.config 或 app.config 中寻找和DbContext类相同名称的连接字符串。如没有找到对应的connection string,则自动在本地SQL Express创建和DbContext类名称相同的数据库,也可在DbContext类的构造函数中定义数据库的名称。
基于EF Code First的Repository模式
上面创建了Model 类和DbContext类,这里创建数据持久化类的Repository模式。如不了解Repository模式,可参考Martin Fowler关于Repository模式的文章或者《Repository 设计模式》。
创建IRepository接口
|
1
2
3
4
5
6
7
8
9
|
public interface IRepository<T> where T : class{void Add(T entity);void Delete(T entity);T GetById(long Id);T Get(Func<T, Boolean> where);IEnumerable<T> GetAll();IEnumerable<T> GetMany(Func<T, bool> where);} |
RepositoryBase基类
|
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
35
36
37
38
39
40
41
42
43
44
45
46
47
|
public abstract class RepositoryBase<T> where T : class{private MyFinanceContext dataContext;private readonly IDbSet<T> dbset;protected RepositoryBase(IDatabaseFactory databaseFactory){DatabaseFactory = databaseFactory;dbset = DataContext.Set<T>();}protected IDatabaseFactory DatabaseFactory{get; private set;}protected MyFinanceContext DataContext{get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }}public virtual void Add(T entity){dbset.Add(entity);}public virtual void Delete(T entity){dbset.Remove(entity);}public virtual T GetById(long id){return dbset.Find(id);}public virtual IEnumerable<T> GetAll(){return dbset.ToList();}public virtual IEnumerable<T> GetMany(Func<T, bool> where){return dbset.Where(where).ToList();}public T Get(Func<T, Boolean> where){return dbset.Where(where).FirstOrDefault<T>();}} |
DatabaseFactory 类
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class DatabaseFactory : Disposable, IDatabaseFactory{private MyFinanceContext dataContext;public MyFinanceContext Get(){return dataContext ?? (dataContext = new MyFinanceContext());}protected override void DisposeCore(){if (dataContext != null)dataContext.Dispose();}} |
上述Repository相关类仅仅是一个基本的版本,在后面需进行重构。
Unit of Work 模式
如对Unit of Work 模式不熟悉,可参考Fowler的文章Unit of Work或者《Unit Of Work模式》。Unit of Work 模式负责维护业务事务影响的对象列表,并协调更新和同步问题。
创建一个类,处理Unit of Work:
|
1
2
3
4
|
public interface IUnitOfWork{void Commit();} |
UnitofWork类
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public class UnitOfWork : IUnitOfWork{private readonly IDatabaseFactory databaseFactory;private MyFinanceContext dataContext;public UnitOfWork(IDatabaseFactory databaseFactory){this.databaseFactory = databaseFactory;}protected MyFinanceContext DataContext{get { return dataContext ?? (dataContext = databaseFactory.Get()); }}public void Commit(){DataContext.Commit();}} |
UnitOfWork的Commit方法将调用MyFinanceContext类的Commit方法,并执行DbContext类的SaveChanges方法。
Category 的Repository类
本文将专注Category实体类的持久化操作。继承RepositoryBase<T> 基类,创建CategoryRepository 类负责Category 实体类的CRUD操作。
|
1
2
3
4
5
6
7
8
9
10
11
|
public class CategoryRepository: RepositoryBase<Category>, ICategoryRepository{public CategoryRepository(IDatabaseFactory databaseFactory): base(databaseFactory){}}public interface ICategoryRepository : IRepository<Category>{} |
如需要添加RepositoryBase 基类之外的其他方法,可直接在CategoryRepository类中定义。
使用Unity 2.0实现依赖注入
如对依赖注入或Unity不熟悉,可参考文章《在ASP.NET MVC 项目使用Unity 2.0实现依赖注入》,这里不再重复描述。
开发ASP.NET MVC 3 Web应用程序
前面已经创建了业务Model类、Repository类,并配置好了Unity容器,使用依赖注入。现在,创建Controller类和View视图,实现对Category实体的CRUD操作。
创建Category Controller类
|
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
public class CategoryController : Controller{private readonly ICategoryRepository categoryRepository;private readonly IUnitOfWork unitOfWork;public CategoryController(ICategoryRepository categoryRepository, IUnitOfWork unitOfWork){this.categoryRepository = categoryRepository;this.unitOfWork = unitOfWork;}public ActionResult Index(){var categories = categoryRepository.All();return View(categories);}[HttpGet]public ActionResult Edit(int id){var category = categoryRepository.GetById(id);return View(category);}[HttpPost]public ActionResult Edit(int id, FormCollection collection){var category = categoryRepository.GetById(id);if (TryUpdateModel(category)){unitOfWork.Commit();return RedirectToAction("Index");}else return View(category);}[HttpGet]public ActionResult Create(){var category = new Category();return View(category);}[HttpPost]public ActionResult Create(Category category){if (!ModelState.IsValid){return View("Create", category);}categoryRepository.Add(category);unitOfWork.Commit();return RedirectToAction("Index");}[HttpPost]public ActionResult Delete(int id){var category = categoryRepository.GetById(id);categoryRepository.Delete(category);unitOfWork.Commit();var categories = categoryRepository.All();return PartialView("CategoryList", categories);}} |
创建Razor视图
现在创建Razor 视图,首先创建Partial视图 – CategoryList.cshtml用来显示category列表信息,并提供Edit和Delete 操作的链接。
|
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
|
@using MyFinance.Helpers;@using MyFinance.Domain;@model IEnumerable<Category><table><tr><th>Actions</th><th>Name</th><th>Description</th></tr>@foreach (var item in Model) {<tr><td>@Html.ActionLink("Edit", "Edit",new { id = item.CategoryId })@Ajax.ActionLink("Delete", "Delete", new { id = item.CategoryId }, new AjaxOptions { Confirm = "Delete Expense?", HttpMethod = "Post", UpdateTargetId = "divCategoryList" })</td><td>@item.Name</td><td>@item.Description</td></tr>}</table><p>@Html.ActionLink("Create New", "Create")</p> |
Delete链接使用Ajax.ActionLink 提供Ajax 功能,Ajax异步请求CategoryController类的Delete Action方法。在删除记录后,Delete Action方法将返回Partial 视图 – CategoryList。
创建Index视图,其中包含CategoryList 部分视图
|
1
2
3
4
5
6
7
8
9
|
@model IEnumerable<MyFinance.Domain.Category>@{ViewBag.Title = "Index";}<h2>Category List</h2><script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script><div id="divCategoryList">@Html.Partial("CategoryList", Model)</div> |
其中使用Html.Partial 辅助方法调用Partial视图,下面创建新增和更新功能的视图页面。这两个视图有相同的用户界面,用来输入Category信息。因此,为 Category信息创建一个EditorTemplate,该EditorTemplate名称需要和实体对象名称保持一致,这样可在视图页面使用 @Html.EditorFor(model=>model) 引用。
创建Category.cshtml 视图
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@model MyFinance.Domain.Category<div>@Html.LabelFor(model => model.Name)</div><div>@Html.EditorFor(model => model.Name)@Html.ValidationMessageFor(model => model.Name)</div><div>@Html.LabelFor(model => model.Description)</div><div>@Html.EditorFor(model => model.Description)@Html.ValidationMessageFor(model => model.Description)</div> |
下面创建新增Category信息的视图页面
|
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
|
@model MyFinance.Domain.Category@{ViewBag.Title = "Save";}<h2>Create</h2><script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script><script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>@using (Html.BeginForm()) {@Html.ValidationSummary(true)<fieldset><legend>Category</legend>@Html.EditorFor(model => model)<p><input type="submit" value="Create" /></p></fieldset>}<div>@Html.ActionLink("Back to List", "Index")</div> |
ViewStart 文件
在Razor视图中,我们在Views目录下添加一个名称为 _viewtart.cshtml文件,该文件将在Views目录下所有视图文件共享。如下为_viewstart.cshtml 文件,为所有视图设置页面布局。
@{
Layout = “~/Views/Shared/_Layout.cshtml”;
}
总 结
本文使用ASP.NET MVC 3 和 EF Code First技术创建了一个简单的Web应用程序,其中讨论了 ASP.NET MVC 3、Razor、EF Code First、Unity 2、Repository模式和Unit of Work 模式等等技术。
英文原文链接:
Developing web apps using ASP.NET MVC 3, Razor and EF Code First – Part 1

浙公网安备 33010602011771号