通常,在同一個頁面上實現增刪改查,會通過彈出框實現異步的添加和修改,這很好。但有些時候,是不希望在頁面上彈出框的,我們可能會想到Knockoutjs,它能以MVVM模式實現同一個頁面上的增刪改查,再輔以knockout.validation.js,還可以對Model進行驗證。但knockout.validation.js與ASP.NET MVC本身的驗證沒有做到無縫對接,不能形成一個從客戶端到服務端連貫、統一的驗證解決方案。關於在ASP.NET MVC中使用Knockoutjs和knockout.validation.js,不知道各位朋友有沒有好的案例?
於是,驀然回首,jQuery在燈火闌珊處無比堅定地說:我已經和ASP.NET MVC聯袂好久了,什么客戶端驗證、服務端驗證,那都不是事!大致想做成這樣:
Repository的搭建
在Models文件夾下創建一個簡單的領域模型。
namespace MvcApplication3.Models{public class Product{public int Id { get; set; }public string Name { get; set; }public string Category { get; set; }public decimal Price { get; set; }}}
通過EF Code First創建數據庫初始數據。首先有一個派生於DbContext的EF上下文。
using System.Data.Entity;namespace MvcApplication3.Models{public class ProductContext : DbContext{public ProductContext() : base("conn"){Database.SetInitializer(new ProductInitializer());}public DbSet<Product> Products { get; set; }}}
數據庫數據的初始化工作是在ProductInitializer類中完成的。
using System.Data.Entity;namespace MvcApplication3.Models{public class ProductInitializer : DropCreateDatabaseIfModelChanges<ProductContext>{protected override void Seed(ProductContext context){context.Products.Add(new Product() {Name = "秋意真濃", Price = 85M, Category = "散文"});context.Products.Add(new Product() {Name = "冬日戀歌", Price = 95M, Category = "小說" });context.Products.Add(new Product() { Name = "春暖花開", Price = 105M, Category = "散文" });}}}
在Web.config中需要配置一下有關EF的連接字符串。
<connectionStrings>......<add name="conn" connectionString="Data Source=.;User=用戶名;Password=密碼;Initial Catalog=ProductStore;Integrated Security=True" providerName="System.Data.SqlClient" /></connectionStrings>
倉儲層首先需要一個接口。
using System.Collections.Generic;namespace MvcApplication3.Models{public interface IProductRepository{IEnumerable<Product> GetAll(); //獲取所有IEnumerable<Product> LoadProductPageData(ProductParam p, out int total);//獲取分頁數據Product GetById(int id); //根據id獲取,通常顯示更新頁面時使用Product Add(Product item);//添加Product Update(Product item);//更新bool Delete(int id);//刪除int DeleteBatch(string[] ids);//批量刪除}}
倉儲接口的實現通過EF上下文。
using System;using System.Collections.Generic;using System.Data;using System.Linq;namespace MvcApplication3.Models{public class ProductRepository : IProductRepository{private ProductContext db = new ProductContext();/// <summary>/// 獲取所有/// </summary>/// <returns></returns>public System.Collections.Generic.IEnumerable<Product> GetAll(){return db.Products;}/// <summary>/// 獲取分頁數據/// </summary>/// <param name="para">查詢參數,包括:PageInde, PageSize,與Product有關的Name,Category</param>/// <param name="total">查詢條件過濾之后的記錄總數</param>/// <returns></returns>public IEnumerable<Product> LoadProductPageData(ProductParam para, out int total){var products = db.Products.AsEnumerable();if (!string.IsNullOrEmpty(para.Name)){products = products.Where(p => p.Name.Contains(para.Name));}if (!string.IsNullOrEmpty(para.Category)){products = products.Where(p => p.Category.Contains(para.Category));}total = products.Count();IEnumerable<Product> result = products.OrderByDescending(x => x.Id).Skip(para.PageSize * (para.PageIndex - 1)).Take(para.PageSize);return result;}/// <summary>/// 根據Id獲取/// </summary>/// <param name="id"></param>/// <returns></returns>public Product GetById(int id){return db.Products.Find(id);}/// <summary>/// 添加/// </summary>/// <param name="item"></param>/// <returns></returns>public Product Add(Product item){db.Products.Add(item);db.SaveChanges();return item;}/// <summary>/// 更新/// </summary>/// <param name="item"></param>/// <returns></returns>public Product Update(Product item){try{if (item == null){throw new ArgumentException("Product不能為null");}var entry = db.Entry(item);if (entry.State == EntityState.Detached){var set = db.Set<Product>();Product attachedProduct = set.Local.SingleOrDefault(p => p.Id == item.Id);//如果已經被上下文追蹤if (attachedProduct != null){var attachedEntry = db.Entry(attachedProduct);attachedEntry.CurrentValues.SetValues(item);}else //如果不在當前上下文追蹤{entry.State = EntityState.Modified;}}db.SaveChanges();return item;}catch (Exception){throw;}}/// <summary>/// 刪除/// </summary>/// <param name="id"></param>/// <returns></returns>public bool Delete(int id){Product product = db.Products.Find(id);db.Products.Remove(product);if (db.SaveChanges() > 0){return true;}else{return false;}}/// <summary>/// 批量刪除/// </summary>/// <param name="ids"></param>/// <returns></returns>public int DeleteBatch(string[] ids){foreach (string id in ids){Delete(int.Parse(id));}return -1;}}}
以上,需要特別說明的是Update方法,為類避免在更新的時候報類似"ObjectStateManager 中已存在具有同一鍵的對象。ObjectStateManager 無法跟蹤具有相同鍵的多個對象"錯,因為,在EF上上下文中是不允許存在2個具有相同鍵的實體的。在更新的時候,我們需要判斷需要被更新的實體是否已經被當前上下文追蹤。
當然,在三層架構中,我們可以通過CallContext線程槽獲取到當前線程內的唯一EF上下文實例。詳細做法請參考"MVC項目實踐,在三層架構下實現SportsStore-01,EF Code First建模、DAL層等"。
下一篇進入增刪改查的界面設計。



