学习 MVC - 第 5 部分:MVC3 应用程序中的存储库模式与 Entity Framework






4.73/5 (69投票s)
MVC3 应用程序中的存储库模式与 Entity Framework
- 下载 PDFArticle.zip - 2.3 MB
- 下载 LearningMVCWithEntityFrameworkPart3DataBaseFirst.zip - 2.3 MB
- 下载 LearningMVCRepoPattern.zip - 2.3 MB
- 下载 SqlScriptToCreateTable.zip - 537 B
- 下载 SqlScriptToCreateDatabase.zip - 872 B
- 下载 Learning_MVC_Part_5_Repository_Pattern_in_MVC_Application.zip - 6.7 MB
引言
在我们之前的四篇文章中,我们学习了几乎所有关于如何创建 MVC 应用程序以及如何使用该应用程序与数据库进行通信的内容。
在学习 MVC 的第三部分中,我们学习了 MVC 应用程序与数据库之间的通信,使用了 Entity Framework
,所以我将以此为背景。在本文中,我将重点介绍如何在同一个 MVC 应用程序中实现存储库模式,从而朝着开发企业应用程序的架构方法迈出一步。
我们的路线图
提醒您完整的 MVC 学习路线图
- 第一部分:MVC 架构和关注点分离简介。
- 第二部分:从头开始创建 MVC 应用程序并使用 LINQ to SQL 将其连接到数据库。
- 第三部分:借助 EntityFramework DB-First 方法连接 MVC 应用程序。
- 第四部分:借助 EntityFramework Code-First 方法连接 MVC 应用程序。
- 第五部分:在带有 EntityFramework 的 MVC 应用程序中实现 Repository 模式。
- 第六部分:在带有 EntityFramework 的 MVC 应用程序中实现通用 Repository 模式和 Unit Of Work 模式。
先决条件
在开始本文之前,有一些先决条件
- 我们有一个在本文系列第三部分中创建的正在运行的示例应用程序。
- 我们在本地文件系统上有 Entity Framework 4.1 包或 DLL。
- 我们了解 MVC 应用程序是如何创建的。
仓储模式
很少有作者解释概念然后直接跳到模式的实际实现。所以,首先让我们理解什么是存储库模式?为什么我们应该使用它?
简单来说,存储库主要充当应用程序的业务逻辑层和数据访问层之间的中介。有时,将数据访问机制直接暴露给业务逻辑层会很麻烦,这可能会导致访问相似实体数据的代码冗余,或者导致难以测试或理解的代码。为了克服这些问题,并编写接口驱动和测试驱动的代码来访问数据,我们使用存储库模式。存储库向数据源查询数据,然后将数据源中的数据映射到业务实体/领域对象,最后将业务实体中的更改持久化到数据源。根据 MSDN,存储库将业务逻辑与底层数据源或 Web 服务的交互分离开来。数据层和业务层之间的分离有三个好处:
- 它集中了数据逻辑或 Web 服务访问逻辑。
- 它为单元测试提供了替换点。
- 它提供了一个灵活的体系结构,可以随着应用程序整体设计的演变而进行调整。
当我们使用 Entity Framework 时,就像我们在上一个应用程序中所做的那样,我们会在控制器类中调用 Entity Framework 类对象来访问实体类。现在我们可以说,该系统在某种程度上是一个紧耦合的系统。为了克服这种情况,正如我们所讨论的,我们将实现存储库模式。
在存储库中,我们使用 Entity Framework 类编写所有 CRUD 操作的业务逻辑,这不仅可以产生有意义的、可测试的代码,还可以减少我们控制器中的数据访问代码。
创建存储库
创建存储库并不像听起来那么难,一旦你自己实现了它,你就会爱上它。
步骤 1:在 Visual Studio 中打开我们现有的 MVC3 应用程序,该应用程序是我们在第三部分中创建的,用于通过 Entity Framework 与数据库交互。
步骤 2:创建一个名为 Repository 的文件夹,并在该文件夹中添加一个名为 IUserRepository
的接口,该接口继承自 IDisposable
类型接口。
我们将在其中声明用户实体类上 CRUD 操作的方法,您可以根据自己的选择命名方法,但这些名称应该易于理解和遵循。
就像我在下面的接口代码中使用的那样
using System;
using System.Collections.Generic;
namespace LearningMVC.Repository
{
public interface IUserRepository:IDisposable
{
IEnumerable<User> GetUsers();
User GetUserByID(int userId);
void InsertUser(User user);
void DeleteUser(int userId);
void UpdateUser(User user);
void Save();
}
}
我们可以看到每个方法名都表示对 User 实体的特定 CRUD 操作。
User Entity 就是我们在 MVC 第三部分学习时在 Model.tt 类中生成的同一个实体,还记得吗??????
步骤 3:从该接口提取一个类,并将其命名为 UserRepository
。这个 UserRepository
类将实现该接口的所有方法,但会借助 Entity Framework。现在,我们MVCEntities
的 DBContext
类发挥作用了,我们的现有解决方案中已经有了这个类,所以我们不必修改它,只需在 UserRepository
类实现的接口方法中编写业务逻辑即可。
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace LearningMVC.Repository
{
public class UserRepository:IUserRepository
{
private MVCEntities context;
public UserRepository(MVCEntities context)
{
this.context = context;
}
public IEnumerable<User> GetUsers()
{
return context.Users.ToList();
}
public User GetUserByID(int userId)
{
return context.Users.Find(userId);
}
public void InsertUser(User user)
{
context.Users.Add(user);
}
public void DeleteUser(int userId)
{
User user = context.Users.Find(userId);
context.Users.Remove(user);
}
public void UpdateUser(User user)
{
context.Entry(user).State = EntityState.Modified;
}
public void Save()
{
context.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
context.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
在解决方案中
接口
类
90% 的工作现在完成了。现在只剩下在控制器中使用这个存储库了。
解释如何在控制器中调用存储库是不必要的,因为您现在知道如何处理您的控制器,但我们还是过一遍。
步骤 4:进入控制器,声明 IUserRepository
引用,并在构造函数中用 UserRepository
类初始化对象,将 MVCEntities
作为参数传递给 UserRepository
类中定义的构造函数。
#region Private member variables...
private IUserRepository userRepository;
#endregion
#region Public Constructor...
/// <summary>
/// Public Controller to initialize User Repository
/// </summary>
public MyController()
{
this.userRepository = new UserRepository(new MVCEntities());
}
#endregion
在解决方案中,它看起来会像这样
步骤 5:现在,对于我们在控制器中直接使用 Entity Framework context 的所有操作,我们将调用逻辑替换为创建的 userRepository
对象,并调用 repository
类中定义的方法。
例如,在 Index 控制器中,我们显示用户列表,我们这样做
var userList = from user in userRepository.GetUsers() select user;
var users = new List<LearningMVC.Models.UserList>();
if (userList.Any())
{
foreach (var user in userList)
{
users.Add(new LearningMVC.Models.UserList()
{ UserId = user.UserId, Address = user.Address,
Company = user.Company, FirstName = user.FirstName,
LastName = user.LastName, Designation = user.Designation,
EMail = user.EMail, PhoneNo = user.PhoneNo });
}
}
我们可以看到之前使用的代码保持不变,只是在 Entity Framework 数据访问层和业务逻辑之间引入了一个层,控制器现在只使用那个抽象层来与数据库通信。
同样,对于控制器的其他操作
详细说明
Create
未使用。
删除
步骤 6:运行应用程序,我们看到应用程序像以前一样运行。
现在是派对时间了。
结论
现在我们知道如何创建存储库并使用它执行 CRUD 操作了。
现在我们可以直观地看到该模式多么有用,以及它如何解决了我们紧耦合的问题并产生了适当的架构。
根据 MSDN,使用存储库模式来实现一个或多个以下目标:
- 您希望最大化可通过自动化进行测试的代码量,并隔离数据层以支持单元测试。
- 您从许多位置访问数据源,并希望应用集中管理的一致的访问规则和逻辑。
- 您希望实现和集中化数据源的缓存策略。
- 您希望通过将业务逻辑与数据或服务访问逻辑分离来提高代码的可维护性和可读性。
- 您希望使用强类型的业务实体,以便在编译时而不是运行时识别问题。
- 您希望将行为与相关数据关联。例如,您希望计算字段或强制执行实体内数据元素之间的复杂关系或业务规则。
- 您希望应用领域模型来简化复杂的业务逻辑。
我完全同意这一点,但我们的应用程序是否恰当地使用了该模式?如果需要创建数百个存储库怎么办?如果我们有数百个实体怎么办?我们为它们都创建存储库,导致混乱和代码冗余吗?答案是肯定的,大大的否定。在我下一篇也是本系列的最后一篇文章中,我们将学习如何创建一个通用存储库来处理大量实体。本文的源代码以及现有文章(即第三部分)以及数据库脚本已附上。您可以下载并运行该解决方案,如果您有任何想法,请随时给我提问。我很乐意回答。
编程愉快 :)