Lost !

-----hard working for the furture.

导航

统计

使用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

posted on   失落''80  阅读(512)  评论(0编辑  收藏  举报

编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
点击右上角即可分享
微信分享提示