65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (48投票s)

2013年11月18日

CPOL

7分钟阅读

viewsIcon

281703

downloadIcon

8497

本文将讨论 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 方法。我们将创建的应用程序是一个小型图书信息网站,它将

  1. 显示图书列表。
  2. 显示所选图书的详细信息。
  3. 添加新图书。
  4. 删除图书。
  5. 编辑图书信息。
  6. 用户还应该能够为任何给定的图书添加评论。

根据上述问题定义,我们需要 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; }
}

上面定义的上下文类能够对BookReview模型执行 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赋予任何其他名称或将某个已定义的connectionStringDbContext类一起使用,那么我们需要在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 日:第一版
© . All rights reserved.