本系列将使用zTree来创建、编辑关于品牌、车系、车型的无限级分类,使用datagrid显示,源码在github。先上最终效果:

datagrid显示所有记录、分页,提供添加、修改、删除按钮,并提供简单搜索:

创建分类,弹出模态窗口,zTree显示所有分类,点击勾选按钮或节点,所选节点名称显示到文本框:

提供客户端和服务端验证,验证不通过显示出错信息:

修改分类,弹出模态窗口,zTree显示当前选中的节点名称:

创建数据模型并生成到数据库

→创建CarModel.edmx,创建模型,无限级分类的一切"神奇"从ParentID字段开始。

→右键界面,选择"根据模型生成数据库"

→配置数据库连接,运行sql语句......等等,最终生成如下文件:

● App.config中的连接字符串需要复制到MVC主站点的web.config中。
● CarModel.Context.cs中包含了继承DbContext的上下文。
● CarModel.tt下包含了所有的领域模型Domain Models。
● CarModel.edmx.sql每次映射完执行里面的sql语句,将把数据同步到数据库。

架构设计

由于主要是体验无限级分类的增删改查,架构做得相对简单一些。其它组件列举的只是示意,实际只用到了缓存和序列化json的帮助类。如下:

□ 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的使用,在这里

下篇进入无限级分类的增删改查环节。

MVC无限级分类01,分层架构,引入缓存,完成领域模型与视图模型的映射的更多相关文章

  1. MVC无限级分类02,增删改查

    继上一篇"MVC无限级分类01,分层架构,引入缓存,完成领域模型与视图模型的映射",本篇开始MVC无限级分类的增删改查部分,源码在github. 显示和查询 使用datagrid显 ...

  2. 说一说Web开发中两种常用的分层架构及其对应的代码模型

    昨天妹子让我帮她解决个问题,本以为可以轻松搞定,但是打开他们项目的一瞬间,我头皮发麻.本身功能不多的一个小项目,解决方案里竟然有几十个类库.仅仅搞明白各个类库的作用,代码层次之间的引用关系就花了一个多 ...

  3. 从Microsoft.AspNet.Identity看微软推荐的一种MVC的分层架构

    Microsoft.AspNet.Identity简介 Microsoft.AspNet.Identity是微软在MVC 5.0中新引入的一种membership框架,和之前ASP.NET传统的mem ...

  4. 移动App设计之分层架构+MVC

    http://www.cnblogs.com/Logen/archive/2012/11/08/2760638.html 场景分析:我们知道,一个移动设备的应用大多与网络有关,也就是说,我在移动设备上 ...

  5. 无限级分类Asp.net Mvc实现

    无限级分类Asp.net Mvc实现   无限级分类涉及到异步加载子类.加载当前类和匹配问题,现在做一个通用的实现.   (一) 效果如下:   (二)设计.实现及使用 (1)数据库 (a)表设计db ...

  6. 左右值无限级分类 MVC + EntityFramework 的简单实现

    在度娘上查了大半个月的资料,最后发现每个网友分享的文章都有一定的错误(PS:大家是故意的么?).最后是在看了一个ASP版本后知道了大概流程:看了一个存储过程实现的文章后知道了大概需要的功能:看了一个S ...

  7. DDD分层架构之领域实体(验证篇)

    DDD分层架构之领域实体(验证篇) 在应用程序框架实战十四:DDD分层架构之领域实体(基础篇)一文中,我介绍了领域实体的基础,包括标识.相等性比较.输出实体状态等.本文将介绍领域实体的一个核心内容—— ...

  8. 【转载】DDD分层架构的三种模式

    引言 在讨论DDD分层架构的模式之前,我们先一起回顾一下DDD和分层架构的相关知识. DDD DDD(Domain Driven Design,领域驱动设计)作为一种软件开发方法,它可以帮助我们设计高 ...

  9. DDD分层架构的三种模式

    引言 在讨论DDD分层架构的模式之前,我们先一起回顾一下DDD和分层架构的相关知识. DDD DDD(Domain Driven Design,领域驱动设计)作为一种软件开发方法,它可以帮助我们设计高 ...

随机推荐

  1. 关于整型Integer、Int32、Int64、IntPtr、UINT、UInt32、Cardinal、UInt64、UIntPtr、NativeUInt、Pointer、Handle

    知识点1:UIntPtr = NativeUInt = Pointer = Handle 随程序的位数改变而改变.如下: 所以以后再用指针的时候要这样:UintPtr/NativeUInt(实例) = ...

  2. 根据后端传的时间前端js进行倒计时

    一.故事背景: 1. 今天公司有个项目需求 2. 在前端页面实现一个倒计时功能 3. 初步设想:后端根据需求规定一个未来的时间,前端根据当前时间进行计算 4. 然后将时间格式化,时分秒的格式 5. 时 ...

  3. java算法小例子

    作为一个程序员,有时候我觉得自己都不适合,因为自己数学不好,算法不好,脑子不够灵活.而对于算法,感觉就像是数学题,总觉得很难.以前上学,在班里总有几个什么都不好,但唯独数学很厉害,真气人!面对难题时, ...

  4. 13 在 O(1) 时间内删除链表节点

    删除链表的一个结点,用下一个结点覆盖掉要删除的结点,再释放掉要删结点的下一个结点的内存 Java: public ListNode deleteNode(ListNode head, ListNode ...

  5. TypeScript学习笔记(一) - 环境搭建

    本篇将简单介绍一下TypeScript,并记录开发环境的搭建.使用Visual Studio Code进行一个简单的Demo开发过程. 第一部分.简介 TypeScript是一种由微软开发的自由和开源 ...

  6. python和mongodb简单交互

    python和mongodb简单交互 1.安装pymongo: pip3 install pymongo 2.pymongo的简单用法: # /usr/bin/env python3 import p ...

  7. 【解决】 64位笔记本丢失 MSVCR110.dll win10 phpstudy2017

    启动程序报错如下: 无法启动此程序,因为计算机中丢失MSVCR110.dll.尝试重新安装该程序以解决此问题. 应该很容易就搜索到,缺少这样的dll文件,是没有安装Visual C++ Redistr ...

  8. OptParse选项工具模块

    OptParse是一个从Python2.3版本起引入的一个编写命令行工具模块,示例如下 ######example.py###### import optparse if __name__ == &q ...

  9. Revit二次开发示例:DeleteObject

    在本例中,通过命令可以删除选中的元素. 需要注意的是要在代码中加入Transaction,否则的话会出现Modifying  is forbidden because the document has ...

  10. python 对字典"排序"

    对字典进行排序?这其实是一个伪命题,搞清楚python字典的定义---字典本身默认以key的字符顺序输出显示---就像我们用的真实的字典一样,按照abcd字母的顺序排列,并且本质上各自没有先后关系,是 ...