在ASP.NET MVC4中實現同頁面增刪改查,無彈出框01,Repository的搭建

Darren Ji發表於2014-11-06

通常,在同一個頁面上實現增刪改查,會通過彈出框實現非同步的新增和修改,這很好。但有些時候,是不希望在頁面上彈出框的,我們可能會想到Knockoutjs,它能以MVVM模式實現同一個頁面上的增刪改查,再輔以knockout.validation.js,還可以對Model進行驗證。但knockout.validation.jsASP.NET MVC本身的驗證沒有做到無縫對接,不能形成一個從客戶端到服務端連貫、統一的驗證解決方案。關於在ASP.NET MVC中使用Knockoutjsknockout.validation.js,不知道各位朋友有沒有好的案例?


於是,驀然回首,jQuery在燈火闌珊處無比堅定地說:我已經和ASP.NET MVC聯袂好久了,什麼客戶端驗證、服務端驗證,那都不是事!大致想做成這樣:

 

顯示的時候,只出現資料列表和搜尋條件:
15

 

當點選"新增"按鈕,在列表和搜尋區域上方出現新增區域:
16

 

當點選"修改"按鈕,在列表和搜尋區域上方出現修改區域:
17


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層等"。

 

下一篇進入增刪改查的介面設計。

相關文章