通常,在同一個頁面上實現增刪改查,會通過彈出框實現非同步的新增和修改,這很好。但有些時候,是不希望在頁面上彈出框的,我們可能會想到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層等"。
下一篇進入增刪改查的介面設計。