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

使用 MVC 中的 Entity Framework 5.0 Code First 方法进行 CRUD 操作

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.84/5 (33投票s)

2013年8月20日

CPOL

7分钟阅读

viewsIcon

189100

downloadIcon

6145

在本文中,您将学习如何使用 MVC 中的 Entity Framework 5.0 Code First 方法执行 CRUD 操作。

引言

本文介绍了 MVC 应用程序中的 Entity Framework 5.0 Code First 方法。我们正在开发一个针对 Publisher 和 Book 实体的应用程序,我们可以在其上执行创建 (Create)、读取 (Read)、更新 (Update) 和删除 (Delete) 操作。我们创建这两个实体,并使用 Fluent API 在它们之间建立一对多关系。

ADO.NET Entity Framework 是 .NET Framework 中包含的一个对象关系映射器 (ORM)。它基本上会根据数据库表生成业务对象和实体。它提供基本的 CRUD 操作,可以轻松管理实体之间的关系,并支持实体之间的继承关系。

在使用 EF 时,我们与实体模型进行交互,而不是与应用程序的关系数据库模型进行交互。这种抽象允许我们专注于业务行为和实体之间的关系。我们使用 Entity Framework 数据上下文来执行查询。当调用 CRUD 操作之一时,Entity Framework 会生成执行该操作所需的 SQL。

学习使用 Entity Framework 进行 MVC 的路线图

背景

要创建此应用程序,我们应该对 System.Data.Entity 命名空间中的 DbContext 类有基本了解。我们也应该熟悉视图,因为在本文中我不会为每个操作方法描述视图,所以您可以根据操作方法创建视图,或者可以使用脚手架模板为 Edit、List、Create、Delete 和 Details 创建视图。

我们需要将 Entity Framework Nuget 包安装到我们的应用程序中。当我们安装 Entity Framework Nuget 包时,会在我们的应用程序中添加两个引用 (System.Data.Entity 和 EntityFramework)。之后,我们就可以使用 Entity Framework 执行 CRUD 操作了。

图 1.1 安装 Entity Framework Nuget 包

Entity Framework Code First 方法

无论您是否已有数据库,都可以编写自己的类和属性(也称为 POCO - Plain Old CLR Objects),这些类和属性对应于表和列,并可以在没有 .edmx 文件的情况下与 Entity Framework 一起使用。在这种方法中,Entity Framework 不利用任何配置文件(*.edmx 文件)来存储数据库架构,因为映射 API 使用这些约定在运行时动态生成数据库架构。目前,Entity Framework Code First 方法不支持映射到存储过程。可以使用 ExecuteSqlCommand() 和 SqlQuery() 方法来执行存储过程。

要理解 Entity Framework Code First 方法,您需要创建一个具有两个实体的 MVC 应用程序,一个实体是 Publisher,另一个是 Book。所以让我们一步一步地看一个例子。

要创建您的 MVC 应用程序,在 Visual Studio 中选择“文件”->“新建”->“项目...”,然后选择“MVC 4 应用程序”,然后选择“空应用程序”。

创建模型类

我们在 Models 文件夹下为 Publisher 和 Book 创建类,这些类用作实体和实体集。这些类将与数据库进行映射,因为我们正在使用 Code First 方法,并且这些类将使用 Entity Framework 的 DbContext 类在数据库中创建表。所以让我们逐一看看这些类。

Publisher 类位于 Models 文件夹下;该文件名是 Publisher.cs,如下所示:

using System.Collections.Generic; 
namespace ExampleCodeFirstApproch.Models
{
    public class Publisher
    {
        public int PublisherId { get; set; }
        public string PublisherName { get; set; }
        public string Address { get; set; }
        public ICollection<book> Books { get; set; }
    }
}</book>

Book 类位于 Models 文件夹下;该文件名是 Book.cs,如下所示:

namespace ExampleCodeFirstApproch.Models
{
    public class Book 
    {
        public int BookId { get; set; }
        public string Title { get; set; }
        public string Year { get; set; }
        public int PublisherId { get; set; }
        public Publisher Publisher { get; set; }
    }
}

创建数据访问层

本文的这一部分是本文以及 Code First 方法的核心。首先,我们需要一个连接字符串,以便应用程序可以连接到我们的数据库。我们在 web.config 文件中创建一个连接。我将连接字符串名称指定为 DbConnectionString,您可以随意命名,但请记住它,因为我们将在上下文类中使用它。

<connectionStrings>
    <add name="DbConnectionString" 
      connectionString="Data Source=sandeepss-PC;Initial Catalog=CodeFirst;User ID=sa; 
      Password=*******" providerName="System.Data.SqlClient" />
</connectionStrings>

我们已经有了 PublisherBook 这两个类,现在我们将创建一个上下文类。我们在 Models 文件夹下创建一个 LibraryContext 类,该文件名是 LibraryContext.cs。此类继承 DbContext,因此我们可以使用 LibraryContext 类的对象来使用 DbContext 类的方法,如下所示:

using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity; 
namespace ExampleCodeFirstApproch.Models
{
    public class LibraryContext :DbContext
    {
        public LibraryContext()
            : base("name=DbConnectionString")
        {
        } 
        public DbSet<Publisher> Publishers { get; set; }
        public DbSet<Book> Books { get; set; } 
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {                      
            modelBuilder.Entity<Publisher>().HasKey(p => p.PublisherId);
            modelBuilder.Entity<Publisher>().Property(c => c.PublisherId)
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);            
            modelBuilder.Entity<Book>().HasKey(b => b.BookId);
            modelBuilder.Entity<Book>().Property(b => b.BookId)
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);            
            modelBuilder.Entity<Book>().HasRequired(p => p.Publisher)
                .WithMany(b => b.Books).HasForeignKey(b=>b.PublisherId);            
            base.OnModelCreating(modelBuilder);
        }
    }
}

我们继承 DbContext 类的 LibraryContext 类已经准备好了。LibraryContext 类包含一个三部分构造函数、DbSet 属性和一个 OnModelCreating 方法。让我们逐一查看。

构造函数:这是一个不带任何参数的空构造函数,换句话说,它是默认构造函数,但它继承了基类的单字符串参数化构造函数。此构造函数使用给定的字符串作为数据库的名称(如我们所用)或连接字符串来构造新的上下文实例。这里 DbConnectionString 是应用程序 web.config 文件中定义的连接字符串的名称。

public LibraryContext(): base("name=DbConnectionString")
{
}

属性:在 Code First 工作流程开发时,您会定义一个派生的 DbContext,它代表与数据库的会话,并为模型中的每种类型公开一个 DbSet。Code First 示例中显示的常见情况是,DbContext 具有针对模型实体类型的公共自动 DbSet 属性。

public DbSet<Publisher> Publishers { get; set; }
public DbSet<Book> Books { get; set; }

Dbset 属性代表一个实体集,用于执行创建、读取、更新和删除操作。当实体类型在编译时未知时,可以使用非泛型版本的 DbSet<TEntity>。这里我们使用实体的属性的复数名称,这意味着您的表将在数据库中以该特定实体的名称创建。

方法LibraryContext 类有一个重写的 OnModelCreating 方法。此方法在派生上下文的模型已初始化但模型尚未锁定并用于初始化上下文之前被调用。此方法的默认实现不执行任何操作,但可以在派生类中重写它,以便在模型锁定之前对其进行进一步配置。基本上,在此方法中,我们配置将由模型创建的数据库表或这些表之间的定义的任何关系。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{   
   modelBuilder.Entity<Publisher>().HasKey(p => p.PublisherId);
   modelBuilder.Entity<Publisher>().Property(c => c.PublisherId)
          .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
   modelBuilder.Entity<Book>().HasKey(b => b.BookId);
   modelBuilder.Entity<Book>().Property(b => b.BookId)
         .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
   modelBuilder.Entity<Book>().HasRequired(p => p.Publisher)
         .WithMany(b => b.Books).HasForeignKey(b=>b.PublisherId);
   base.OnModelCreating(modelBuilder);
}

此方法接受一个参数,即 DbModelBuilder 对象。DbModelBuilder 类将 POCO 类映射到数据库架构。此方法仅在创建派生上下文的第一个实例时调用一次。然后缓存该上下文的模型,并供应用程序域中所有后续的上下文实例使用。可以通过在给定的 ModelBuidler 上设置 ModelCaching 属性来禁用此缓存,但这可能会严重降低性能。通过直接使用 DbModelBuilderDbContext 类可以获得对缓存的更多控制。

使用 Fluent API 配置/映射属性

LibraryContext 类下的 OnModelCreating() 方法使用 Fluent API 来映射和配置表中的属性。所以让我们逐一查看 OnModelCreating() 方法中使用的每个方法。

modelBuilder.Entity<Book>().HasRequired(p => p.Publisher)
.WithMany(b => b.Books).HasForeignKey(b=>b.PublisherId);
  • HasKey()Haskey() 方法配置表的主键。
  • Property()Property 方法配置属于实体或复杂类型的每个属性的属性。它用于获取给定属性的配置对象。配置对象上的选项特定于正在配置的类型;例如,IsUnicode 仅对字符串属性可用。
  • HasDatabaseGeneratedOption:它配置属性的值如何由数据库生成。
  • DatabaseGeneratedOption.IdentityDatabaseGeneratedOption 是数据库注释。它枚举了一个数据库生成选项。DatabaseGeneratedOption.Identity 用于通过唯一值在表中创建自增列。
  • 使用以下表达式定义 PublisherBook 之间的外键关系

为 CRUD 操作创建控制器

现在我们在应用程序的 Controllers 文件夹下创建两个控制器,一个用于 Publisher CRUD 操作 (PublisherController.cs),另一个用于 Book CRUD 操作 (BookController.cs)。所以这是每个控制器的代码。

Publisher 控制器在 Controllers 文件夹下的 PublisherController.cs 文件中

using System.Linq;
using System.Web.Mvc;
using ExampleCodeFirstApproch.Models; 
namespace ExampleCodeFirstApproch.Controllers
{
    public class PublisherController : Controller
    {
        LibraryContext objContext;
        public PublisherController()
        {
            objContext = new LibraryContext();
        } 
        #region List and Details Publisher 
        public ActionResult Index()
        {
            var publishers = objContext.Publishers.ToList();
            return View(publishers);
        } 
        public ViewResult Details(int id)
        {
            Publisher publisher = 
              objContext.Publishers.Where(x=>x.PublisherId==id).SingleOrDefault();
            return View(publisher);
        } 
        #endregion 
        #region Create Publisher 
        public ActionResult Create()
        {
            return View(new Publisher());
        } 
        [HttpPost]
        public ActionResult Create(Publisher publish)
        {
            objContext.Publishers.Add(publish);
            objContext.SaveChanges();
            return RedirectToAction("Index");
        } 
        #endregion 
        #region edit publisher 
        public ActionResult Edit(int id)
        {
            Publisher publisher = objContext.Publishers.Where(
              x => x.PublisherId == id).SingleOrDefault();
            return View(publisher);
        } 
        [HttpPost]
        public ActionResult Edit(Publisher model)
        {
            Publisher publisher = objContext.Publishers.Where(
              x => x.PublisherId == model.PublisherId).SingleOrDefault();
            if (publisher != null)
            {
                objContext.Entry(publisher).CurrentValues.SetValues(model);
                objContext.SaveChanges();
                return RedirectToAction("Index");
            }              
            return View(model);
        } 
       #endregion 
        #region Delete Publisher
        public ActionResult Delete(int id)
        {
            Publisher publisher = objContext.Publishers.Find(id);
            //.Where(x => x.PublisherId == id).SingleOrDefault();

            return View(publisher);
        } 
        [HttpPost]
        public ActionResult Delete(int id, Publisher model)
        {
           var publisher = 
             objContext.Publishers.Where(x => x.PublisherId == id).SingleOrDefault();
           if (publisher != null)
            {
                objContext.Publishers.Remove(publisher);
                objContext.SaveChanges();
            }
            return RedirectToAction("Index");
        }
        #endregion 
    }
}

Book 控制器在 Controllers 文件夹下的 BookController.cs 文件中

using System.Linq;
using System.Web.Mvc;
using ExampleCodeFirstApproch.Models; 
namespace ExampleCodeFirstApproch.Controllers
{
    public class BookController : Controller
    {
       LibraryContext objContext;
       public BookController()
        {
            objContext = new LibraryContext();
        } 
        #region List and Details Book 
        public ActionResult Index()
        {
            var books = objContext.Books.ToList();
            return View(books);
        } 
        public ViewResult Details(int id)
        {
            Book book = objContext.Books.Where(x=>x.BookId==id).SingleOrDefault();
            return View(book);
        } 
        #endregion 
        #region Create Publisher 
        public ActionResult Create()
        {
            return View(new Book());
        } 
        [HttpPost]
        public ActionResult Create(Book book)
        {
            objContext.Books.Add(book);
            objContext.SaveChanges();
            return RedirectToAction("Index");
        } 
        #endregion 
        #region Edit Book 
        public ActionResult Edit(int id)
        {
            Book book = objContext.Books.Where(x => x.BookId == id).SingleOrDefault();
            return View(book);
        }  
        [HttpPost]
        public ActionResult Edit(Book model)
        {
            Book book = objContext.Books.Where(x => x.BookId == model.BookId).SingleOrDefault();
            if (book != null)
            {
                objContext.Entry(book).CurrentValues.SetValues(model);
                objContext.SaveChanges();
                return RedirectToAction("Index");
            }              
            return View(model);
        } 
       #endregion 
        #region Delete Book 
        public ActionResult Delete(int id)
        {
            Book book = objContext.Books.Find(id);
            return View(book);
        } 
        [HttpPost]
        public ActionResult Delete(int id, Publisher model)
        {
           var book = objContext.Books.Where(x => x.BookId == id).SingleOrDefault();
           if (book != null)
            {
                objContext.Books.Remove(book);
                objContext.SaveChanges();
            }
            return RedirectToAction("Index");
        }
        #endregion
    }
}

Publisher 和 Book 控制器都已准备就绪,使用脚手架模板根据操作方法创建视图,您可以下载 zip 文件夹。运行应用程序,您会发现您的表已在数据库中创建并建立了关系。

图 1.2 Publishers 和 Books 表之间的关系

注意:下载源代码后,请在 web.config 文件中更改连接字符串并运行应用程序。

© . All rights reserved.