在此介紹如何在 Controller 之下建置底層,使 Controller 變得只需要宣告、繼承、實作即可與原本 Controller 能做的事情一樣:
namespace RMSApi.Controllers { public class CompanyController : BaseApiController{ // 這裡是空的,真的是這樣 } }
步驟是研究與參考 Generic Repository Pattern with Entity Framework and Web API ( Entity Framework 和 Web API 的通用 Repository 模式 ),目前唯一的限制就是你的資料庫 Key 值名稱都要一樣,我目前專案已經有了這項衝突,所以這個方法介紹給有緣人,建置過程必須依照以下方式:
1.
在 Model 內新增一個 Interface 資料夾,建立一個 interface 類別 IRepository 繼承 IDisposable,裡面宣告一些常用的方法:using System; using System.Data.SqlClient; using System.Linq; using System.Linq.Expressions; namespace RMSApi.Models.Interface { public interface IRepository: IDisposable { /* 定義需要實作的函數 */ IQueryable<T> All<T>(string[] includes = null) where T : class; T Get<T>(Expression<Func<T, bool>> expression, string[] includes = null) where T : class; T Find<T>(Expression<Func<T, bool>> predicate, string[] includes = null) where T : class; IQueryable<T> Filter<T>(Expression<Func<T, bool>> predicate, string[] includes = null) where T : class; IQueryable<T> Filter<T>(Expression<Func<T, bool>> filter, out int total, int index = 0, int size = 50, string[] includes = null) where T : class; bool Contains<T>(Expression<Func<T, bool>> predicate) where T : class; T Create<T>(T t) where T : class; int Delete<T>(T t) where T : class; int Delete<T>(Expression<Func<T, bool>> predicate) where T : class; int Update<T>(T t) where T : class; void SaveChanges(); void ExecuteProcedure(String procedureCommand, params SqlParameter[] sqlParams); } }
2.
接著建立每個 Model 內一定會出現的資料欄位的 Interface,視習慣建立兩個或者統整到同一個檔案中,像我這裡是 CreateOn 和 UpdateOn。using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace RMSApi.Models.Interface { public interface ICreateOn { DateTime CreateOn { get; set; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace RMSApi.Models.Interface { public interface IUpdateOn { DateTime UpdateOn { get; set; } } }
再建立一個 IIdentifier,這就是你每個 Model 的 Key 值,需要一模一樣。
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace RMSApi.Models.Interface { public interface IIdentifier { Guid ID { get; set; } } }
3.
在 Model 內新增一個 Repository 資料夾,建立一個類別 Repository 繼承 IRepository,裡面宣告一些常用的方法:using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Data; using System.Data.SqlClient; using RMSApi.Models.Interface; using RMSApi.Models; using System.Linq.Expressions; using System.Web.Http; namespace RMSApi.Models.Repository { public class Repository : IRepository { private DefaultConnection db; public Repository() { db = new DefaultConnection(); // db.Configuration.ProxyCreationEnabled = false; // db.Configuration.LazyLoadingEnabled = false; } public IQueryable<T> All<T>(string[] includes = null) where T : class { if (includes != null && includes.Count() > 0) { var query = db.Set<T>().Include(includes.First()); foreach (var include in includes.Skip(1)) query = query.Include(include); return query.AsQueryable(); } return db.Set<T>().AsQueryable(); } public T Get<T>(Expression<Func<T, bool>> expression, string[] includes = null) where T : class { return All<T>(includes).FirstOrDefault(expression); } public virtual T Find<T>(Expression<Func<T, bool>> predicate, string[] includes = null) where T : class { if (includes != null && includes.Count() > 0) { var query = db.Set<T>().Include(includes.First()); foreach (var include in includes.Skip(1)) query = query.Include(include); return query.FirstOrDefault<T>(predicate); } return db.Set<T>().FirstOrDefault<T>(predicate); } public virtual IQueryable<T> Filter<T>(Expression<Func<T, bool>> predicate, string[] includes = null) where T : class { if (includes != null && includes.Count() > 0) { var query = db.Set<T>().Include(includes.First()); foreach (var include in includes.Skip(1)) query = query.Include(include); return query.Where<T>(predicate).AsQueryable<T>(); } return db.Set<T>().Where<T>(predicate).AsQueryable<T>(); } public virtual IQueryable<T> Filter<T>(Expression<Func<T, bool>> predicate, out int total, int index = 0, int size = 50, string[] includes = null) where T : class { int skipCount = index * size; IQueryable<T> _resetSet; if (includes != null && includes.Count() > 0) { var query = db.Set<T>().Include(includes.First()); foreach (var include in includes.Skip(1)) query = query.Include(include); _resetSet = predicate != null ? query.Where<T>(predicate).AsQueryable() : query.AsQueryable(); } else { _resetSet = predicate != null ? db.Set<T>().Where<T>(predicate).AsQueryable() : db.Set<T>().AsQueryable(); } _resetSet = skipCount == 0 ? _resetSet.Take(size) : _resetSet.Skip(skipCount).Take(size); total = _resetSet.Count(); return _resetSet.AsQueryable(); } public virtual T Create<T>(T TObject) where T : class { if (TObject is ICreateOn) { (TObject as ICreateOn).CreateOn = DateTime.Now; } if (TObject is IUpdateOn) { (TObject as IUpdateOn).UpdateOn = DateTime.Now; } var newEntry = db.Set<T>().Add(TObject); db.SaveChanges(); return newEntry; } public virtual int Delete<T>(T TObject) where T : class { db.Set<T>().Remove(TObject); return db.SaveChanges(); } public virtual int Update<T>(T TObject) where T : class { if (TObject is IUpdateOn) { (TObject as IUpdateOn).UpdateOn = DateTime.UtcNow; } var entry = db.Entry(TObject); db.Set<T>().Attach(TObject); entry.State = EntityState.Modified; return db.SaveChanges(); } public virtual int Delete<T>(Expression<Func<T, bool>> predicate) where T : class { var objects = Filter<T>(predicate); foreach (var obj in objects) db.Set<T>().Remove(obj); return db.SaveChanges(); } public bool Contains<T>(Expression<Func<T, bool>> predicate) where T : class { return db.Set<T>().Count<T>(predicate) > 0; } public virtual void ExecuteProcedure(String procedureCommand, params SqlParameter[] sqlParams) { db.Database.ExecuteSqlCommand(procedureCommand, sqlParams); } public virtual void SaveChanges() { db.SaveChanges(); } public void Dispose() { if (db != null) db.Dispose(); } } }
4.
最後建立一個類別繼承 ApiController 實作 IIdentifier,且實作在 Api 中會使用的方法 ( CRUD )。using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; using RMSApi.Models; using RMSApi.Filters; using RMSApi.Models.Repository; using RMSApi.Models.Interface; using System.Data; namespace RMSApi.Controllers { public class BaseApiController<T> : ApiController where T : class, IIdentifier { protected DefaultConnection db = new DefaultConnection(); protected IRepository DataStore { get; set; } protected string[] Includes { get; set; } public BaseApiController() { this.DataStore = new Repository(); } public virtual IEnumerable<T> Get() { return DataStore.All<T>(Includes); } public virtual T Get(Guid id) { return DataStore.Find<T>(t => t.ID == id, Includes); } public virtual void Post([FromBody]T value) { try { DataStore.Update<T>(value); } catch (OptimisticConcurrencyException ex) { throw ex; } } public virtual void Put([FromBody]T value) { DataStore.Create<T>(value); } public virtual void Delete(Guid id) { DataStore.Delete<T>(t => t.ID == id); } public virtual void Delete([FromBody]T value) { Delete(value.ID); } protected IEnumerable GetModelErrors() { return this.ModelState.SelectMany(x => x.Value.Errors.Select(error => error.ErrorMessage)); } } }
5.
最後在每一個 Controller 中都繼承 BaseApiController 並且帶入類別即可。namespace RMSApi.Controllers { public class CompanyController : BaseApiController{ // 這裡是空的,真的是這樣 } }
這就是完整的把 Controller 的共同部分,也就是資料處理的部分抽離出來,讓你在 Controller 看到的程式碼更簡潔 ( 其實完全沒有了! )。如果你就在這 Controller 有獨特取得的資料方式,那就是在各隻 Controller 加上就好,比較直觀亦較好維護。
沒有留言 :
張貼留言