MVC+EF 理解和实现仓储模式和工作单元模式

原文:Understanding Repository and Unit of Work Pattern and Implementing Generic Repository in ASP.NET MVC using Entity Framework

文章介绍

在这篇文章中,我们试着来理解Repository(下文简称仓储)和Unit of Work(下文简称工作单元)模式。同时我们使用ASP.NET MVC和Entity Framework 搭建一个简单的web应用来实现通用仓储和工作单元模式。

背景

我记得在.NET 1.1的时代,我们不得不花费大量的时间为每个应用程序编写数据访问代码。即使代码的性质几乎相同,数据库模式的差异使我们为每个应用程序编写单独的数据访问层。在新版本的.NET框架中,在我们的应用程序中使用orm(对象-关系映射工具)使我们避免像以前一样编写大量的数据访问层的代码成为可能

由于orm的数据访问操作变得那么简单直接,导致数据访问逻辑和逻辑谓词(predicates)有可能散落在整个应用程序中。例如,每个控制器都有ObjectContext对象的实例,都可以进行数据访问。

存储模式和工作单位模式使通过ORM进行数据访问操作更加干净整洁,把所有的数据访问几种在一个位置,并且使程序维持可测试的能力。让我们通过在一个简单的MVC应用程序中实现仓储模式和工作单元来代替枯燥的谈论他们(“Talk is cheap,show me the code!)

创建代码

首先使用vs创建一个MVC web应用程序,然后在Models中添加一个简单的 Books类,我们将对这个类进行数据库的CRUD操作。(原文使用的DB First方式搭建实例,鉴于我从开始正式接触EF就没有认真的进行DB First方式的学习,所以此处使用Code First方式来进行演示)

 [Table("Books")]
public class Book
{
[Key]
public int Id { get; set; } [Column(TypeName = "varchar")]
[MaxLength(100)]
[Display(Name = "封面")]
public string Cover { get; set; }
[Column(TypeName = "nvarchar")]
[MaxLength(200)]
[Display(Name = "书名")]
public string BookName { get; set; } [Column(TypeName = "nvarchar")]
[MaxLength(200)]
[Display(Name = "作者")]
public string Author { get; set; } [Column(TypeName = "nvarchar")]
[MaxLength(200)]
[Display(Name = "译名")]
public string TranslatedName { get; set; } [Column(TypeName = "nvarchar")]
[MaxLength(200)]
[Display(Name = "译者")]
public string Translator { get; set; } [Column(TypeName = "nvarchar")]
[MaxLength(200)]
[Display(Name = "出版社")]
public string Publisher { get; set; } [Display(Name = "字数")]
public int WordCount { get; set; } [Display(Name = "页数")]
public int Pages { get; set; } [Column(TypeName = "varchar")]
[MaxLength(50)]
[Display(Name = "ISBN号")]
public string ISBN { get; set; } [Column(TypeName = "float")]
[Display(Name = "定价")]
public double Price { get; set; } [Column(TypeName = "float")]
[Display(Name = "售价")]
public double SalePrice { get; set; } [Column(TypeName="date")]
[Display(Name="出版日期")]
public DateTime PublicationDate { get; set; } [Column(TypeName = "nvarchar")]
[MaxLength(1000)]
[Display(Name = "内容简介")]
[DataType(DataType.MultilineText)]
public string Introduction { get; set; } [Column(TypeName = "nvarchar")]
[MaxLength(1000)]
[Display(Name = "作者简介")]
[DataType(DataType.MultilineText)]
public string AboutTheAuthors { get; set; } [Column(TypeName = "varchar")]
[MaxLength(100)]
[Display(Name = "购买链接")]
public string Link { get; set; }

然后就是在程序包管理器控制台中输入数据迁移指令来实现数据表的创建(之前的步骤如果还不会的话,建议先去看下MVC基础项目搭建!)一般是依次执行者如下三个命令即可,我说一般:

PM> Enable-migrations
PM>add-migration createBook
PM> update-database
 

可以用Vs自带的服务器资源管理器打开生成的数据库查看表信息。

使用MVC Scaffolding

现在我们的准备工作已经完成,可以使用Entity Framework来进行开发了,我们使用VS自带的MVC模板创建一个Controller来完成Books 表的CRUD操作。

在解决方案中Controllers文件夹右键,选择添加Controller,在窗口中选择“包含视图的MVC x控制器(使用Entity Framework)”

public class BooksController : Controller
{
private MyDbContext db = new MyDbContext(); // GET: Books
public ActionResult Index()
{
return View(db.Books.ToList());
} // GET: Books/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Book book = db.Books.Find(id);
if (book == null)
{
return HttpNotFound();
}
return View(book);
} // GET: Books/Create
public ActionResult Create()
{
return View();
} // POST: Books/Create
// 为了防止“过多发布”攻击,请启用要绑定到的特定属性,有关
// 详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=317598。
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Cover,BookName,Author,TranslatedName,Translator,Publisher,WordCount,Pages,ISBN,Price,Introduction,AboutTheAuthors,Link")] Book book)
{
if (ModelState.IsValid)
{
db.Books.Add(book);
db.SaveChanges();
return RedirectToAction("Index");
} return View(book);
} // GET: Books/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Book book = db.Books.Find(id);
if (book == null)
{
return HttpNotFound();
}
return View(book);
} // POST: Books/Edit/5
// 为了防止“过多发布”攻击,请启用要绑定到的特定属性,有关
// 详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=317598。
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "Id,Cover,BookName,Author,TranslatedName,Translator,Publisher,WordCount,Pages,ISBN,Price,Introduction,AboutTheAuthors,Link")] Book book)
{
if (ModelState.IsValid)
{
db.Entry(book).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(book);
} // GET: Books/Delete/5
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Book book = db.Books.Find(id);
if (book == null)
{
return HttpNotFound();
}
return View(book);
} // POST: Books/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Book book = db.Books.Find(id);
db.Books.Remove(book);
db.SaveChanges();
return RedirectToAction("Index");
} protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}

F5启动调试,我们应该是已经可以对Books进行CRUD操作了

现在从代码和功能的角度来看这样做并没有什么错。但这种方法有两个问题。

  1. 数据方位的代码零散分布在应用程序中(Controllers),这将是后期程序维护的噩梦
  2. 在控制器(Controller)和动作(Action)内部创建了数据上下文(Context),这使得功能无法通过伪数据进行测试,我们无法验证其结果,除非我们使用测试数据。(应该就是说功能不可测试)

Note:如果第二点感觉不清晰,那推荐阅读关于在MVC中进行测试驱动开发(Test Driven Development using MVC)方面的内容。为防止离题,不再本文中进行讨论。

实现仓储模式

现在,我们来解决上面的问题。我们可以通过把所有包含数据访问逻辑的代码放到一起来解决这个问题。所以让我们定义一个包含所有对 Books 表的数据访问逻辑的类

但是在创建这个类之前,我们也顺便考虑下第二个问题。如果我们创建一个简单的定义了访问Books表的约定的接口然后用刚才提到的类实现接口,我们会得到一个好处,我们可以使用另一个类伪造数据来实现接口。这样,就可以保持Controller是可测试的。(原文很麻烦,就是表达这个意思)

所以,我们先定义对 Books 进行数据访问的约定。

    public interface IRepository<T> where T:class
{
IEnumerable<T> GetAll(Func<T, bool> predicate = null);
T Get(Func<T, bool> predicate);
void Add(T entity);
void Update(T entity);
void Delete(T entity);
}

下面的类包含了对 Books 表CRUD操作接口的实现

 public class BooksRepository:IRepository<Book>
{
private MyDbContext dbContext = new MyDbContext(); public IEnumerable<Book> GetAll(Func<Book, bool> predicate = null)
{
if(predicate!=null)
{
return dbContext.Books.Where(predicate);
}
return dbContext.Books;
} public Book Get(Func<Book, bool> predicate)
{
return dbContext.Books.First(predicate);
} public void Add(Book entity)
{
dbContext.Books.Add(entity);
} public void Update(Book entity)
{
dbContext.Entry(entity).State = EntityState.Modified;
} public void Delete(Book entity)
{
dbContext.Books.Remove(entity);
}
internal void SaveChanges()
{
dbContext.SaveChanges();
}
}

现在,我们创建另一个包含对 Books 表进行CRUD操作的Controller,命名为BooksRepoController

public class BooksRepoController : Controller
{
private BooksRepository repo = new BooksRepository(); // GET: Books1
public ActionResult Index()
{
return View(repo.GetAll().ToList());
} // GET: Books1/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Book book = repo.Get(t=>t.Id==id.Value);
if (book == null)
{
return HttpNotFound();
}
return View(book);
} // GET: Books1/Create
public ActionResult Create()
{
return View();
} // POST: Books1/Create
// 为了防止“过多发布”攻击,请启用要绑定到的特定属性,有关
// 详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=317598。
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Cover,BookName,Author,TranslatedName,Translator,Publisher,WordCount,Pages,ISBN,Price,Introduction,AboutTheAuthors,Link")] Book book)
{
if (ModelState.IsValid)
{
repo.Add(book);
repo.SaveChanges(); return RedirectToAction("Index");
} return View(book);
} // GET: Books1/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Book book = repo.Get(t => t.Id == id);
if (book == null)
{
return HttpNotFound();
}
return View(book);
} // POST: Books1/Edit/5
// 为了防止“过多发布”攻击,请启用要绑定到的特定属性,有关
// 详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=317598。
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "Id,Cover,BookName,Author,TranslatedName,Translator,Publisher,WordCount,Pages,ISBN,Price,Introduction,AboutTheAuthors,Link")] Book book)
{
if (ModelState.IsValid)
{
repo.Update(book);
repo.SaveChanges();
return RedirectToAction("Index");
}
return View(book);
} // GET: Books1/Delete/5
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Book book = repo.Get(t => t.Id == id);
if (book == null)
{
return HttpNotFound();
}
return View(book);
} // POST: Books1/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Book book = repo.Get(t => t.Id == id);
repo.Delete(book); repo.SaveChanges();
return RedirectToAction("Index");
} }

现在这种方法的好处是,我的ORM的数据访问代码不是分散在控制器。它被包装在一个Repository类里面。

如何处理多个Repository库?

下面想象下如下场景,我们数据库中有多个表,那样我们需要为每个表创建一个Reporsitory类。(好多重复工作的说,其实这不是问题)

问题是关于 数据上下文(DbContext) 对象的。如果我们创建多个Repository类,是不是每一个都单独的包含一个 数据上下文对象?我们知道同时使用多个 数据上下文 会存在问题,那我们该怎么处理每个Repository都拥有自己的数据上下文 对象的问题?

来解决这个问题吧。为什么每个Repository要拥有一个数据上下文的实例呢?为什么不在一些地方创建一个它的实例,然后在repository被实例化的时候作为参数传递进去呢。现在这个新的类被命名为 UnitOfWork ,此类将负责创建数据上下文实例并移交到控制器的所有repository实例。

实现工作单元

所以,我们在单独创建一个使用 UnitOfWork 的Repository类,数据上下文对象将从外面传递给它因此,让我们创建一个单独的存储库将使用通过UnitOfWork类和对象上下文将被传递到此类以外。

    public class BooksRepositoryWithUow : IRepository<Book>
{
private MyDbContext dbContext = null; public BooksRepositoryWithUow(MyDbContext _dbContext)
{
dbContext = _dbContext;
}
public IEnumerable<Book> GetAll(Func<Book, bool> predicate = null)
{
if (predicate != null)
{
return dbContext.Books.Where(predicate);
}
return dbContext.Books;
}
public Book Get(Func<Book, bool> predicate)
{
return dbContext.Books.FirstOrDefault(predicate);
} public void Add(Book entity)
{
dbContext.Books.Add(entity);
} public void Update(Book entity)
{
dbContext.Entry(entity).State = EntityState.Modified;
} public void Delete(Book entity)
{
dbContext.Books.Remove(entity);
} }

现在这个Repository类将从类的外面得到DbContext对象(每当它被创建时).

现在,假如我们创建多个仓储类,我们在仓储类实例化的时候得到 ObjectContext 对象。让我们来看下 UnitOfWork 如何创建仓储类并且传递到Controller中的。

public class UnitOfWork : IDisposable
{
private MyDbContext dbContext = null;
public UnitOfWork()
{
dbContext = new MyDbContext();
} IRepository<Book> bookReporsitory = null; public IRepository<Book> BookRepository
{
get
{
if (bookReporsitory == null)
{
bookReporsitory = new BooksRepositoryWithUow(dbContext);
}
return bookReporsitory;
}
} public void SaveChanges()
{
dbContext.SaveChanges();
} private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
dbContext.Dispose();
}
this.disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}

现在我们在创建一个Controller,命名为 BooksUowController 将通过调用 工作单元类来实现 Book 表的CRUD操作

public class BooksUowController : Controller
{
private UnitOfWork uow = null;
//private MyDbContext db = new MyDbContext(); public BooksUowController() {
uow = new UnitOfWork();
}
public BooksUowController(UnitOfWork _uow)
{
this.uow = _uow;
} // GET: BookUow
public ActionResult Index()
{
return View(uow.BookRepository.GetAll().ToList());
} // GET: BookUow/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Book book = uow.BookRepository.Get(b => b.Id == id.Value);
if (book == null)
{
return HttpNotFound();
}
return View(book);
} // GET: BookUow/Create
public ActionResult Create()
{
return View();
} // POST: BookUow/Create
// 为了防止“过多发布”攻击,请启用要绑定到的特定属性,有关
// 详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=317598。
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Cover,BookName,Author,TranslatedName,Translator,Publisher,WordCount,Pages,ISBN,Price,Introduction,AboutTheAuthors,Link")] Book book)
{
if (ModelState.IsValid)
{
uow.BookRepository.Add(book);
uow.SaveChanges();
return RedirectToAction("Index");
} return View(book);
} // GET: BookUow/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Book book = uow.BookRepository.Get(b => b.Id == id.Value);
if (book == null)
{
return HttpNotFound();
}
return View(book);
} // POST: BookUow/Edit/5
// 为了防止“过多发布”攻击,请启用要绑定到的特定属性,有关
// 详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=317598。
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "Id,Cover,BookName,Author,TranslatedName,Translator,Publisher,WordCount,Pages,ISBN,Price,Introduction,AboutTheAuthors,Link")] Book book)
{
if (ModelState.IsValid)
{
uow.BookRepository.Update(book);
uow.SaveChanges(); return RedirectToAction("Index");
}
return View(book);
} // GET: BookUow/Delete/5
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Book book = uow.BookRepository.Get(b => b.Id == id.Value);
if (book == null)
{
return HttpNotFound();
}
return View(book);
} // POST: BookUow/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Book book = uow.BookRepository.Get(b => b.Id == id);
uow.BookRepository.Delete(book);
uow.SaveChanges();
return RedirectToAction("Index");
} }

现在,Controller通过默认的构造函数实现了可测试能力。例如,测试项目可以为 UnitOfWork 传入虚拟的测试数据来代替真实数据。同样数据访问的代码也被集中到一个地方。

通用仓储和工作单元

现在我们已经创建了仓储类和 工作单元类。现在的问题是如果数据库包含很多表,那样我们需要创建很多仓储类,然后我们的工作单元类需要为每个仓储类创建一个访问属性

如果为所有的Mode类创建一个通用的仓储类和 工作单元类岂不是更好,所以我们继续来实现一个通用的仓储类。

 public class GenericRepository<T> : IRepository<T> where T : class
{
private MyDbContext dbContext = null;
IDbSet<T> _objectSet; public GenericRepository(MyDbContext _dbContext)
{
dbContext = _dbContext;
_objectSet = dbContext.Set<T>();
} public IEnumerable<T> GetAll(Expression< Func<T, bool>> predicate = null)
{
if (predicate != null)
{
return _objectSet.Where(predicate);
}
return _objectSet.AsEnumerable();
}
public T Get(Expression<Func<T, bool>> predicate)
{
return _objectSet.First(predicate);
}
public void Add(T entity)
{
_objectSet.Add(entity);
} public void Update(T entity)
{
_objectSet.Attach(entity);
} public void Delete(T entity)
{
_objectSet.Remove(entity);
} public IEnumerable<T> GetAll(Func<T, bool> predicate = null)
{
if (predicate != null)
{
return _objectSet.Where(predicate);
}
return _objectSet.AsEnumerable();
} public T Get(Func<T, bool> predicate)
{
return _objectSet.First(predicate);
}
}

UPDATE: 发现一个很有用的评论,我认为应该放在文章中分享一下

在.NET中,对‘Where’至少有两个重写方法:

public static IQueryable Where(this IQueryable source, Expression> predicate);
public static IEnumerable Where(this IEnumerable source, Func predicate);

现在我们正在使用的是

Func<T, bool>

现在的查询将会使用'IEnumerable'版本,在示例中,首先从数据库中取出整个表的记录,然后再执行过滤条件取得最终的结果。想要证明这一点,只要去看看生成的sql语句,它是不包含Where字句的。

若要解决这个问题,我们需要修改'Func' to 'Expression Func'.

Expression<Func<T, bool>> predicate 

现在 'Where'方法使用的就是 'IQueryable'版本了。

Note: 因此看来,使用 Expression Func 比起使用 Func是更好的主意.

现在使用通用的仓储类,我们需要创建一个对应的工作单元类。这个工作单元类将检查仓储类是否已经创建,如果存在将返回一个实例,否则将创建一个新的实例。

 public class GenericUnitOfWork:IDisposable
{
private MyDbContext dbContext=null; public GenericUnitOfWork()
{
dbContext = new MyDbContext();
}
public Dictionary<Type, object> repositories = new Dictionary<Type, object>(); public IRepository<T> Repository<T>() where T : class
{
if (repositories.Keys.Contains(typeof(T)) == true)
{
return repositories[typeof(T)] as IRepository<T>;
}
IRepository<T> repo=new GenericRepository<T>(dbContext);
repositories.Add(typeof(T), repo);
return repo;
}
public void SaveChanges()
{
dbContext.SaveChanges();
} private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
dbContext.Dispose();
}
}
this.disposed = true;
} public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}

然后,我们在创建一个使用通用工作单元类 GenericUnitOfWork 的Controller,命名为GenericContactsController ,完成对 Book 表的CRUD操作。

 public class GenericBooksController : Controller
{
private GenericUnitOfWork uow = null;
//private MyDbContext db = new MyDbContext(); public GenericBooksController()
{
uow = new GenericUnitOfWork();
} public GenericBooksController(GenericUnitOfWork uow)
{
this.uow = uow;
} // GET: GenericBooks
public ActionResult Index()
{
return View(uow.Repository<Book>().GetAll().ToList());
} // GET: GenericBooks/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Book book = uow.Repository<Book>().Get(b=>b.Id==id.Value);
if (book == null)
{
return HttpNotFound();
}
return View(book);
} // GET: GenericBooks/Create
public ActionResult Create()
{
return View();
} // POST: GenericBooks/Create
// 为了防止“过多发布”攻击,请启用要绑定到的特定属性,有关
// 详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=317598。
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Cover,BookName,Author,TranslatedName,Translator,Publisher,WordCount,Pages,ISBN,Price,Introduction,AboutTheAuthors,Link")] Book book)
{
if (ModelState.IsValid)
{
uow.Repository<Book>().Add(book);
uow.SaveChanges();
return RedirectToAction("Index");
} return View(book);
} // GET: GenericBooks/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Book book = uow.Repository<Book>().Get(b => b.Id == id.Value);
if (book == null)
{
return HttpNotFound();
}
return View(book);
} // POST: GenericBooks/Edit/5
// 为了防止“过多发布”攻击,请启用要绑定到的特定属性,有关
// 详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=317598。
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "Id,Cover,BookName,Author,TranslatedName,Translator,Publisher,WordCount,Pages,ISBN,Price,Introduction,AboutTheAuthors,Link")] Book book)
{
if (ModelState.IsValid)
{
uow.Repository<Book>().Update(book);
uow.SaveChanges();
return RedirectToAction("Index");
}
return View(book);
} // GET: GenericBooks/Delete/5
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Book book = uow.Repository<Book>().Get(b => b.Id == id.Value);
if (book == null)
{
return HttpNotFound();
}
return View(book);
} // POST: GenericBooks/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Book book = uow.Repository<Book>().Get(b => b.Id == id);
uow.Repository<Book>().Delete(book);
uow.SaveChanges();
return RedirectToAction("Index");
} }

现在,我们已经在解决方案中现实了一个通用的仓储类和工作单元类

要点总结

在这篇文章中,我们理解了仓储模式和工作单元模式。我们也在ASP.NET MVC应用中使用Entity Framework实现了简单的仓储模式和工作单元模式。然后我们创建了一个通用的仓储类和工作单元类来避免在一大堆仓储类中编写重复的代码。我希望你在这篇文章中能有所收获

History

07 May 2014: First version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

译注

原文采用objectContext,使用EF图形化建模编写的示例代码,译者修改code first形式

参考

https://msdn.microsoft.com/en-us/data/jj592676.aspx

https://msdn.microsoft.com/en-us/library/system.data.entity.dbset(v=vs.113).aspx

http://stackoverflow.com/questions/8947427/differences-between-createobjectsett-sett-and-createqueryt

转载自:http://www.cnblogs.com/wit13142/p/5432147.html

MVC+EF 理解和实现仓储模式和工作单元模式的更多相关文章

  1. [.NET领域驱动设计实战系列]专题五:网上书店规约模式、工作单元模式的引入以及购物车的实现

    一.前言 在前面2篇博文中,我分别介绍了规约模式和工作单元模式,有了前面2篇博文的铺垫之后,下面就具体看看如何把这两种模式引入到之前的网上书店案例里. 二.规约模式的引入 在第三专题我们已经详细介绍了 ...

  2. 仓储(Repository)和工作单元模式(UnitOfWork)

    仓储和工作单元模式 仓储模式 为什么要用仓储模式 通常不建议在业务逻辑层直接访问数据库.因为这样可能会导致如下结果: 重复的代码 编程错误的可能性更高 业务数据的弱类型 更难集中处理数据,比如缓存 无 ...

  3. 5.在MVC中使用泛型仓储模式和工作单元来进行增删查改

    原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-operations-using-the-generic-repository-pat ...

  4. 在MVC中使用泛型仓储模式和工作单元来进行增删查改

    原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-operations-using-the-generic-repository-pat ...

  5. Contoso 大学 - 9 - 实现仓储和工作单元模式

    原文 Contoso 大学 - 9 - 实现仓储和工作单元模式 By Tom Dykstra, Tom Dykstra is a Senior Programming Writer on Micros ...

  6. 关于工作单元模式——工作单元模式与EF结合的使用

    工作单元模式往往和仓储模式一起使用,本篇文章讲到的是工作单元模式和仓储模式一起用来在ef外面包一层,其实EF本身就是工作单元模式和仓储模式使用的经典例子,其中DbContext就是工作单元,而每个Db ...

  7. [.NET领域驱动设计实战系列]专题四:前期准备之工作单元模式(Unit Of Work)

    一.前言 在前一专题中介绍了规约模式的实现,然后在仓储实现中,经常会涉及工作单元模式的实现.然而,在我的网上书店案例中也将引入工作单元模式,所以本专题将详细介绍下该模式,为后面案例的实现做一个铺垫. ...

  8. 演练5-8:Contoso大学校园管理系统(实现存储池和工作单元模式)

    在上一次的教程中,你已经使用继承来消除在 Student 和 Instructor 实体之间的重复代码.在这个教程中,你将要看到使用存储池和工作单元模式进行增.删.改.查的一些方法.像前面的教程一样, ...

  9. 重新整理 .net core 实践篇—————工作单元模式[二十六]

    前言 简单整理一下工作单元模式. 正文 工作单元模式有3个特性,也算是其功能: 使用同一上下文 跟踪实体的状态 保障事务一致性 工作单元模式 主要关注事务,所以重点在事务上. 在共享层的基础建设类库中 ...

随机推荐

  1. 身份证校验(java)

    判断是第几代身份证(第一代15位, 第二代18位) if (cardId.length() == 15 || cardId.length() == 18) { if (!this.cardCodeVe ...

  2. js中同步与异步请求方式

    异步请求方式: $.ajax({ url : 'your url', data:{name:value}, cache : false, async : true, type : "POST ...

  3. CentOS6.5搭建LNMP

      1:查看环境: ? 1 2 [root@10-4-14-168 html]# cat /etc/redhat-release CentOS release 6.5 (Final) 2:关掉防火墙 ...

  4. java enum(枚举)的使用

    在实际编程中,往往存在着这样的“数据集”,它们的数值在程序中是稳定的,而且“数据集”中的元素是有限的. 例如星期一到星期日七个数据元素组成了一周的“数据集”,春夏秋冬四个数据元素组成了四季的“数据集” ...

  5. 报表session与应用session常识普及

    1. 报表session与应用session 报表集成到项目中可能会有一个疑问就是系统应用和报表应用在一个web服务器下,那系统session和报表session是不是一个session呢?如果不是那 ...

  6. finereport普通报表的移动端自适应方案

    移动端报表呈现,首先要求的是页面随手机屏幕大小自动放缩(自适应),下面给出一个普通报表中的finereport移动端自适应方案,适用于finereport 7.1之前的版本. 首先,了解一下当前我们可 ...

  7. unix automake 使用,快速生成你的Makefile

    使用automake快速生成编译的Makefile 1,确保自己装有的软件automake autoconf 2, 1)执行autoscan 并将生成的configure.scan重命名为config ...

  8. Network Experiment Environment Construction

    In order to provide an environment for network experiments in the future, I use VirutalBox to create ...

  9. SpringMVC总结帖

    SpringMVC是基于MVC设计理念的一款优秀的Web框架,是目前最流行的MVC框架之一,SpringMVC通过一套注解,让POPJ成为处理请求的控制器,而无需实现任何接口,然后使用实现接口的控制器 ...

  10. 代理模式 vs 装饰模式

    代理模式和装饰模式有很大的相似性,二者的类图(几乎)是一样的.下面分别讲解代理模式和装饰模式. 1.代理模式 一般著名的跑步运动员都会有自己的代理人,如果想联系该运动员的比赛事宜,可以直接联系他的代理 ...