4.在MVC中使用仓储模式进行增删查改
原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-using-the-repository-pattern-in-mvc/
或者:http://www.codeproject.com/Articles/644605/CRUD-Operations-Using-the-Repository-Pattern-in-MV
系列目录:
- Relationship in Entity Framework Using Code First Approach With Fluent API【【使用EF Code-First方式和Fluent API来探讨EF中的关系】】
- Code First Migrations with Entity Framework【使用EF 做数据库迁移】
- CRUD Operations Using Entity Framework 5.0 Code First Approach in MVC【在MVC中使用EF 5.0做增删查改】
- CRUD Operations Using the Repository Pattern in MVC【在MVC中使用仓储模式,来做增删查改】
- CRUD Operations Using the Generic Repository Pattern and Unit of Work in MVC【在MVC中使用泛型仓储模式和工作单元来做增删查改】
- CRUD Operations Using the Generic Repository Pattern and Dependency Injection in MVC【在MVC中使用泛型仓储模式和依赖注入,来做增删查改】
上一篇文章,讲到了MVC中基本的增删查改,这篇文章,我会继续说到,使用仓储模式,进行增删查改。
什么是仓储模式呢,我们先来了解一下:
仓储模式是为了在程序的数据访问层和业务逻辑层之间创建一个抽象层,它是一种数据访问模式,提供了一种更松散耦合的数据访问方法。我们把创建数据访问的逻辑代码写在单独的类中,或者类库中,这就是仓储。仓储负责和业务层进行持久化通信。在这篇文章中,我将会实现一个单一的业务模型的方法,来设计仓储模式-----这个仓储模式对于每一个实体都有一个仓储类。对于Book实体,我将会创建一个仓储接口和一个仓储类。当我们在控制器中初始化仓储的时候,我们使用仓储接口,所以控制器就会接受,任何实现了这个仓储接口的对象的引用。当控制器运行在Web服务器下的时候,它会接受一个和EF协同工作的仓储。MVC的控制器和仓储协同工作,去加载并持久化业务层。接着利用依赖注入,仓储能够被注射到控制器的构造函数中去。下面的图表,显示了仓储和EF数据上下文之间的关系,在这里,MVC控制器和仓储直接协同工作,而不是和EF。
那什么是EF呢,我们也来简单的了解一下吧:
EF是一个包含在.NET Framework中的ORM框架。它可以根据数据库表,生成业务对象和业务实体。它提供了基本的增删查改的功能。它可以很好的管理实体之间的关系。有了EF,我们可以直接和实体模型相互协同工作,而不用和关系数据库模型直接交互。这个抽象层,允许我们去专注于业务层的行为,还有实体之间的关系。我们使用EF数据上下文,进行查询数据。当增删查改中的某个操作被触发的时候,EF就会生成必要的SQL去执行这个操作。
EF处理数据的方式?
EF允许开发者,选择Database First【数据库优先】,Model First【模型优先】,Code First【代码优先】中的任何一个方式。
DataBase First【数据库优先】
数据库优先:是基于现有的数据库上的,以数据为中心的设计。EF能够基于数据库中的表和列生成业务实体。我们数据库的结构信息,都存储在一个后缀名为.edmx的文件中,存储的形式是XML。
Model First【模型优先】
在模型优先中,我们事先并不存在数据库,并且EF为我们提供了设计器,我们可以用来设计概念上的模型。模型优先同样使用后缀名为.edmx的文件来存储模型和映射信息。当我们的实体模型被创建好之后,EF设计器就可以为我们生成数据库。
Code First【代码优先】
在这个代码优先中,不管你是不是已经有数据库了,你都可以根据表和列,来创建你自己的实体。然后使用EF【不带后缀名为.edmx】。在代码优先的方式中,EF不使用任何形式的配置文件(.edmx文件)来存储数据库,因为EF中的Mapping API使用了约定,在运行的时候,会动态为我们创建数据库。
EF 6.0中已经开始支持存储过程了:参考文章《Entity Framework 6 Code First新特性:支持存储过程》
在这篇文章中,我会使用EF Code First在程序中开发一个数据访问层。这背后的推动力量是使用POCO (Plain Old CLR Objects) 类。Code First使用一系列的约定,来映射POCO类,但是这可以通过Code First数据注解来改变。
好了,理论知识将的差不多了,现在开始将今天的正题吧:
首先看看项目结构:
我们需要在空白的资源解决方案基础上,添加两个类库EF.Data、EF.Entity、还有一个空白MVC模板的EF.Web 项目;三个项目之间的引用关系,就是EF.Data需要引用EF.Entity,EF.Web项目需要引用EF.Data和EF.Entity两个类库。然后,EF.Data和EF.Web项目都需要引入EF框架。
这里我为了方便大家理解,也为了简单起见,就只添加一个实体Book:
下面看看EF.Entity类库中的Book实体代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EF.Entity
{
public class Book:BaseEntity
{
/// <summary>
/// 书名
/// </summary>
public string BookTitle { get; set; }
/// <summary>
/// 价格
/// </summary>
public decimal Price { get; set; }
/// <summary>
/// 出版商
/// </summary>
public string PublisherName { get; set; }
/// <summary>
/// 出版时间
/// </summary>
public DateTime PublishedDate { get; set; }
}
}
BaseEntity实体:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EF.Entity
{
public abstract class BaseEntity
{
/// <summary>
/// 编号
/// </summary>
public int ID { get; set; }
/// <summary>
/// 添加时间
/// </summary>
public DateTime AddedDate { get; set; }
/// <summary>
/// 修改时间
/// </summary>
public DateTime ModifiedDate { get; }
}
}
EF.Data类库中BookMap实体:
using EF.Entity;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EF.Data
{
public class BookMap:EntityTypeConfiguration<Book>
{
public BookMap()
{
//配置主键
this.HasKey(s => s.ID);
//配置列
this.Property(s => s.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.Property(s => s.BookTitle).HasColumnType("NVARCHAR").HasMaxLength(50).IsRequired();
this.Property(s => s.PublisherName).HasColumnType("nvarchar").HasMaxLength(50).IsRequired();
this.Property(s => s.Price).IsRequired();
this.Property(s => s.AddedDate).IsRequired();
this.Property(s => s.ModifiedDate).IsOptional();
this.Property(s => s.PublishedDate).IsRequired();
//配置表
this.ToTable("Books");
}
}
}
实体和映射类弄完了,开始写EF数据上下文类:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace EF.Data
{
public class EFDbContext:DbContext
{
public EFDbContext()
: base("name=DbConnectionString")
{ }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
.Where(type => !String.IsNullOrEmpty(type.Namespace))
.Where(type => type.BaseType != null && type.BaseType.IsGenericType
&& type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
foreach (var type in typesToRegister)
{
dynamic configurationInstance = Activator.CreateInstance(type);
modelBuilder.Configurations.Add(configurationInstance);
}
}
}
}
连接字符串:【EF.Data和EF.Web项目都要添加】
<connectionStrings>
<add name="DbConnectionString" connectionString="Server=.;Database=BookDB;uid=sa;Pwd=Password_1" providerName="System.Data.SqlClient"/>
</connectionStrings>
弄完之后,我们先来开启数据库迁移技术吧:
在程序包管理器控制台下输入:
Enable-Migrations
然后修改生成的Configuration类:
最后在程序管理器控制台输入:
Update-Database -Verbose
可以看到生成的数据库的SQL语句了。看下数据库:
数据库生成了,现在开始创建仓储模式,来进行增删查改吧:
using EF.Entity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EF.Data
{
public interface IRepository
{
/// <summary>
/// 获取所有的Book
/// </summary>
/// <returns></returns>
IEnumerable<Book> GetAllBooks();
/// <summary>
/// 根据BookId查找Book
/// </summary>
/// <param name="bookId"></param>
/// <returns></returns>
Book GetBookById(int bookId);
/// <summary>
/// 添加Book
/// </summary>
/// <param name="model"></param>
void InsertBook(Book model);
/// <summary>
/// 修改Book
/// </summary>
/// <param name="model"></param>
void UpdateBook(Book model);
/// <summary>
/// 删除Book
/// </summary>
/// <param name="bookId"></param>
void DeleteBook(int bookId);
/// <summary>
/// 保存
/// </summary>
void Save();
}
}
BookRepository类:
using EF.Entity;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EF.Data
{
public class BookRepository:IRepository
{
private EFDbContext db;
public BookRepository()
{
db = new EFDbContext();
}
public IEnumerable<Book> GetAllBooks()
{
return db.Set<Book>().ToList();
//throw new NotImplementedException();
}
public Book GetBookById(int bookId)
{
return db.Set<Book>().Find(bookId);
//throw new NotImplementedException();
}
public void InsertBook(Book model)
{
db.Entry(model).State = EntityState.Added;
//throw new NotImplementedException();
}
public void UpdateBook(Book model)
{
db.Entry(model).State = EntityState.Modified;
//throw new NotImplementedException();
}
public void DeleteBook(int bookId)
{
Book model= db.Set<Book>().Find(bookId);
db.Entry(model).State = EntityState.Deleted;
//throw new NotImplementedException();
}
public void Save()
{
db.SaveChanges();
//throw new NotImplementedException();
}
}
}
Book控制器代码:
using EF.Data;
using EF.Entity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace EF.Web.Controllers
{
public class BookController : Controller
{
private IRepository _bookRepository;
public BookController()
{
this._bookRepository = new BookRepository();
}
// GET: Book
#region Index
public ActionResult Index()
{
return View(_bookRepository.GetAllBooks().ToList());
}
#endregion
#region InsertBook
public ActionResult InsertBook()
{
return View();
}
[HttpPost]
public ActionResult InsertBook(Book model)
{
_bookRepository.InsertBook(model);
_bookRepository.Save();
return RedirectToAction("Index");
}
#endregion
#region UpdateBook
public ActionResult UpdateBook(int bookId)
{
Book model = _bookRepository.GetBookById(bookId);
if (model != null)
{
return View(model);
}
else
{
return View();
}
}
[HttpPost]
public ActionResult UpdateBook(Book model)
{
_bookRepository.UpdateBook(model);
_bookRepository.Save();
return RedirectToAction("Index");
}
#endregion
#region BookDetails
public ActionResult BookDetails(int bookId)
{
Book model = _bookRepository.GetBookById(bookId);
if (model != null)
{
return View(model);
}
else
{
return View();
}
}
#endregion
#region DeleteBook
public ActionResult DeleteBook(int bookId)
{
Book model = _bookRepository.GetBookById(bookId);
if (model != null)
{
return View(model);
}
else
{
return View();
}
}
[HttpPost]
public ActionResult DeleteBook(int bookId, FormCollection form)
{
_bookRepository.DeleteBook(bookId);
_bookRepository.Save();
return RedirectToAction("Index");
}
#endregion
}
}
具体的视图,根据模板生成。
修改一下默认的路由:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace EF.Web
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Book", action = "Index", id = UrlParameter.Optional }
);
}
}
}
运行项目:
添加玩数据之后:
这样就完成了使用仓储模式,进行增删查改的操作了。
最后看下项目完成之后的结构吧: