ASP.NET MVC 中理解 Entity Framework Code First 方法的绝对初学者教程






4.88/5 (48投票s)
本文将讨论 Entity Framework Code First 方法。
引言
本文将讨论 Entity Framework Code First 方法。我们将了解 Code First 方法的优点。我们还将使用 Code First 方法创建一个 ASP.NET MVC 示例应用程序。
背景
在开始本文之前,我们需要了解现代应用程序架构方式的转变。传统上,我们一直在设计和开发以数据为中心的应用程序(数据驱动设计)
。这意味着我们过去常常思考需要什么数据来满足我们的业务需求,然后我们从数据库架构自下而上地构建我们的软件。这种方法仍然适用于许多应用程序,对于此类应用程序,我们应该使用 Entity Framework Database First 方法。
设计或架构应用程序的另一种方法是使用以领域为中心的方法(领域驱动设计)
。在这种方法中,我们从解决特定业务问题所需的实体和模型的角度进行思考。现在,如果其中一些模型需要持久化,我们可以将它们保存在数据库或任何数据存储中。在这种方法中,我们以这样一种方式设计我们的模型,即它们可以存储/持久化在任何地方。换句话说,我们创建持久化无关的模型并单独编写持久化逻辑。Entity Framework Code First 方法是用于使用领域中心方法创建应用程序模型,然后可以稍后持久化它们。
因此,Entity Framework Code First 方法使我们能够为我们的模型编写普通旧 CLR 对象(POCO)
,然后通过为我们的模型类定义DbContext
类,让我们将它们持久化到数据存储中。使用这种方法的一些好处是
- 支持领域驱动设计的能力。
- 能够更快地开始开发(无需等待数据库准备就绪和成熟)。
- 模型类更清晰,因为模型中没有(或非常少)与持久化相关的代码。
- 持久化层可以更改,而不会对模型产生任何影响。
注意:本文适用于从未接触过 Code First 方法或不了解此方法的绝对初学者。因此,本文只包含介绍性信息和代码,以解释概念并帮助读者入门。本文不会讨论与 Code First 方法相关的高级主题和最佳实践。
使用代码
让我们尝试通过创建一个小型演示应用程序来了解如何实现 Code First 方法。我们将创建的应用程序是一个小型图书信息网站,它将
- 显示图书列表。
- 显示所选图书的详细信息。
- 添加新图书。
- 删除图书。
- 编辑图书信息。
- 用户还应该能够为任何给定的图书添加评论。
根据上述问题定义,我们需要 2 个模型。一个用于Book
,另一个用于Reviews
。首先,让我们添加对 Entity Framework 的nuget
包引用。
现在我们已经准备好开始开发我们的应用程序。
创建 POCO
现在让我们继续创建这些模型,而无需担心持久化问题。
public class Book { public int BookID { get; set; } public string BookName { get; set; } public string ISBN { get; set; } } public class Review { public int ReviewID { get; set; } public int BookID { get; set; } public string ReviewText { get; set; } }
在我们的模型类中,我们定义了我们想要保留在模型中的属性。但是,我们到目前为止还没有做一些事情。这些模型之间存在一对多关系。因此,我们需要在模型中考虑到这一点。此外,如果我们要向数据库生成模块提示持久化信息,例如表名、键列等。我们也可以在模型类中这样做。因此,经过这些更改后,我们的模型类将如下所示。
[Table("Books")] // Table name
public class Book
{
[Key] // Primary key
public int BookID { get; set; }
public string BookName { get; set; }
public string ISBN { get; set; }
// This is to maintain the many reviews associated with a book entity
public virtual ICollection<Review> Reviews { get; set; }
}
[Table("Reviews")] // Table name
public class Review
{
[Key]
public int ReviewID { get; set; }
[ForeignKey("Book")]
public int BookID { get; set; }
public string ReviewText { get; set; }
// This will keep track of the book this review belong too
public virtual Book Book { get; set; }
}
创建上下文类
现在我们已经准备好模型类。接下来,我们需要创建一个DBContext
对象,它将负责对这些模型执行所有 CRUD 操作。让我们继续为这些模型创建DBContext
类。
public class BooksDbContext : DbContext
{
public DbSet<Book> Books { get; set; }
public DbSet<Review> Reviews { get; set; }
}
上面定义的上下文类能够对Book
和Review
模型执行 CRUD 操作,因为它为它们都定义了DbSet
。
设置数据库和位置
现在 DbContext 类已经准备就绪,我们唯一剩下要问的问题是这些数据将存储在哪里。自古以来,我们一直使用ConnectionStrings
来指定与数据库的连接。在这种情况下,我们也可以使用ConnectionString
来指定DbContext
类应该使用的数据库的位置和元数据。让我们看看如果我们需要在 App_Data 文件夹中创建数据库,ConnectionString
会是什么样子
<connectionStrings>
<add name="BooksDbContext" connectionString="data source=.\SQLEXPRESS;attachdbfilename=|DataDirectory|\sampleDb.mdf;integrated security=True;user instance=True;multipleactiveresultsets=True;" providerName="System.Data.SqlClient" />
</connectionStrings>
这里需要注意的重要一点是,connectionString
的名称与我们创建的DbContext
类的名称相同。如果我们将connectionString
的名称与DbContext
类保持相同,则相应的DbContext
类将使用connectionString
来持久化数据。这符合“约定优于配置”的原则。但这也具有灵活性,因此我们可以为 connectionString 赋予自定义名称,即,如果我们需要为connectionString
赋予任何其他名称或将某个已定义的connectionString
与DbContext
类一起使用,那么我们需要在DbContext
类的基类构造函数中传入connectionString
名称。
测试 Code First 模型和上下文
为了测试上述定义的模型,让我们创建一个简单的 ASP.NET MVC 控制器,它将对Book
实体执行 CRUD 操作。
public class BooksController : Controller
{
BooksDbContext context = new BooksDbContext();
public ActionResult Index()
{
List books = context.Books.ToList();
return View(books);
}
public ActionResult Details(int id)
{
Book book = context.Books.SingleOrDefault(b => b.BookID == id);
if (book == null)
{
return HttpNotFound();
}
return View(book);
}
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Book book)
{
if (ModelState.IsValid)
{
context.Books.Add(book);
context.SaveChanges();
return RedirectToAction("Index");
}
return View(book);
}
public ActionResult Edit(int id)
{
Book book = context.Books.Single(p => p.BookID == id);
if (book == null)
{
return HttpNotFound();
}
return View(book);
}
[HttpPost]
public ActionResult Edit(int id, Book book)
{
Book _book = context.Books.Single(p => p.BookID == id);
if (ModelState.IsValid)
{
_book.BookName = book.BookName;
_book.ISBN = book.ISBN;
context.SaveChanges();
return RedirectToAction("Index");
}
return View(book);
}
public ActionResult Delete(int id)
{
Book book = context.Books.Single(p => p.BookID == id);
if (book == null)
{
return HttpNotFound();
}
return View(book);
}
[HttpPost]
public ActionResult Delete(int id, Book book)
{
Book _book = context.Books.Single(p => p.BookID == id);
context.Books.Remove(_book);
context.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
context.Dispose();
base.Dispose(disposing);
}
}
在这个模型中,我们使用 Context 类对Book
实体执行 CRUD 操作。(要了解如何使用DbContext
类执行 CRUD 操作,请参阅:绝对初学者的 Entity Framework 简介[^])
添加视图
让我们使用 ASP.NET MVC 视图生成向导为这个控制器生成强类型脚手架视图。(要了解有关 ASP.NET MVC 中默认脚手架控制器和视图的更多信息,请参阅:ASP.NET MVC for Web Forms 开发人员的绝对初学者教程[^])
图书列表
所选图书详情
创建新图书条目
编辑图书条目
删除图书
让我们运行并测试应用程序
这样,我们就拥有了一个使用 Entity Framework Code First 方法对 Books 实体进行 CRUD 操作的应用程序。让我们尝试运行这个应用程序。首次运行时,它不会显示任何数据,但它会在connectionString
中指定的位置创建一个新数据库。
创建新图书条目
所选图书详情
编辑图书条目
删除图书
图书列表
管理实体关系 - 添加图书评论
让我们修改Book
详细信息视图,以显示与该书相关的所有评论。
现在我们将为评论创建一个控制器,以便将评论添加到图书中。目前,我们只在此控制器中实现Create
方法。
public class ReviewsController : Controller
{
BooksDbContext context = new BooksDbContext();
public ActionResult Create(int id)
{
Book book = context.Books.SingleOrDefault(b => b.BookID == id);
ViewBag.bookName = book.BookName;
Review review = new Review();
review.BookID = id;
return View(review);
}
[HttpPost]
public ActionResult Create(Review review)
{
try
{
if (ModelState.IsValid)
{
Book book = context.Books.SingleOrDefault(b => b.BookID == review.BookID);
book.Reviews.Add(review);
context.SaveChanges();
return RedirectToAction("Details", "Books", new { id = book.BookID });
}
return View();
}
catch
{
return View();
}
}
}
现在我们将添加一个简单的视图来为图书添加评论。
一旦添加,这些评论将在图书详情页面上显示。
现在我们有了一个简单的应用程序,其中包含两个实体,它们之间具有一对多关系,并使用 Entity Framework Code First 方法将数据持久化到数据库中。
注意:在构建示例项目之前,请打开nuget
包管理器并恢复缺少的包。
值得关注的点:
本文是为对 Code First 方法一无所知的绝对初学者编写的。本文向读者介绍了一些关于什么是 Code First 方法以及如何使用 Code First 方法实现一个简单的 ASP.NET MVC 应用程序的介绍性说明。本文未讨论但对于此主题仍然非常重要的是,当我们更改模型时数据库架构的更新,即数据库迁移。
注意:本文中我们没有使用存储库和工作单元等模式,因为文章的主要目的是向读者介绍 Entity Framework 的 Code First 方法。大多数有经验的程序员会觉得本文非常基础且帮助不大。但我希望这对于所有 Entity Framework Code First 的绝对初学者来说是有益的。
历史
- 2013 年 11 月 18 日:第一版