[转载]MVC3+Spring.net+NHibernate+ExtJs的简单架构 – RegicideGod – 博客园.
对于MVC3、Spring.net、NHibernate、ExtJs等单个的技术点使用起来,并不是很复杂,出问题比较多的还是配置相关的问题;下面我们来一步一步的实现4个框架的融合配置;
首先我们来谈谈4个框架的各自负责的事情:
- MVC3:最开始是用MVC2搭建框架,后来用VS12后,只支持MVC3的模 板,就改为MVC3搭建了,MVC3兼容MVC2并且多加入了一种Razor视图引擎,但我们这个框架还是用ASPX视图引擎,言归正传;MVC的概念我 个人的理解就是一种约定俗成,更深层次的理解还得各位看其他文章和多做练习来加深理解,在这个框架中,我们主要是用到Controller去后台取数,然 后呈现到View上面;当然还有很多MVC中的一些技巧和MVC的注意点,我们代码或其他文章慢慢讲解;View层的框架主要是ExtJs;
- ExtJs:ExtJs主要是用于前台的页面展示,属于MVC中View层的框架,就是ExtJs写URL指向Controller中的action取得数据然后展现到ExtJs控件中;
- NHibernate:主要是用于底层数据的ORM框架,属于国外的开源框架,最开始是JAVA那边的“特有”,后来被完美移植到.NET平台,我们主要是用来做底层数据的持久化操作;
- Spring.NET:也是有Java平台上完美移植过来的一个框架,我们这里 面只用到Spring.NET的依赖注入,就是创建对象有Spring.NET来统一管理,对对象进行注入操作,推崇决不重新造轮子的原则!我们架构中主 要用来对Controller中需要的对象进行注入;继续我们的架构,下面我们从代码结构来说明我们该如何来完成这每一步;
首先,我们去spring.net网站下载最新的源代码,在源文件中找到 Spring.NET-1.3.2\Spring.NET\examples\Spring下面的 Spring.Data.NHibernate.Northwind这个项目,这个是Spring.NET官方出的一个示例,用于Spring.NET和 NHibernate集成的示例,下面还有MVC2及MVC3的相关示例,大家可以先熟悉了解下;
最开始搭建框架,可能最恼火的是引用的相关DLL不好找,即使找到了,也有可能相关的版本不对,这个也很头疼,我找DLL的办法是将Spring.NET全部重新编译一遍,然后找到编译后的相关需要的源码生成的DLL;
我们开始搭建如下图的结构:
简述下,BLL、Common、DAL、Model四个类库和一个MVCMain的MVC3的网站;
我们从每一个层里面来简述相关的代码
Model:
我们以Student为例,代码如下
View Code protected string stuGUID; public virtual string StuGUID { get { return stuGUID; } set { stuGUID = value; } } protected int stuNo; public virtual int StuNo { get { return stuNo; } set { stuNo = value; } } protected string stuName; public virtual string StuName { get { return stuName; } set { stuName = value; } } •••••等等
因为我们要使用NHibernate所以在定义对象的时候,属性要用virtual来修饰,我们定义的对象然后,要把对象和数据的表对应起来,就要用到xml来描述Student对象和数据表Student的对应关系;
下面我们来看看相对应的描述,一定要记住一点,对于我们的整个项目中的xml(除了网站下面的xml)都是把文件设置嵌入式资源
View Code
PS:对于NHibernate的实体映射,网上有更多的相关专题,我们现在主要讨论的是把这几项东西结合,所以对于各自技术,都可以去找相关的技术专题;这篇文章只是以最简单的方式实现框架的融合,融合后对于技术本身的特性没有任何影响;
Model就可以可以说已经完成;下面我们来看DAL层
DAL:
对重要的是Helper文件夹中的东西,就是NHibernate的相关的使用(这个里面的东西,大家可以参考Spring.net中的spring.net+NHibernate的实现示例),我们作一下简单的介绍
NHelper类主要是对ISessionFactory和ISession 的注册,session就是相当于我们的DBHelper类一样,提供强大的数据访问支持,只要继承NHelper类就可以对数据库进行访问,但 ISession需要通过Spring.NET进行注入;
View Code public abstract class NHelper { private ISessionFactory sessionFactory; /// <summary> /// Session factory for sub-classes. /// </summary> public ISessionFactory SessionFactory { protected get { return sessionFactory; } set { sessionFactory = value; } } /// <summary> /// Get's the current active session. Will retrieve session as managed by the /// Open Session In View module if enabled. /// </summary> protected ISession CurrentSession { get { return sessionFactory.GetCurrentSession(); } } protected IListGetAll() where TEntity : class { ICriteria criteria = CurrentSession.CreateCriteria(); return criteria.List(); } }
然后就是IDAL, ISupportsDeleteDAL, ISupportsSaveDAL等一些接口,主要提供对数据库的增删改查的接口
View Code public interface ISupportsSaveDAL { /// <summary> /// 保存一个实体 /// </summary> ///需要保存的实体 /// 返回保存实体的主键ID TId Save(TEntity entity); /// <summary> /// 更新一个实体 /// </summary> ///更新一个实体 void Update(TEntity entity); /// <summary> /// 保存或更新一个实体 /// </summary> ///保存或更新一个实体 void SaveOrUpdate(TEntity entity); }
因为我们对要操作的具体类不明确,所以我们用泛型接口来约束接口中的方法;TEntity代表一个实体类型,TId代表实体的主键类型,下面还有删除和查找,就不一一列举;
然后就是ICommonDAL接口,这个接口主要是实现其他定义好的增删改查的相关接口,在定义个性化查询的及个性化方法的时候,就可以定义在此定义,而增删改查的接口,可以对应的去继承;
View Code public interface ICommonDAL:IDAL,ISupportsSaveDAL,ISupportsDeleteDAL { /// <summary> /// 根据HQL和参数查询实体 /// </summary> ///HQL ///参数 /// 实体 IList GetCommonByHql(string hqlQuery, CommonParam [] commonParams); /// <summary> /// 根据HQL和参数查询实体 /// </summary> ///HQL /// 实体 IList GetCommonByHql(string hqlQuery); /// <summary> /// 标准查询实体 /// </summary> /// 指定对象 /// 查询到的实体 IList GetCommonByCriteria(); /// <summary> /// 标准查询实体 /// </summary> ///主键ID /// 查询到的实体 IList GetCommonByCriteria(TId id); }
当然这里不只局限于,这些查询了,NHibernate给我们提供的相关查询,都可以在接口中定义好,让子类去实现就行了;
CommonParam这个东西,是在Common中定义的一个类,主要是按照ADO.NET中的SQLparameter来简单的定义了简单的key和value
在往下面就是实现ICommonDAL接口了
CommonDAL类,如下
我们要继承NHelper才能通过Session来访问数据库,所以我们必须继承
View Code /// <summary> /// 根据HQL和参数查询实体 /// </summary> ///HQL ///参数 /// 实体 public IList GetCommonByHql(string hqlQuery, CommonParam [] commonParams) { IQuery iquery = CurrentSession.CreateQuery(hqlQuery); if (commonParams != null) { foreach (var item in commonParams) { iquery.SetString(item.Key, item.Val); } } return iquery.List(); } /// <summary> /// 标准查询实体 /// </summary> ///主键ID /// 查询到的实体 public IList GetCommonByCriteria(TId id) { ICriteria icriteria = CurrentSession.CreateCriteria(typeof(TEntity)); icriteria.Add(Restrictions.IdEq(id)); return icriteria.List(); } /// <summary> /// 保存对象 /// </summary> ///形参对象 /// public TId Save(TEntity entity) { var id=(TId)CurrentSession.Save(entity); CurrentSession.Flush(); return id; }
里面列举了一些简单的方法实现,都是使用CurrentSession进行实现,CurrentSession就是NHelper里面定义的东西,是通过spring.net注入进行的;对于保存数据的相关操作,在一定要加CurrentSession.Flush();提交到数据库,否则无法保持到数据库;
在接着就是具体的表的操作,以Student表为例(上面我们已经建立了Model类)
我们定义IStudentDAL接口
public interface IStudentDAL:ICommonDAL { }
这个接口啥都不用写,只用继承ICommonDAL接口就行,因为我们已经知道是Student的DAL接口类,所以我们不用定义形参接口,当然,如果你有个性化的方法,依然,可以定义在IStudent接口中;
我们在来完成IStudentDAL接口的实现
StudentDAL
public class StudentDAL :CommonDAL,IStudentDAL { }
只用继承CommonDAL和实现IStudentDAL接口就行,一定要是 CommonDAL在前,实现接口在后,因为IStudentDAL是继承ICommonDAL的,而CommonDAL又是实现ICommonDAL 的,所以我们这里面基本一句话都不用,就实现了,Student的之前在ICommonDAL中的所有方法;
接口和继承的好处很明显的体现出来,当然要泛型的支撑,如果我们在一个class 班级表,只用加一个接口,继承ICommonDAL,然后在加一个类,继承CommonDAL,实现接口就可以实现所有的增删改查,当然你也可以在 IStudentDAL中,提供自定义化的方法,让StudentDAL去实现,StudentDAL继承CommonDAL,而CommonDAL也继 承NHelper,所以StudentDAL也可以通过CurrentSession来访问数据库
DAL到这里也基本写完了;
BLL
在来看BLL,如果项目中没有BLL层,可以完全抛开这层,但是我们还是简单的叙述一下,
BLL中也有Helper,这个主要是来定义BLL中的特殊化查询方法;
我们主要看CommonBLL的实现操作,因为其他的与DAL中的没有差异,但我们这个BLL中没有NHelper,因为BLL负责业务逻辑,不负责数据访问,故没有NHelper,不提供CurrentSession来访问数据库
View Code public class CommonBLL: ICommonBLL { private ICommonDAL comDAL; public ICommonDAL ComDAL { set { comDAL=value; } } public IList GetCommonByHql(string hqlQuery, CommonParam[] commonParams) { return comDAL.GetCommonByHql(hqlQuery,commonParams); } }
我们是通过创建ICommonDAL的实例进行DAL中的CommonDAL中的实现调用,至于ComDAL我们只是定义了,根本没有创建对象,这个我们是通过Spring.net来注入对象的,所以我们这里也暂时这么写;
在接着就是IStudentBLL,也是继承ICommonBLL就可以了,然后就是StudentBLL,实现CommonBLL类,并且实现接口IStudentBLL;这时我们的StudentBLL中,有一个ComDAL只是定义了,没有创建对象,因为我们是通过spring.net注入对象的
BLL完了后,就是Common了,这个里面目前就是一个CommonParam类,定义了一个key和value来传递HQL的参数
MVCMain
然后在看MVCMain网站,我们做的简单一些吧,在我们输入地址http://localhost:8001/Home/Index的时候,就是默认访问HomeController下的Index方法,我们在HomeController下面写东西来后台访问我们刚写好的BLL
View Code public class HomeController : Controller { public string Message { get; set; } private IStudentBLL studentBLL; public IStudentBLLStudentBLL { set { studentBLL = value; } } }
我们只用定义IStudentBLL的对象就行,我们创建对象是通过Spring.net注入的;所以我们来看看最最重要的配置吧;
首先当然是从web.config里面开始,首先在configuration节点中加入(加入log4net日志记录)
View Code <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging"></section><section name="context" type="Spring.Context.Support.MvcContextHandler, Spring.Web.Mvc3"></section><section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"></section>
在接着下面写
View Code <!-- choices are INLINE, FILE, FILE-WATCH, EXTERNAL--> <!-- otherwise BasicConfigurer.Configure is used --> <!-- log4net configuration file is specified with key configFile-->
这里是指定log4net的相关配置和spring.net的相关配置,context节点下,是其他xml注入描述的相关文件
在appsettings中一定要加这句话,用于注册NHibernate的SessionFactory
在system.web节点下的httpModules节点中加入
用于注册spring.net和nhibernate
如果你的网站用vs打开没问题,发布到IIS上出现问题,可以调整IIS的应用程序池的模式为集成,或者修改system.webServer
View Code
Web.config中的东西写完了,在web.config中我们提到来几个文件
第一个是log4net的相关配置文件,这个基本就是把log4net的配置写进行就行了
View Code
第二个是NHibernate的配置文件
View Code <!--?xml version="1.0" encoding="utf-8" ?--> <!-- Referenced by main application context configuration file --> The Northwind object definitions for the Data Access Objects. <!-- Property placeholder configurer for database settings --> <object type="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core"> </object> <!-- NHibernate Configuration --> <object id="NHibernateSessionFactory" type="MVCMain.Controllers.LocalSessionFactoryObjects, MVCMain"> Model <!-- provides integation with Spring's declarative transaction management features --> </object> <!-- Transaction Management Strategy - local database transactions --> <object id="transactionManager" type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate21"> </object> <!-- Exception translation object post processor --> <object type="Spring.Dao.Attributes.PersistenceExceptionTranslationPostProcessor, Spring.Data"></object> <!-- Data Access Objects --> <object id="StudentBLL" type="BLL.StudentBLL, BLL"> </object> <object id="ComStudentDAL" type="DAL.StudentDAL, DAL"> </object>
大家要仔细看,其中的
View Code public class LocalSessionFactoryObjects : LocalSessionFactoryObject { ///<summary> /// Overwritten to return Spring's bytecode provider for entity injection to work. /// </summary>public override IBytecodeProvider BytecodeProvider { get { return new BytecodeProvider(ApplicationContext); } set { } } }
在然后,就是一些NHibernate的标准配置
这两个节点就是创建DAL中的StudentDAL的对象了,然后注入到BLL中的StudentBLL中的ComDAL中,因为我们的ComDAL是CommonBLL中定义的ICommonDAL的对象,现在通过StudentDAL注入给ComDAL,因为StudentDAL是继承IStudentDAL,而IStudentDAL又是继承ICommonDAL的,所以可以注入,这也就是面向接口的编程了;
Dao.xml中主要是NHibernate的相关配置文件,和后台对象通过Spring.net创建;然后在看第三个web.xml
Web.xml中主要是定义MVCMain中要使用的对象,因为我们的对象都要交给spring.net来管理,并且还要访问BLL中的对象,间接的访问数据
View Code <!--?xml version="1.0" encoding="utf-8" ?--> <!-- Referenced by main application context configuration file --> The Northwind web layer definitions <object type="MVCMain.Controllers.HomeController, MVCMain"> </object> <object type="MVCMain.Controllers.AccountController, MVCMain"> </object>
这个里面主要是对HomeController中的相关对象和属性进行注入,对于其他Controller如果没有属性需要注入也要创建对象才能访问,否则会报找不到的错误;
到这里基本的配置基本已经完了,但还有最重要的一点,容易被忽视,就是配置文件写了后,如何注入的了?
我们还有最后一步就是在Global中修改一点东西
修改Global为如下
View Code public class MvcApplication : SpringMvcApplication { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); } public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults ); } protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); } }
就是Global的Application要继承SpringMVCApplication才行;
到这里基本已经完成,里面需要引入的DLL没有详细的说明,在此截图一下
一定要去spring.net的源代码中把这些东西找齐,然后在引入就行;
还有一点就是ExtJs或者是JQuery easyui等框架,访问后台时
View Code var eastPanel = new Ext.Panel({ region: 'east', title: 'East Side', collapsible: true, split: true, width: 225, minSize: 175, maxSize: 400, layout: 'fit', margins: '0 0 0 0', items: { autoScroll: true, autoLoad: { url: "/Home/Index" } } });
只用指定url就可以,这个代表是访问HomeController下的Index方法;
至此框架搭建好了,如果你报错了,你可以对应这篇文章及spring.net提供 的S+N的集成示例和里面的MVC3的示例进行相应的修改,你也一定能搭建出此框架!后面会继续对此框架进行优化处理,并完善各单个框架的的功能点优化! 对于spring.net和nhibernate的相关使用大家不太好理解的多去网上搜搜文章!博客园上也有相关的专题;
在此多说一点,架构只是为了应付某种需求才出现的,好的架构可以让程序员更多的去 关心程序的业务逻辑,而不是成天的写代码;好的架构在初期设计好架构,初期写好架构代码,即使初期写架构代码的时间很长,只要后期程序员进行实现的时候, 写少量的代码,可以做多量的事情,那么架构就是合理的!
文章出处:博客园 http://www.cnblogs.com/RegicideGod/archive/2012/06/25/2562871.html