初探 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)
以上
沒有留言:
張貼留言