MVC無限級分類01,分層架構,引入快取,完成領域模型與檢視模型的對映

Darren Ji發表於2014-04-19

本系列將使用zTree來建立、編輯關於品牌、車系、車型的無限級分類,使用datagrid顯示,原始碼在github。先上最終效果:

 

datagrid顯示所有記錄、分頁,提供新增、修改、刪除按鈕,並提供簡單搜尋:
1

 

建立分類,彈出模態視窗,zTree顯示所有分類,點選勾選按鈕或節點,所選節點名稱顯示到文字框:
2
3

 

提供客戶端和服務端驗證,驗證不通過顯示出錯資訊:
4

 

修改分類,彈出模態視窗,zTree顯示當前選中的節點名稱:
5

 

  建立資料模型並生成到資料庫

→建立CarModel.edmx,建立模型,無限級分類的一切"神奇"從ParentID欄位開始。

6

 

→右鍵介面,選擇"根據模型生成資料庫"

7

 

→配置資料庫連線,執行sql語句......等等,最終生成如下檔案:

8

● App.config中的連線字串需要複製到MVC主站點的web.config中。
● CarModel.Context.cs中包含了繼承DbContext的上下文。
● CarModel.tt下包含了所有的領域模型Domain Models。
● CarModel.edmx.sql每次對映完執行裡面的sql語句,將把資料同步到資料庫。

 

  架構設計

由於主要是體驗無限級分類的增刪改查,架構做得相對簡單一些。其它元件列舉的只是示意,實際只用到了快取和序列化json的幫助類。如下:

structure

 

□ IBaseRepository是介面的基類,提供了所有介面的泛型實現

using System; 
using System.Collections.Generic; 
using System.Linq.Expressions;
 
namespace Car.Test.DAL 
{ 
    public interface IBaseRepository<T> where T : class,new() 
    { 
        IEnumerable<T> LoadEntities(Expression<Func<T, bool>> whereLambda); 
        IEnumerable<T> LoadEntitiesByCache(Expression<Func<T, bool>> whereLambda); 
        T AddEntity(T entity); 
        T UpdateEntity(T entity); 
        void ClearCache(); 
        int SaveChanges(); 
    } 
}

 

□ 其它介面只需要繼承該基類介面就可以

namespace Car.Test.DAL 
{ 
    public interface ICarCategoryRepository : IBaseRepository<Car.Test.Model.CarCategory> 
    { 
         
    } 
}

當然,如果Domain Model很多的話,這樣寫方便tt模版自動生成。

 

□ BaseRepository是Repository的基類,提供了所有Repository的泛型實現

提供了針對泛型的增刪改查,還包括快取查詢,提交變化。

using System.Data; 
using System.Linq; 
using Car.Test.Cache; 
using Car.Test.Model; 
using System; 
using System.Collections.Generic; 
using System.Linq.Expressions;
 
namespace Car.Test.DAL 
{ 
    public class BaseRepository<T> where T:class,new() 
    { 
        protected CarModelContainer DataContext { get; private set; } 
        public ICacheProvider Cache { get; set; }
 
        public BaseRepository(ICacheProvider cacheProvider) 
        { 
            this.DataContext = new CarModelContainer(); 
            this.Cache = cacheProvider; 
        }
 
        public BaseRepository():this(new DefaultCacheProvider()){}
 
        public virtual IEnumerable<T> LoadEntities(Expression<Func<T, bool>> whereLambda) 
        { 
            IEnumerable<T> temp = DataContext.Set<T>().Where(whereLambda).AsEnumerable(); 
            return temp; 
        }
 
        public virtual IEnumerable<T> LoadEntitiesByCache(Expression<Func<T, bool>> whereLambda) 
        { 
            IEnumerable<T> entities = Cache.Get(typeof(T).ToString()) as IEnumerable<T>; 
            if (entities == null) 
            { 
                entities = DataContext.Set<T>().Where(whereLambda).AsEnumerable(); 
                if (entities.Any()) 
                { 
                    Cache.Set(typeof(T).ToString(),entities,3); 
                } 
            } 
            return entities; 
        }
 
        public virtual T AddEntity(T entity) 
        { 
            DataContext.Set<T>().Add(entity); 
            return entity; 
        }
 
        public virtual T UpdateEntity(T entity) 
        { 
            if (entity != null) 
            { 
                DataContext.Set<T>().Attach(entity); 
                DataContext.Entry(entity).State = EntityState.Modified;
 
            } 
            return entity; 
        }
 
        public void ClearCache() 
        { 
            Cache.InValidate(typeof(T).ToString()); 
        }
 
        public int SaveChanges() 
        { 
            return DataContext.SaveChanges(); 
        } 
    } 
}
 

 

□ 其它Repository只需繼承BaseRepository並實現各自的介面

namespace Car.Test.DAL 
{ 
    public class CarCategoryRepository : BaseRepository<Car.Test.Model.CarCategory>,ICarCategoryRepository 
    { 
         
    } 
}

 

□ 快取介面

namespace Car.Test.Cache 
{ 
    public interface ICacheProvider 
    { 
        object Get(string key); 
        void Set(string key, object data, int cacheTime); 
        bool IsSet(string key); 
        void InValidate(string key); 
    } 
}

 

□ 快取實現,需要引入System.Runtime.Caching

using System; 
using System.Runtime.Caching;
 
namespace Car.Test.Cache 
{ 
    public class DefaultCacheProvider : ICacheProvider 
    { 
        private ObjectCache Cache 
        { 
            get { return MemoryCache.Default; } 
        }
 
        public object Get(string key) 
        { 
            return Cache[key]; 
        }
 
        public void Set(string key, object data, int cacheTime) 
        { 
            CacheItemPolicy policy = new CacheItemPolicy(); 
            policy.AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(cacheTime); 
            Cache.Add(new CacheItem(key, data), policy); 
        }
 
        public bool IsSet(string key) 
        { 
            return Cache[key] != null; 
        }
 
        public void InValidate(string key) 
        { 
            Cache.Remove(key); 
        } 
    } 
}
 

 

  使用AutoMapper對映領域模型和檢視模型

□ 檢視模型

using DataAnnotationsExtensions; 
using System.ComponentModel.DataAnnotations;
 
namespace Car.Test.Portal.Models 
{ 
    public class CarCategoryVm 
    { 
        public int ID { get; set; }
 
        [Display(Name = "類名")] 
        [Required(ErrorMessage = "必填")] 
        [StringLength(10, MinimumLength = 2,ErrorMessage = "長度2-10位")] 
        public string Name { get; set; }
 
        [Display(Name = "字首字母")] 
        [Required(ErrorMessage = "必填")] 
        [StringLength(1,ErrorMessage = "長度1位")] 
        public string PreLetter { get; set; }
 
        [Display(Name = "所屬父級")] 
        [Required(ErrorMessage = "必填")] 
        public int ParentID { get; set; }
 
        public System.DateTime SubTime { get; set; }
 
        [Display(Name = "層級(根節點為0級)")] 
        [Required(ErrorMessage = "必填")] 
        [Min(1, ErrorMessage = "至少為1")] 
        public int Level { get; set; }
 
        [Display(Name = "是否為頁節點")] 
        [Required(ErrorMessage = "必填")] 
        public bool IsLeaf { get; set; }
 
        public short Status { get; set; } 
        public short DelFlag { get; set; } 
    } 
}
 

引入DataAnnotationsExtensions元件,通過它可以設定最小值,關於DataAnnotationsExtensions的使用,在這裡

 

□ 繼承AutoMapper的Profile類,建立領域模型→檢視模型對映

using AutoMapper; 
using Car.Test.Model; 
using Car.Test.Portal.Models;
 
namespace Car.Test.Portal.Helpers.AutoMapper 
{ 
    public class DomainToVmProfile : Profile 
    { 
        protected override void Configure() 
        { 
            Mapper.CreateMap<CarCategory, CarCategoryVm>(); 
        } 
    } 
}
 

□ 繼承AutoMapper的Profile類,建立檢視模型→領域模型對映

using AutoMapper; 
using Car.Test.Model; 
using Car.Test.Portal.Models;
 
namespace Car.Test.Portal.Helpers.AutoMapper 
{ 
    public class VmToDomainProfile : Profile 
    { 
        protected override void Configure() 
        { 
            Mapper.CreateMap<CarCategoryVm, CarCategory>() 
                .ForMember("Car", opt => opt.Ignore()); 
        } 
    } 
}
 

□ 初始化所有的Profile

using AutoMapper;
 
namespace Car.Test.Portal.Helpers.AutoMapper 
{ 
    public static class AutoMapperConfiguration 
    { 
        public static void Configure() 
        { 
            Mapper.Initialize(x => x.AddProfile<VmToDomainProfile>()); 
            Mapper.Initialize(x => x.AddProfile<DomainToVmProfile>()); 
        } 
    } 
}
 

 

□ 全域性註冊

        protected void Application_Start() 
        { 
            AreaRegistration.RegisterAllAreas();
 
            WebApiConfig.Register(GlobalConfiguration.Configuration); 
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 
            RouteConfig.RegisterRoutes(RouteTable.Routes); 
            BundleConfig.RegisterBundles(BundleTable.Bundles);
 
            //全域性配置對映 
            AutoMapperConfiguration.Configure(); 
        }

 

關於AutoMapper的使用,在這裡

下篇進入無限級分類的增刪改查環節。   

相關文章