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)来得到实例。

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

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。