首页 > 代码库 > Mvc4+autofac+jggrid+nhibernate(1) 架构介绍

Mvc4+autofac+jggrid+nhibernate(1) 架构介绍

 写这篇博文只是介绍下思路,主要目的是为了找工作,所以细节不是很详细,请大家勿喷

第一章架构介绍

1,架构介绍

先看个整体图

                       

Infrastructure解决方案中使用的基础工具。

Application站点应用层,组织业务逻辑管理业务事务的地方。比如登录,菜单等。Models是用来放UI中所使用的ViewModel即DTO类,以及利用value.injecter进行实体和viewmodel转换的映射类。

Core 核心层,实现ORM的实体类和映射,以及仓储,业务服务类等。

Demo.site.web 即web层。在这里使用了jqgrid表格UI,bootstrap,和jquery验证等增加用户体验的富客户端技术。

2,采用面向接口开发模式,

面向接口开发:好处不用说了,最大的理由,方便解耦。仓储和业务服务层都采用接口设计,有个很大的不便,每个类都要搞个接口。有点小麻烦。下面详细说下这种设计。

在基类NHibernateRepositoryBase中设置共用的增删改(CUD)操作,采用泛型设计,这样更加通用,查询先不设置,因为这里又牵涉到一个知识点。等下再说。下面是基类代码。

 

//-----------------------------------------------------------------------// <copyright file="EFRepositoryBase.cs" company="Company">// Copyright (C) Company. All Rights Reserved.// </copyright>// <author></author>// <summary></summary>//-----------------------------------------------------------------------using System;using System.Collections.Generic;using System.Linq;using System.Linq.Expressions;using Component.Tools;using NHibernate;namespace Component.Data{               /// <summary>        ///     NH仓储操作基类        /// </summary>        /// <typeparam name="TEntity">动态实体类型</typeparam>        /// <typeparam name="TKey">实体主键类型</typeparam>    public class NHibernateRepositoryBase<TEntity, TKey> : IRepository<TEntity, TKey> where TEntity : Entity        {            #region 属性        /// <summary>        /// 作为基类,若被继承则必须有无参构造函数,否则报错。或者在继承类中显示调用 有参构造。或者用属性来ioc则不需显示定义无参构造函数。        /// </summary>        public NHibernateRepositoryBase() { }                    /// <summary>            ///     获取 仓储上下文的实例            /// </summary>                private IUnitOfWorkContext NHContext;        public NHibernateRepositoryBase(IUnitOfWorkContext UnitOfWork)            {                NHContext = UnitOfWork;                        }        public virtual ISession Session        {            get { return NHContext.Session; }        }            /// <summary>            ///     获取 当前实体的查询数据集            /// </summary>            public virtual IQueryable<TEntity> Entities            {                get { return NHContext.Set<TEntity, TKey>(); }            }            #endregion            #region 公共方法            /// <summary>            ///     插入实体记录            /// </summary>            /// <param name="entity"> 实体对象 </param>            /// <param name="isSave"> 是否执行保存 </param>            /// <returns> 操作影响的行数 </returns>            public virtual object Insert(TEntity entity, bool isSave = true)            {                PublicHelper.CheckArgument(entity, "entity");                object obj = NHContext.RegisterNew<TEntity, TKey>(entity);                if (isSave)                     NHContext.Commit();                return obj;            }            /// <summary>            ///     批量插入实体记录集合            /// </summary>            /// <param name="entities"> 实体记录集合 </param>            /// <param name="isSave"> 是否执行保存 </param>            /// <returns> 操作影响的行数 </returns>            public virtual void Insert(IEnumerable<TEntity> entities, bool IsUsedStatelessSession)            {                PublicHelper.CheckArgument(entities, "entities");                NHContext.RegisterNew<TEntity, TKey>(entities,IsUsedStatelessSession);                            }            /// <summary>            ///     删除指定编号的记录            /// </summary>            /// <param name="id"> 实体记录编号 </param>            /// <param name="isSave"> 是否执行保存 </param>            /// <returns> 操作影响的行数 </returns>            public virtual int Delete(TKey id, bool isSave = true)            {                int isTrue = 0;                PublicHelper.CheckArgument(id, "id");                try {                TEntity entity = NHContext.Set<TEntity, TKey>().Single<TEntity>(t=>t.Id.ToString()==id.ToString());                if (entity != null)                     isTrue = Delete(entity, isSave);                                }                catch(Exception e)                {                    PublicHelper.ThrowDataAccessException(e.Message, e);                                    }                return isTrue;            }        /// <summary>        ///         /// </summary>        /// <param name="ids"></param>        /// <param name="IsUsedStatelessSession">不可以级联删除</param>        /// <returns></returns>            public int Delete(string ids, bool IsUsedStatelessSession)            {                PublicHelper.CheckArgument(ids, "ids");                return NHContext.RegisterIdsDeleted<TEntity, TKey>(ids, IsUsedStatelessSession);                                        }            /// <summary>            ///     删除实体记录            /// </summary>            /// <param name="entity"> 实体对象 </param>            /// <param name="isSave"> 是否执行保存 </param>            /// <returns> 操作影响的行数 </returns>            public virtual int Delete(TEntity entity, bool isSave = true)            {                PublicHelper.CheckArgument(entity, "entity");                NHContext.RegisterDeleted<TEntity, TKey>(entity);              if(isSave)                  return NHContext.Commit();              else return 0;            }            /// <summary>            ///     删除实体记录集合            /// </summary>            /// <param name="entities"> 实体记录集合 </param>            /// <param name="isSave"> 是否执行保存 </param>            /// <returns> 操作影响的行数 </returns>            public virtual void Delete(IEnumerable<TEntity> entities, bool IsUsedStatelessSession)            {                PublicHelper.CheckArgument(entities, "entities");                NHContext.RegisterDeleted<TEntity, TKey>(entities, IsUsedStatelessSession);                                                }            /// <summary>            ///     删除所有符合特定表达式的数据            /// </summary>            /// <param name="predicate"> 查询条件谓语表达式 </param>            /// <param name="isSave"> 是否执行保存 </param>            /// <returns> 操作影响的行数 </returns>            public virtual int Delete(Expression<Func<TEntity, bool>> predicate, bool IsUsedStatelessSession)            {                PublicHelper.CheckArgument(predicate, "predicate");                List<TEntity> entities = NHContext.Set<TEntity, TKey>().Where(predicate).ToList();                if (entities.Count > 0)                {                    Delete(entities, IsUsedStatelessSession);                    return 1;                }                else return 0;            }            /// <summary>            ///     更新实体记录            /// </summary>            /// <param name="entity"> 实体对象 </param>            /// <param name="isSave"> 是否执行保存 </param>            /// <returns> 操作影响的行数 </returns>            public virtual int Update(TEntity entity, bool isSave = true)            {                PublicHelper.CheckArgument(entity, "entity");                NHContext.RegisterModified<TEntity, TKey>(entity);                if (isSave)                    return NHContext.Commit();                else return 0;            }            /// <summary>            /// 使用附带新值的实体信息更新指定实体属性的值            /// </summary>            /// <param name="propertyExpression">属性表达式</param>            /// <param name="isSave">是否执行保存</param>            /// <param name="entity">附带新值的实体信息,必须包含主键</param>            /// <returns>操作影响的行数</returns>            public void Update(IEnumerable<TEntity> entities, bool IsUsedStatelessSession)            {                PublicHelper.CheckArgument(entities, "entities");                NHContext.RegisterModified<TEntity, TKey>(entities, IsUsedStatelessSession);                            }                       /// <summary>            ///使用session时用和用linq查询集合时用            /// </summary>            /// <param name="args">0为是否用session,1为session对象,剩下的是查询参数</param>            /// <returns></returns>            public IQueryable<TEntity> FindBySpecification(Specification<TEntity>[] specifications, params object[] args)            {                bool isSuccess = false;                IQueryable<TEntity> querys;                if (specifications == null || specifications.Any(s => s == null))                    throw new ArgumentNullException("specifications");                if (args.Count()!=0&&bool.TryParse(args[0].ToString(), out isSuccess))                {                    args[1] = Session;                    querys = specifications[0].SatisfyingElementsFrom(args);                                   }                               else{                    querys = specifications.Aggregate(Entities, (current, specification) => specification.SatisfyingElementsFrom(current, args));                }                return querys;            }            #endregion                 /// <summary>            /// 用linq查询单个实体时用,注意返回集合实体的重载            /// </summary>            /// <param name="candidates"></param>            /// <param name="args"></param>            /// <returns></returns>                       public TEntity FindBySpecification(Specification<TEntity> specification,params object[] args)            {                if (specification == null)                    throw new ArgumentNullException("specification");                return specification.SatisfyingElementsFrom(Entities.AsEnumerable(), args);            }               }    }
View Code

 

 

有了基类NHibernateRepositoryBase 就可以设置储了。所有的仓储都实现接口,每个接口继承基类NHibernateRepositoryBase 的接口,这样如果需求有所变化只需要在仓储接口上加上方法即可。如图所示

 

 

业务层同样如此,设计一个业务基类,然后如上所示继承即可。在Demo.site是实现视图逻辑的地方,调用业务层的业务类实现如登录等业务逻辑。然后在Controller中引用接口。进行调用。非常方便。如需修改只需找到相应的接口和实现类即可,其他地方不用修改,从而实现了一定程度上解耦的目的。

上面说了,查询还没有介绍,我在这架构中,把查询放在了业务层方面。利用规约模式,资料在这里http://www.cnblogs.com/daxnet/archive/2010/07/19/1780764.html,仓储基类中只需放置一个调用函数即可。如图。

 

specification.SatisfyingElementsFrom 即是利用规约调用查询类的代码。规约类即一个查询设置一个类。这样做到了解耦。维护方便,但会出现大量的这种类,使项目臃肿,解决方法上面的连接已经给出。

利用规约和延迟加载将查询在业务类中实现,可以按需查询,不必像以前那样,把所有的公共查询如加载所有数据load,getbyname等定义都放在基类中,如果没有用到,就会使代码臃肿,还有一个不便就是在基类中执行查询再把结果传递到业务层,会把所有结果都加载到内存中,这会大大降低查询效率,解决方案就是利用廷迟加载,传递IQueryable<T>泛型实体集或session类到业务层,进行按需查询,这样就可避免上述情况。

在这个架构中我设计了根据业务层需要,把IQueryable<T>或session传到规约类中进行查询。简单查询可以用Iqueryable复杂一些的用session,如下图

 

业务层根据args[]第一个参数来决定是否传递session

代码如下 

/// <summary>            ///使用session时用和用linq查询集合时用            /// </summary>            /// <param name="args">0为是否用session,1为session对象,剩下的是查询参数</param>            /// <returns></returns>            public IQueryable<TEntity> FindBySpecification(Specification<TEntity>[] specifications, params object[] args)            {                bool isSuccess = false;                IQueryable<TEntity> querys;                if (specifications == null || specifications.Any(s => s == null))                    throw new ArgumentNullException("specifications");                if (args.Count()!=0&&bool.TryParse(args[0].ToString(), out isSuccess))                {//使用session查询                    args[1] = Session;                    querys = specifications[0].SatisfyingElementsFrom(args);                                   }                               else{//使用linq查询                    querys = specifications.Aggregate(Entities, (current, specification) => specification.SatisfyingElementsFrom(current, args));                }                return querys;            }
View Code

然后在规约类中进行查询即可。重写specification基类方法即可,代码如下。

using System.Collections.Generic;using System.Linq;using NHibernate;namespace Component.Data{    public class Specification<T> where T : Entity    {               public ISession _session{get;set;}              /// <summary>       ///使用session时用       /// </summary>       /// <param name="args">0为是否用session,1为session对象,剩下的是查询参数</param>       /// <returns></returns>        public virtual IQueryable<T> SatisfyingElementsFrom(params object[] args)        {            return null;                    }        /// <summary>        /// 用linq查询集合时用        /// </summary>        /// <param name="candidates"></param>        /// <param name="args"></param>        /// <returns></returns>               public virtual IQueryable<T> SatisfyingElementsFrom(IQueryable<T> candidates, params object[] args)        {            return null;        }        /// <summary>        /// 用linq查询单个实体时用        /// </summary>        /// <param name="candidates"></param>        /// <param name="args"></param>        /// <returns></returns>        public virtual T SatisfyingElementsFrom(IEnumerable<T> candidates, params object[] args)        {            return candidates.Where<T>(t => t.Id.ToString() == args[0].ToString()).FirstOrDefault();        }     /// <summary>        ///     id值取回记录        /// </summary>        /// <param name="args[0]">id值</param>        /// <returns>根据id返回的实体</returns>        //public TEntity GetElementsById<TEntity>(IQueryable<TEntity> candidates, params object[] args) where TEntity : Entity        //{        //    PublicHelper.CheckArgument(args, "args");        //    return candidates.Single<TEntity>(t => t.Id.ToString() == args[0].ToString());        //}           }}
View Code

3,领域驱动(DDD)

这是一个很大的话题,有兴趣的可以看这里。http://www.cnblogs.com/xishuai/p/ddd-entity-value-object-follow-up.html。。在这个架构中介绍下DDD驱动中怎样体现聚合和聚合根的实现。先看下实体关系类图。

 

 

userAccount和roleUserAccount、Role这三个实体是一个聚合,通过聚合根进行数据操作。

Capability,Menu,RoleCapability,和role,可以划为一个聚合,role划为聚合根,通过它进行数据操作

Capability,Menu,可以分别做为他们聚合的聚合根,进行数据操作。

至于怎样实现数据操作的,当然要靠orm了。因为这里只是为了展现我所会的技术,找工作用,具体详细的DDD可以拜读博客园中一些大牛的文章。

4,nhibernate(orm)

现在大多数公司都使用了EF code first,我学习Nhibernate不知能不能派上用场很是忐忑。nh相信大家已经很熟悉了,不多介绍再这里说一下,他的映射方式

使用fluent nhibernate来进行映射,使用它完全不再用xml了,有智能提示,不再担心手美女飘过时手抖出错的问题了。资料在这里,http://www.fluentnhibernate.org/当然园子里也有中文文档,不过还是原文来的详细些。这里用automapping和fluentmapping结合使用,大大减少编码量,需要特别处理的,如对某个类的cascade级别的修改,id命名策略的处理等,可以通过实现conventions接口来处理。非常方便。看代码

using NHibernate.Cfg;using FluentNHibernate.Conventions;using FluentNHibernate.Cfg;using FluentNHibernate.Cfg.Db;using FluentNHibernate.Automapping;using Demo.Core.NHFluentConfiguring.Conventions;using FluentNHibernate.Conventions.Helpers;using System.Reflection;using System.Data;using Demo.Core.Models;namespace Demo.Core.NHFluentConfiguring{    public enum CurrentSessionContextClass    {        managed_web,        call,        thread_static,        web    }    public static class FluentNhCfg    {               public static Configuration GetNHConfiguration()        {            //也可以用ioc注入            var cfg = new AutoMappingConfig();            var model = AutoMap.Assembly(Assembly.Load("Demo.Core.Models"))                .Where(t => t.Namespace == "Demo.Core.Models" && t.BaseType.Name == "Entity" && t.Name.IsNotAny(new[] { "RoleCapability", "RoleUserAccount" }))            .IgnoreBase<Component.Data.Entity>()          .Override<Menu>(map =>map.IgnoreProperty(ig => ig.Children))                      .Conventions.Add(new EnumConvention(),             new InverseConvention(),             new HiloIdConvention(),              DefaultCascade.All(),              new MenuCascadeCapabilityConvention(),              new MenuCascadeConvention(),            DefaultLazy.Always(),            DynamicInsert.AlwaysTrue(),            DynamicUpdate.AlwaysTrue(),            OptimisticLock.Is(x => x.Dirty())           );                       // Cache.Is(x => x.ReadWrite())                       var nhConfig = Fluently.Configure()      .Database(MsSqlConfiguration.MsSql2008      .ConnectionString("Data Source=.;Initial Catalog=FrameworkShan;Integrated Security=True")                //fnh api中没有的属性可以用raw来设置nh中的属性。                //•validate:在程序启动时,检查持久化类与映射文件的改变。                //  •none:不产生任何操作。               //•create-drop:在程序启动时,自动生成数据库架构并导入到数据库中,在程序关闭时,删除数据库架构,常用在测试中。                //update 加载hibernate自动更新数据库结构      .Raw("hbm2ddl.auto", "update")      .ShowSql()            .IsolationLevel(IsolationLevel.ReadCommitted)      .AdoNetBatchSize(20)      )      //.CurrentSessionContext("web")在web项目和test项目中分别各自设置            .Mappings(mappings =>      {          mappings.FluentMappings              .AddFromAssemblyOf<Demo.Core.Models.Mapping.RoleCapabilityMapping>()              .Conventions.Add(new EnumConvention())               .ExportTo(@"C:\Users\qishan\Desktop\fnh");          mappings.AutoMappings.Add(model)                .ExportTo(@"C:\Users\qishan\Desktop\fnh");          //Conventions若放在fluentMappings中各设定将不起作用。只有放在autoMapping中才起作用          //在此设置的默认conventions将被在Mapping文件中设置的属性替换。                                    })      .BuildConfiguration();            //string[] nhd = nhConfig.GenerateSchemaCreationScript(new NHibernate.Dialect.MsSql2008Dialect());            return nhConfig;        }    }}
View Code

5,控制反转(ioc)

在选择ioc时很纠结是用spring,ninject,还是其他,最后想想还是以效率为优吧,就选了这个autofac,官方推荐是以构造函数注入的,不过也支持属性注入

对很多功能都进行了集成,比如,对controller,modelbinder等直接在global.cs中一行代码就搞定,还有注入模块支持,一些功能所需要的注入可以写在一个模块中统一注入,这个功能很优雅。还有聚合服务注入,这个在controller中最好用,把controller中所用的服处,放在一个聚合接口中,调用时直接调用接口,就可以调用服务,官方文档这里http://autofac.readthedocs.org/en/latest/,使用方式,看下代码就好了。

 

  protected void Application_Start()        {            HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.Initialize();            var builder = new ContainerBuilder();            builder.RegisterModelBinders(Assembly.GetExecutingAssembly());            builder.RegisterModelBinderProvider();            builder.RegisterControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();            //To make use of property injection for your filter attributes            builder.RegisterFilterProvider();            builder.RegisterModule<AutofacWebTypesModule>();                      builder.RegisterModule<NHibernateModule>();            // builder.RegisterModule<ControllersModule>();            builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(RoleRepository)))                .Where(t => t.Name.EndsWith("Repository"))                .AsImplementedInterfaces();            builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(UserAccountMapper)))                 .Where(t => t.Name.EndsWith("Mapper"))                .AsSelf();            builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(RoleService)))                 .Where(t => t.Name.EndsWith("Service"))                 .AsImplementedInterfaces();            builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(DataPagingViewService)))                  .Where(t => t.Name.EndsWith("ViewService"))                  .AsImplementedInterfaces();                       builder.RegisterType<ExtensibleActionInvoker>().As<IActionInvoker>().InstancePerRequest();                       IContainer Container = builder.Build();           IoC.Container = new AutofacDependencyResolver(Container);            DependencyResolver.SetResolver(IoC.Container);                       AreaRegistration.RegisterAllAreas();                                    WebApiConfig.Register(GlobalConfiguration.Configuration);            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);            RouteConfig.RegisterRoutes(RouteTable.Routes);            BundleConfig.RegisterBundles(BundleTable.Bundles);                                  ValueProviderFactories.Factories.Insert(0, new JsonNetValueProviderFactory());            ModelBinders.Binders.Add(typeof(Component.Data.Entity), new JsonNetModelBinder());            // Component.Data.Entity        }
View Code

这里注意 IoC.Container = new AutofacDependencyResolver(Container);是使用依赖解析器注入的,不是用工厂模式。当业务需要使用controller工厂时在返回controller实例方法中要这样调用:定义一个ioc静态类

using System;using Autofac;using Autofac.Integration.Mvc;namespace Component.Tools{    public static class IoC    {        public static AutofacDependencyResolver Container       {           get;set;                 }               public static object Resolve(Type type)        {            return Container.GetService(type);        }    }}
View Code

在global.cs中设置Container属性,然后在工厂方法里调用。IoC.Resolve(controllerType)来得到实例。

好了,架构这节先说到这里吧

 

Mvc4+autofac+jggrid+nhibernate(1) 架构介绍