初探 Entity Framework 6 前面已經說明過幾個新功能:
初探 Entity Framework 6 的 Async/Await 功能
初探 Entity Framework 6 – Logging
初探 Entity Framework 6 - Intercepting Part.1
初探 Entity Framework 6 - Intercepting Part.2
而第一篇只有針對資料讀取的部份說明而已,還沒有對新增、修改、刪除來做說明,但其實非同步的新增修改刪除並沒有什麼特別的地方,所以這一篇就以 ASP.NET MVC 5 Controller 開始來說明如何將 Controller 裡基本的 CRUD 使用非同步的方式來做處理。
在之前我們所建立的 IRepository<TEntity> 只有讀取資料的方法,
現在我們再新增三個方法,分別為 Create, Update, Delete,而且都是要使用非同步處理,
IRepository<TEntity> 的全貌
接著我們要在 GenericRepository<TEntity> 裡實做新增的三個方法,
這裡我另外建立的一個 Model「Member」,接下來將使用這個 Model 來建立 Controller 以及使用非同步處理 CRUD 操作,
建立 Controller
在「添加基架」的視窗裡,選擇「具有檢視、使用 Entity Framework 的 MVC 5 控制器」
接著在「加入控制器」視窗裡要勾選「使用非同步控制器動作」,記得模型類別是選擇「Member」,
完成控制器的加入操作後,會建立好 MemberController 內容以及檢視頁面,
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;
using System.Net;
using System.Web;
using System.Web.Mvc;
using Async.Models;
namespace Async.Controllers
{
public class MemberController : Controller
{
private NorthwindEntities db = new NorthwindEntities();
// GET: /Member/
public async Task<ActionResult> Index()
{
return View(await db.Members.ToListAsync());
}
// GET: /Member/Details/5
public async Task<ActionResult> Details(Guid? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Member member = await db.Members.FindAsync(id);
if (member == null)
{
return HttpNotFound();
}
return View(member);
}
// GET: /Member/Create
public ActionResult Create()
{
return View();
}
// POST: /Member/Create
// 若要免於過量張貼攻擊,請啟用想要繫結的特定屬性,如需
// 詳細資訊,請參閱 http://go.microsoft.com/fwlink/?LinkId=317598。
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include="ID,Name,Gender,BirthDate,CreateDate,UpdateDate")] Member member)
{
if (ModelState.IsValid)
{
member.ID = Guid.NewGuid();
db.Members.Add(member);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(member);
}
// GET: /Member/Edit/5
public async Task<ActionResult> Edit(Guid? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Member member = await db.Members.FindAsync(id);
if (member == null)
{
return HttpNotFound();
}
return View(member);
}
// POST: /Member/Edit/5
// 若要免於過量張貼攻擊,請啟用想要繫結的特定屬性,如需
// 詳細資訊,請參閱 http://go.microsoft.com/fwlink/?LinkId=317598。
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include="ID,Name,Gender,BirthDate,CreateDate,UpdateDate")] Member member)
{
if (ModelState.IsValid)
{
db.Entry(member).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(member);
}
// GET: /Member/Delete/5
public async Task<ActionResult> Delete(Guid? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Member member = await db.Members.FindAsync(id);
if (member == null)
{
return HttpNotFound();
}
return View(member);
}
// POST: /Member/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> DeleteConfirmed(Guid id)
{
Member member = await db.Members.FindAsync(id);
db.Members.Remove(member);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
}
修改 Controller
不過我們要置換 MemberController 裡的程式,將原本 Action 方法內直接對 EntityFramework 存取的部份換成使用 GenericRepository<TEntity>,修改後並且使用非同步處理的 MemberController 如下:
using System;
using System.Net;
using System.Threading.Tasks;
using System.Web.Mvc;
using Async.Models;
using Async.Repository;
namespace Async.Controllers
{
public class MemberController : Controller
{
private IRepository<Member> memberRepository = new GenericRepository<Member>();
public async Task<ActionResult> Index()
{
var result = await memberRepository.GetAllAsync();
return View(result);
}
public async Task<ActionResult> Details(Guid? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Member member = await this.memberRepository.GetAsync(x => x.ID == id.Value);
if (member == null)
{
return HttpNotFound();
}
return View(member);
}
public ActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create(
[Bind(Include = "ID,Name,Gender,BirthDate")] Member member)
{
if (ModelState.IsValid)
{
member.ID = Guid.NewGuid();
member.CreateDate = DateTime.Now;
member.UpdateDate = DateTime.Now;
await this.memberRepository.CreateAsync(member);
return RedirectToAction("Index");
}
return View(member);
}
public async Task<ActionResult> Edit(Guid? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Member member = await this.memberRepository.GetAsync(x => x.ID == id.Value);
if (member == null)
{
return HttpNotFound();
}
return View(member);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(
[Bind(Include = "ID,Name,Gender,BirthDate")] Member member)
{
if (ModelState.IsValid)
{
Member original = await this.memberRepository.GetAsync(x => x.ID == member.ID);
original.Name = member.Name;
original.Gender = member.Gender;
original.BirthDate = member.BirthDate;
original.UpdateDate = DateTime.Now;
await this.memberRepository.UpdateAsync(original);
return RedirectToAction("Index");
}
return View(member);
}
public async Task<ActionResult> Delete(Guid? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Member member = await this.memberRepository.GetAsync(x => x.ID == id.Value);
if (member == null)
{
return HttpNotFound();
}
return View(member);
}
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> DeleteConfirmed(Guid id)
{
Member member = await this.memberRepository.GetAsync(x => x.ID == id);
await this.memberRepository.DeleteAsync(member);
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
this.memberRepository.Dispose();
}
base.Dispose(disposing);
}
}
}
Entity Framework 6 直接提供了非同步處理的功能,這篇文章以其之前的「初探 Entity Framework 6 的 Async/Await 功能」直接跟大家說明如何在 Repository 裡建立非同步處理的方法,其實這就跟以前把 Controller 裡直接對 EF 進行資料存取操作的部份抽出來放在 Repository 的方式是一樣的,不一樣的地方在於如果要使用非同步處理就必須做調整,例如 Controller 的 Action 方法就必須要將方法簽名增加 async 關鍵字,然後 ActionResult 要修改為 Task<ActionResult>。
另外在 IRepository 以及 GenericRepository 的實作上,不需要使用非同步處理去取代原本沒有使用非同步處理的方法,兩者可以並存,在需要使用非同步處理或不需要使用的時候就可以做調正,例如原本的 GetAll 與 GetAllAsync 兩個方法,都是做同一個資料讀取的操作,差別只在於是否使用非同步處理,
補充 IRepository 與 GenericRepository 內容
IRepository.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace Async.Repository
{
public interface IRepository<TEntity> : IDisposable
where TEntity : class
{
Task CreateAsync(TEntity instance);
Task UpdateAsync(TEntity instance);
Task DeleteAsync(TEntity instance);
TEntity Get(Expression<Func<TEntity, bool>> predicate);
Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate);
Task<TEntity> GetWithIncludeAsync(Expression<Func<TEntity, bool>> predicate, params Expression<Func<TEntity, object>>[] includes);
IQueryable<TEntity> GetAll();
Task<List<TEntity>> GetAllAsync();
IQueryable<TEntity> GetAllWithInclude(params Expression<Func<TEntity, object>>[] includes);
Task<List<TEntity>> GetAllWithIncludeAsync(params Expression<Func<TEntity, object>>[] includes);
IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);
IQueryable<TEntity> FindWithInclude(Expression<Func<TEntity, bool>> predicate, params Expression<Func<TEntity, object>>[] includes);
}
}
GenericRepository.cs
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using System.Web;
using Async.Models;
namespace Async.Repository
{
public class GenericRepository<TEntity> : IRepository<TEntity>
where TEntity : class
{
private DbContext _context
{
get;
set;
}
private DbSet<TEntity> dbSet
{
get
{
return this._context.Set<TEntity>();
}
}
public GenericRepository()
: this(new NorthwindEntities())
{
}
public GenericRepository(DbContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
this._context = context;
this._context.Database.Log = (log) => Debug.WriteLine(log);
}
public async Task CreateAsync(TEntity instance)
{
this.dbSet.Add(instance);
await this._context.SaveChangesAsync();
}
public async Task UpdateAsync(TEntity instance)
{
this._context.Entry(instance).State = EntityState.Modified;
await this._context.SaveChangesAsync();
}
public async Task DeleteAsync(TEntity instance)
{
this.dbSet.Remove(instance);
await this._context.SaveChangesAsync();
}
public TEntity Get(Expression<Func<TEntity, bool>> predicate)
{
return this.dbSet.FirstOrDefault(predicate);
}
public async Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate)
{
return await this.dbSet.FirstOrDefaultAsync(predicate);
}
public async Task<TEntity> GetWithIncludeAsync(Expression<Func<TEntity, bool>> predicate, params Expression<Func<TEntity, object>>[] includes)
{
var query = this.dbSet.AsQueryable();
if (includes != null)
{
query = includes.Aggregate(
query,
(current, include) => current.Include(include));
}
var result = query.FirstOrDefaultAsync(predicate);
return await result;
}
public IQueryable<TEntity> GetAll()
{
return this.dbSet.AsQueryable();
}
public async Task<List<TEntity>> GetAllAsync()
{
return await this.dbSet.ToListAsync();
}
public IQueryable<TEntity> GetAllWithInclude(params Expression<Func<TEntity, object>>[] includes)
{
var query = this.dbSet.AsQueryable();
if (includes != null)
{
query = includes.Aggregate(
query,
(current, include) => current.Include(include));
}
return query;
}
public async Task<List<TEntity>> GetAllWithIncludeAsync(params Expression<Func<TEntity, object>>[] includes)
{
var query = this.dbSet.AsQueryable();
if (includes != null)
{
query = includes.Aggregate(
query,
(current, include) => current.Include(include));
}
return await query.ToListAsync();
}
public IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return this.dbSet.Where(predicate);
}
public IQueryable<TEntity> FindWithInclude(Expression<Func<TEntity, bool>> predicate, params Expression<Func<TEntity, object>>[] includes)
{
var query = this.dbSet.AsQueryable();
if (includes != null)
{
query = includes.Aggregate(
query,
(current, include) => current.Include(include));
}
return query.Where(predicate);
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (this._context != null)
{
this._context.Dispose();
this._context = null;
}
}
}
}
}
如果對於 Async/Await 的概念與操作還尚未了解的話,可以先仔細閱讀以下連結的文件說明,
MSDN - 使用 Async 和 Await 設計非同步程式 (C# 和 Visual Basic)
以上
沒有留言:
張貼留言