.net企业级架构实战


.net 企业级架构实战之 1——框架综述 近日由于业务需要,接触了一些 Flex 下的东西,比如 Mate 框架(一个集成 MVC 和 IOC 的框 架,其事件广播机制很强大),疏于整理 spring.net 学习资料,现在终于得闲,将心得梳理一 下: spring.net 是 java 下大名鼎鼎的 spring 框架移植到.net 的开源项目,且借助于.net 强大的 反射机制,甚至拥有比原 java 版本更强大的功能。 那它能用来做什么呢?核心功能就是 IOC 和 AOP: IOC(Inversion of Control),字面意思为“反转控制”,我更倾向于理解为“依赖注入”,意思 就是说:在基于接口开发的情况下,我们会对需要的业务处理对象(数据访问,业务逻辑等)一 一做上接口,前端使用时只是对接口的调用,而并不关心具体是什么类具体去实现了这个接口~ 听起来似乎不可能,是的,如果没有 IOC,这是不可能的事情,我们的前端逻辑和后端实现是 紧紧耦合的,做页面开发的人必须知道哪一个类(.cs 文件)拥有哪些方法,即便是基于接口, 我们也依然要在程序里去实例化它,形如: IManager mgr = new DataManager(); 无形中,基于接口开发成了鸡肋,前端开发人员几乎要知道一条龙的编码流程才能做业务开发! (当然,有的项目就是一个人在做) 好吧,那就使用 IOC,它是怎么解开这个耦合关系的? IOC 框架一般会维护一个配置文件,它大概要完成的使命是: 1、将实现接口的对象进行列表,表示它们是被页面需要的; 2、把页面的以基于 URL 的形式进行列表,表示它们是需求方。 余下的事情,是框架来进行协调,在页面上声明一下某一个接口的对象,在它需要实例化时,IOC 框架会自动将对应的接口实现进行注入。如下就是一个 Spring.net 的配置范例片断: 再看看页面里的声明和调用片断: using System; using System.Data; using System.Configuration; using System.Collections.Generic; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; using System.Text; using woodigg.model; using woodigg.Interface.DAO; using woodigg.bll.Tool; using Spring.Context; using Spring.Context.Support; public partial class Admin_Artist_MgrArt : AdminPage { #region 注入对象 private IArtistDAO _ArtistDaoSpring; public IArtistDAO ArtistDaoSpring { get { return _ArtistDaoSpring; } set { _ArtistDaoSpring = value; } } #endregion protected void Page_Load(object sender, EventArgs e) { GridView1.DataSource =ArtistDaoSpring.GetAllArtists(); GridView1.DataBind(); } } 如果是初次接触 IOC,可能理解起来会有些生涩,毕竟是个基于配置的框架,有些东西需要深 入了解,这个以后可以一步步探个究竟。OK,接着就是 AOP。 那么 AOP 又是做什么的呢?Aspect Oriented Programming,字面意思即为“面向切面的编 程”。Aspect,就是切面,代表了我们很多项目中重复开发的模块,如邮件功能,日志功能等。 说起来是会让人很颓废的,我们花费在重复劳动上的时间,有时太多了。这个项目写日志,下一 个还写日志,日志有区别吗?答案是没有,但它就是需要,哪怕是复制粘贴,还是需要!被需要 当然是好事,说明它存在的价值,但能否把这样的东西做成一个组件,以后拿来就用,并且与新 的项目系统人我两不犯,无缝集成? 当然是可以的,AOP 在此时的价值会得到充分的体现。就我个人而言,对它的理解,更多的是 一个监听器,侦测你的业务是否有相关的需要,只要你有需要发出,AOP 框架就会在你需要的 地方设一个断点,进行功能模块的切入,让它去做你希望它做的事。就这么神奇~ 这里放出一个代码片断,小小注解一下以上比较让人头晕的概念: using System; using System.Collections.Generic; using System.Text; using System.Reflection; using woodigg.model; using woodigg.DAO; using Spring.Aop; using AopAlliance.Intercept; namespace woodigg.bll { /// /// 环绕通知 /// public class whenUserSaveAdvice : IMethodInterceptor { public object Invoke(IMethodInvocation invocation) { User user = (User)invocation.Arguments[0]; Logger.ErrorLog(invocation.Method.Name, user.Name, user.N ame); //真正的调用目标方法,并得到返回值 Object obj = invocation.Proceed(); return obj; } } } 这一代码片断实现的功能是:如果发现系统中新增了一个用户(即 User 的业务管理器调用了 Save 方法),那么在日志系统中,存储一下用户名,让管理员可以在翻日志时知道谁又加入了 ~ 当然,就这么一段代码并不能完成这个监控功能,同样的,我们必须做配置(Spring.net 把开 发提高到了对配置进行管理的境地,你在配置管理上花的时间,将大于以往,好处是更关注和贴 近业务而不是代码),告诉 AOP 框架,我们希望监听哪些对象的哪些动作,以及监听到后我们 要调用哪些模块来采取行动: save* woodigg.Interface.DAO.IUserDAO advisor 也许这样的配置片断更让人犯晕,没关系,习惯了就好,有些事情需要我们自己去做(DB, ENTITY,DAO,BLL 开发),有些事情需要的是我们去理解(AOP 框架,通知,切面,代理 对象),相信不需要多长时间,这些都不是问题。 关于 IOC 和 AOP,以上只是寥寥几笔带过,在以后的实例系列中,将各个击破 实例主要围绕的是一个音乐网站的搭建(有点儿像AllMusic内样的,而不同于别的什么无聊SNS 社区),会涉及的内容是:Spring.net、nHibernate、codeSmith 模板、多对多表结构、Castle MonoRail(虽然有人强建不建议把 MonoRail 集成到 Spring.net 中,但我至今没找到.net 2.0 下好的 MVC 解决方案,用用 MonoRail 有助于更好理解 MVC,优化性能)。 .net 企业级架构实战之 2——Spring.net 对象装配 之所以启用 spring.net,看中的是它的容器功能:一个可以管理对象整个生命周期的容器。 在这个容器内,我们加入各种对象的定义信息,让它们自动地装配(类似于乐高积木,定制化的 拼合)、实例化、事务协作、回收销毁,以适应系统的需要。 如前所述,spring.net 基于配置运作。要让 spring.net 的容器感知我们编写的对象,需要 做的就是在配置文件中声明它们。所谓配置,载体当然是 xml 文件,定义明晰,清清楚楚。 看一个 xml 配置片断: 解释一下:这是一个并不复杂的控制器类 ArtistController,我们声明了它的 id 和 type(类 型,类在程序集中完整路径及程序集名)。 这样,spring.net 的对象工厂(ObjectFactory 或者 ApplicationContext)在感知到对 它的调用请求时,会实例化它并返回给需求方(可以是一个类,一个 aspx 页面,一个 asmx, 或者别的什么)——事实比这复杂得多,诸如你可以显式地声明它是单例的(加一个属性 singleton="true",鄙人用的 spring.net 版本为 1.1.0.2),如此一来在 spring.net 容器生 命周期内,它不会再有第二个实例,也许有时这会派上用场,能节省一些开销。 继续观察,节内会有一些声明,这是 object 的属性声明,name 即它在内部对外公开的名称,其后可以使用 value(值),ref(引用对象)或者 expression(表 达式)。此例使用的 ref 引用,意味着容器将干涉内政——把需要的对象注入到 object 的字段 里,前提是:ref 指定的对象存在于容器装载的配置文件集合中,并以相同的 id 声明。 势必存在另一个配置,形如: 在这个稍显简单的供求关系中,它们是供方,被需要、被别的对象引用,容器会在那个 ref 它们的对象(ArtistController)涉及到指定字段被使用时,提供其实例,比如 ArtistController 有个叫 abc 的方法中,用到了 ArtistDaoSpring 字段的 defg()方法,容器接收到这一信息, 装配 ArtistDaoSpring 字段对应的 woodigg.DAO.ArtistDaoSpring 对象——你也许会说,直 接实例化不就完了么?呵呵,如果这个对象也是基于别的组件装配出来的,那就不仅仅只是实例 化一下,还是需要重复一下这个过程,递归着来,搭积木一般把它装配成功交付需求方(像极了 这个商业社会的产业链)。 在这里,我们还看到一个属性 autowire,它可以有几种枚举选值 (no,byName,byType,constuctor,autodetect,default),而 byName 的意思就是让容器 按名称,自行查找同名的对象,进行装配(id 和类中的字段同名)。看到这里,想必有人会说, 你的第一个配置ArtistController,简直就是废话连篇,在object 上指定autowire="byName" 就行了,何必一个个指定 property?bingo!如果不是为了说明供求关系,我才懒得写这些 property,让容器查找吧。 ok,以上皆为务虚,来务实一把,装配一个简单.net 对象(pono?)到一个.aspx 页面耍耍。 这个简单对象 RouteMap,是为 Asp.net MVC 框架服务的——由于还无福使用 WIN2008+IIS7,只得在.net 2.0 下奋战,对于 new {action="Home",controller="Artist"} 这么潇洒的范式自然也是只能眼馋。没关系!自己扩展吧: using System; /// /// Asp.net mvc RouteMap /// namespace woodigg.controllers { public class RouteMap { private string _controller; private string _action; private string _id; public string Controller { get { return this._controller; } set { this._controller = value; } } public string Action { get { return this._action; } set { this._action = value; } } public string Id { get { return this._id; } set { this._id = value; } } public RouteMap(string controller, string action, string id) { this._controller = controller; this._action = action; this._id = id; } } } RouteMap 将被一个叫 test.aspx 页面需要。它长这样: using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class test : System.Web.UI.Page { #region 注入对象 private woodigg.controllers.RouteMap _ArtistRoutmap; public woodigg.controllers.RouteMap ArtistRoutmap { get { return this._ArtistRoutmap; } set { this._ArtistRoutmap = value; } } #endregion protected void Page_Load(object sender, EventArgs e) { Response.Write(ArtistRoutmap.Controller); } } 它会在页面加载完成时,输出这个 RouteMap 的控制器名称。至于这东西到底从哪来的值, 没有说明。那么,就为这俩构造一个配置,由容器来装配它们。 新建一个 routemap.xml 文件,放在网站的~/config 目录下,形如: ArtistRoutmap 在被实例化时,我们希望它是有默认值的,在配置中逐一指定它构造函数 参数项的值,这很好理解。 页面对象的声明更简单,只需说明它在站点中存在的路径,字段引用按名称查找吧。 累述至此,对象装配已经完成,但要让容器自动的运转起来,还需要在站点层面做些配置: 1、引用 spring.net 程序集及相关(以 1.1.0.2 为例,如有出入,稍为调整),包括: Spring.core.dll,Spring.web.dll,Spring.Web.Extensions.dll,log4net.dll; 2、修改 web.config,装载 Spring 模块和处理程序,并指定配置上下文路径,装载日志 系统:
添一个全局配置 Global.asax,在启动时开启日志记录: void Application_Start(object sender, EventArgs e) { // log4net log4net.Config.DOMConfigurator.Configure(); } run 一下这个 test.aspx,它会输出控制器名称"Artist",平平无奇,却是与一种方式的告 别——Craig Walls 们在《Action in Spring》的序中这样写道:开发者有一种宝贵的品质,那 就是“懒惰”。这种懒惰激励开者努力用最小的开销找到最佳的解决方案。 其实,有时不需要那么勤劳的,有些东西用成熟的框架能很好的解决,何必自己造轮子呢? 当然,我并不是来搞推销的,只是由衷的喜欢 spring,仅此而已。 至于 web service 页.asmx 的注入,将会在以后交待,或者有兴趣也可以参看一下这个不 错的中文文档: http://files.cnblogs.com/moye/SpringNet_document.rar 也可以常上官网看看:http://www.springframework.net/ 抑或与同好们交流:http://www.springframework.cn/index.php .net 企业级架构实战之 3——业务对象建模及 codesmith 模板 在软件开发的需求调研完成时,应着手设计业务对象模型。 模型应恰到好处地容纳业务对系统的需求——不冗余致拖累系统,不残缺致无法满足业务, 因此,建模忠实地反映了调研工作的成效。 建模的工具软件有很多,如 Rational、Visual Case、UModel,不过本人基本上习惯了 PowerDesigner:功能适用,各种模型能自由转化,具有不错的正反向数据库工程能力,还能 良好地支持 C#。OK,来建一个数据模型(用于 ORM): 1、PowerDesigner 数据库建模 PowerDesigner 版本为 12.0,“新建”一个“Physical Data Model”(物理数据模型), 在其中构建一张“Table”(表)。此处的 TB_ARTIST 代表的是容纳艺人对象的表; 需要将它在数据库中生成,可以在 PowerDesigner 中完成这步操作。点选“DataBase”下 的“Connect”(Ctrl+Shit+N),将开始连接数据库: 如果(Machine data source)数据源中不存在需要的数据库连接,可点击“Add“按钮,添 加一个 ODBC 连接,操作很简单,此处不累述;连接上数据库后,选中要生成的表对象,选取 “Database”菜单下的“Genernate Database”项,生成描述表结构的 sql 脚本文件: 此处可以选择“Generation type”(生成类型),是只生成脚本,还是通过 ODBC 连接直接 生成表。流程大同小异,选择“ODBC Generation“,一步到位: 在生成操作前,会弹出“Excute SQL Query”对话框,提示确认即将运行的脚本内容,点 击“Run”按钮,表就会生成好在数据库中。 基本上,PowerDesigner 提供了完备的数据库工程功能,以辅助软件工程师专心设计。 2、CodeSmith 模板生成 nHibernate 实体: 利用模板工具,生成实体类这种事情几乎不用写一行代码,效率提升,精力省下来干点值得 投入的事。 这类工具软件也不少,CodeSmith 就是其中的佼佼者之一。由于贯穿此系列中的实例用到 nHibernate,所以就来看看模板生成 nHibernate 实体类(C#)和配置文件(hbm.xml)吧: CodeSmith 版本为 4.1.2,选择右侧的"nHibernate.cst”模板,按右键选"Excute"项: 在弹出的对话框中,需要配置 生成文件的存放路径、ODBC 数据源、生成的实体类程序集 名称、生成的实体类引用空间名称、需要剔除的表名前缀(生成的实体类文件名称): 点击“Generate”,数据库中的表,就变成了一个个 pono 类文件和.hbm.xml 配置文件, 躺在文件夹里了。 借用工具,我们可以四两拨千金,专注于业务开发。在随后的实例中,将利用这些生成的实 体模型,集成到系统的数据访问体系(Spring.net+nHibernate)中去。当然,那也是件很轻 松的事~ .net 企业级架构实战之 4——Spring.net 下的 nHibernate 数据访问模板 在 spring.net 中集成 nHibernate 可以获得许多值得称道的特性。比如:基于元标记(meta Attributes)的事务支持、对物理数据库的抽象、对数据层进行切面式拦截。 好处是不少,但首先要学会配置。为了这个集成的环境,建立一个配置文件 applicationContext.xml : woodigg.DAO woodigg.model OK,这里有几处需要说明: 一、woodigg.DAO.SQLProvider 是一个数据结构类,用以描述物理数据库的相关信息, 诸如连接串、元数据信息等。这里其实就用到了连接串,在配置中植入位置、帐号信息等就能连 接到数据源。这个 SQLProvider 类结构如下: using System; using System.Collections.Generic; using System.Text; using System.Data; using Spring.Data.Common; namespace woodigg.DAO { public class SQLProvider : IDbProvider { #region IDbProvider 成员 private string _connectionString = ""; public string ConnectionString { get { return this._connectionString; } set { this._connectionString = value; } } public IDbCommand CreateCommand() { return null; } public object CreateCommandBuilder() { return null; } public IDbConnection CreateConnection() { return null; } public IDbDataAdapter CreateDataAdapter() { return null; } public IDbDataParameter CreateParameter() { return null; } public string CreateParameterName(string name) { return null; } public string CreateParameterNameForCollection(string name) { return null; } public IDbMetadata DbMetadata { get { return null; } } public string ExtractError(Exception e) { return null; } public bool IsDataAccessException(Exception e) { return false; } #endregion } } 二、在 SessionFactory 配置中,指明需要环境映射的程序集名称,通俗说法是:哪些层会 在集成环境中,被直接引用?这里以示例项目来说是:woodigg.DAO 和 woodigg.Model,分 别为实体(上一节中生成的一堆.cs 实体)层,和数据映射文件(被嵌入在项目中的 hbm.xml 文件)所在的数据访问层。 三、HibernateProperties 节中,可以指定调试时是否显示生成的 sql 语句。同时,能配置 缓存事宜:此处用到的是 NHibernate.Caches.SysCache。 四、配置 HibernateTemplate。nHibernate 的模板,既 nHibernate 项目已经为开发者写 好了一套通用的方法,能便捷的操作数据库,此处将 SessionFactory 植入引用即能让它工作 起来。(并不是所有的复杂 SQL,它都能做到,不能完成的功能,我们得自己写,这个马上会 交待)。 五、DaoTemplate,就是我自己写的一个基于以上配置的复杂模板,能完成诸如 Distinct, top,调用分页存储过程等一干复杂 SQL 功能,抛出来做点贡献吧: using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.Data; using NHibernate; using NHibernate.Cfg; using NHibernate.Engine; using NHibernate.Expression; using Spring.Dao; using Spring.Data.NHibernate.Support; using woodigg.model; using log4net; namespace woodigg.DAO { #region ParamInfo 结构 public struct ParamInfo { public string Name; public object Value; } #endregion /// /// 继续自 HibernateDaoSupport 抽象类 /// HibernateDaoSupport 基类拥有 HibernateTemplate /// public class DaoTemplate : HibernateDaoSupport { /// /// 泛型读取 /// /// /// #region T LoadFromId(object id) public T LoadFromId(object id) { try { T obj = (T)HibernateTemplate.Load(typeof(T), id); return obj; } catch (Exception ex) { ILog log = LogManager.GetLogger(typeof(T)); log.Error(ex.Message, ex); return default(T); } } #endregion /// /// 泛型存储 /// /// /// #region bool Save(T obj) public bool Save(T obj) { try { HibernateTemplate.Save(obj); return true; } catch (DataAccessException ex) { ILog log = LogManager.GetLogger(typeof(T)); log.Error(ex.Message, ex); return false; } } #endregion /// /// 泛型更新 /// /// /// #region bool Update(T obj) public bool Update(T obj) { try { HibernateTemplate.Update(obj); return true; } catch (DataAccessException ex) { ILog log = LogManager.GetLogger(typeof(T)); log.Error(ex.Message, ex); return false; } } #endregion /// /// 泛型删除 /// /// /// #region bool Delete(T obj) public bool Delete(T obj) { try { HibernateTemplate.Delete(obj); return true; } catch (DataAccessException ex) { ILog log = LogManager.GetLogger(typeof(T)); log.Error(ex.Message, ex); return false; } } #endregion /// /// 条件删除 /// /// /// #region bool Delete(string where) public bool Delete(string where) { try { string sql =string.Format("from {0} {1}", typeof(T).ToString(), where.ToUpper().StartsWith("WHERE") ? where : "WH ERE " + where); HibernateTemplate.Delete(sql); return true; } catch (DataAccessException ex) { ILog log = LogManager.GetLogger(typeof(T)); log.Error(ex.Message, ex); return false; } } #endregion /// /// 泛型搜索 /// /// /// #region IList Search(string where) public IList Search(string where) { try { //有意思的模板反射哟~ T obj = (T)System.Reflection.Assembly.GetAssembly(typ eof(T)).CreateInstance(typeof(T).ToString()); string hql = string.Format("from {0} {1}", obj.GetType().ToString(), where.ToUpper().StartsWith("WHERE") ? where : "WH ERE " + where); IList alist = HibernateTemplate.Find(hql); IList list = new List(); if (alist != null && alist.Count > 0) { foreach (T t in alist) { list.Add(t); } return list; } else return null; } catch (Exception ex) { ILog log = LogManager.GetLogger(typeof(T)); log.Error(ex.Message, ex); return null; } } #endregion /// /// 泛型搜索 - DISTINCT /// /// /// 列名,用","分开,不带别名 /// /// 别名 #region IList SearchDistinct(string where,string field, string alias) public IList SearchDistinct(string where, string fiel d, string alias) { try { //有意思的模板反射哟~ T obj = (T)System.Reflection.Assembly.GetAssembly(typ eof(T)).CreateInstance(typeof(T).ToString()); // 反射 DTO 对象的各字段,必须把字段和 DB 中字段同名 System.Reflection.PropertyInfo[] pps = obj.GetType(). GetProperties(); //拆分成别名+列名 string[] cols = field.Split(','); string columns = string.Empty; foreach (string col in cols) columns += string.Format("{0}.{1},", alias, col); columns = columns.TrimEnd(','); //hql string hql = string.Format("select distinct {2} fro m {0} {3} {1}", obj.GetType().ToString(), where.ToUpper().StartsWith("WHERE") ? where : "WH ERE " + where , columns , alias); IList alist = HibernateTemplate.Find(hql); IList list = new List(); if (alist != null && alist.Count > 0) { //是否为数组 bool isArray = (cols.Length == 1 ? false : true); foreach (object arr in alist) { //产生一个类实例 T t = (T)System.Reflection.Assembly.GetAssemb ly(typeof(T)).CreateInstance(typeof(T).ToString()); for (int i = 0; i < cols.Length; i++) { //逐字段检查名称 foreach (System.Reflection.PropertyInfo p i in pps) { if(pi.Name.Equals(cols[i])) { //数组与 object 对象 pi.SetValue(t, (isArray ? (arr a s object[])[i] : arr), null); } } } list.Add(t); } return list; } else return null; } catch (Exception ex) { ILog log = LogManager.GetLogger(typeof(T)); log.Error(ex.Message, ex); return null; } } #endregion /// /// 基于表达式的排序查询 /// /// /// /// /// #region IList SearchWithOrder(string where, string prop ertyName, bool ascending) public IList SearchWithOrder(string where, string prope rtyName, bool ascending) { try { //排序 Order order = new Order(propertyName, ascending); //排序 ICriteria ic = Session.CreateCriteria(typeof(T)); ic.AddOrder(order); //表达式 ICriterion exp = Expression.Sql(where); ic.Add(exp); return ic.List(); } catch (Exception ex) { ILog log = LogManager.GetLogger(typeof(T)); log.Error(ex.Message, ex); return null; } } #endregion /// /// 执行存储过程(返回 bool) /// /// 名称 /// 参数表 #region bool ExecuteStoredProc2(string spName, ICollection pa ramInfos) public bool ExecuteStoredProc2(string spName, ICollection par amInfos) { bool result = true; IDbCommand cmd = Session.Connection.CreateCommand(); cmd.CommandText = spName; cmd.CommandType = CommandType.StoredProcedure; // 加入参数 if (paramInfos != null) { foreach (ParamInfo info in paramInfos) { IDbDataParameter parameter = cmd.CreateParameter (); parameter.ParameterName = info.Name; // driver.Fo rmatNameForSql( info.Name ); parameter.Value = info.Value; cmd.Parameters.Add(parameter); } } IDbConnection conn = Session.Connection; if(conn.State == ConnectionState.Closed) conn.Open(); try { cmd.Connection = conn; IDataReader rs = cmd.ExecuteReader(); result = true; } catch (Exception ex) { ILog log = LogManager.GetLogger(typeof(DaoTemplate)); log.Error(ex.Message, ex); result = false; } finally { Session.Connection.Close(); } return result; } #endregion /// /// 执行存储过程(返回 ILIST) /// /// 名称 /// 参数表 #region IList ExecuteStoredProc(string spName, ICollection pa ramInfos) public IList ExecuteStoredProc(string spName, ICollection par amInfos) { IList result = new ArrayList(); IDbCommand cmd = Session.Connection.CreateCommand (); cmd.CommandText = spName; cmd.CommandType = CommandType.StoredProcedur e; // 加入参数 if (paramInfos != null) { foreach (ParamInfo info in paramInfos) { IDbDataParameter parameter = cmd.CreateParameter (); parameter.ParameterName = info.Name; // driver.Fo rmatNameForSql( info.Name ); parameter.Value = info.Value; cmd.Parameters.Add(parameter); } } IDbConnection conn = Session.Connection; conn.Open(); try { cmd.Connection = conn; IDataReader rs = cmd.ExecuteReader(); while (rs.Read()) { int fieldCount = rs.FieldCount; object[] values = new Object[fieldCount]; for (int i = 0; i < fieldCount; i++) values[i] = rs.GetValue(i); result.Add(values); } } finally { Session.Connection.Close(); } return result; } #endregion /// /// 获取记录数 /// /// /// #region int GetRecordCount(string where) public int GetRecordCount(string where) { return GetRecordCount(where, "*"); } #endregion /// /// 获取记录数 /// /// /// #region int GetRecordCount(string where,string cols) public int GetRecordCount(string where,string cols) { try { //DISTINCT 统计 bool distinct = false; if (cols.ToLower().StartsWith("distinct")) { distinct = true; string[] columns = cols.Replace("distinct", "").S plit(','); StringBuilder sb = new StringBuilder(); sb.Append("distinct "); for (int i = 0; i < columns.Length; i++) sb.Append("alia." + columns[i].Trim()); cols = sb.ToString().TrimEnd(','); } T obj = (T)System.Reflection.Assembly.GetAssembly(typ eof(T)).CreateInstance(typeof(T).ToString()); string hql = ""; if (where.Trim() == String.Empty) { hql = string.Format("select count({1}) fro m {0} {2}", obj.GetType().ToString(),cols ,(distinct ? "alia":"") ); } else { hql = string.Format("select count({2}) fro m {0} {3} {1}", obj.GetType().ToString(), where.ToUpper().StartsWith("WHERE") ? wher e : "WHERE " + where , cols, (distinct ? "alia" : "")); } IQuery query = Session.CreateQuery(hq l); object o = query.UniqueResult(); return int.Parse(o.ToString()); } catch (Exception ex) { ILog log = LogManager.GetLogger(typeof(T)); log.Error(ex.Message, ex); return 0; } finally { Session.Close(); } } #endregion /// /// 获取记录数(全文检索) /// /// /// #region int GetRecordCount4Fulltext(string where,string tb Name) public int GetRecordCount4Fulltext(string where, string tb Name) { try { string hql = string.Format("select count(*) as CountN um from {0} {1}", tbName, where.ToUpper().StartsWith("WHERE") ? wher e : "WHERE " + where); IQuery query = Session.CreateSQLQuery(hql) .AddScalar("CountNum", NHibernateUtil.Int32); object o = query.UniqueResult(); return int.Parse(o.ToString()); } catch (Exception ex) { ILog log = LogManager.GetLogger(typeof(T)); log.Error(ex.Message, ex); return 0; } finally { Session.Close(); } } #endregion /// /// 通过 where 条件查询获取分页数据 /// /// /// /// 排序 /// /// /// #region IList GetPageEntites(string where,string varQu erysort, int Start, int Max) public IList GetPageEntites(string where, string varQue rysort, int Start, int Max) { try { T obj = (T)System.Reflection.Assembly.GetAssembly(typ eof(T)).CreateInstance(typeof(T).ToString()); string hql = ""; if (where.Trim() == String.Empty) { hql = string.Format("from {0}", obj.GetType().ToString()); } else { hql = string.Format("from {0} {1}", obj.GetType().ToString(), where.ToUpper().StartsWith("WHERE") ? wher e : "WHERE " + where); } if (varQuerysort != String.Empty) hql += " " + varQue rysort; IQuery query = Session.CreateQuery(hql); IList list = query.SetFirstResult(Start).SetMaxRes ults(Max).List(); return list; } catch (Exception ex) { ILog log = LogManager.GetLogger(typeof(T)); log.Error(ex.Message, ex); return null; } finally { Session.Close(); } } #endregion /// /// 通过存储过程查询分页信息 /// /// 表名 /// /// 列名集合 /// 排序列名 /// 页尺寸 /// 当前页 /// 升降序,true-0 为升序,false-非 0 为降 序 /// 条件 /// #region public DataTable GetPageEntitesByStoredProc(string t ableName, string Primarykeyname, string colName, string orderCol,in t pageSize, int pageIdx, bool orderType, string condition) public DataTable GetPageEntitesByStoredProc(string tableNam e, string Primarykeyname, string colName, string orderCol, int pageSize, int pageIdx, bool orderType, string conditi on) { IList result = new ArrayList(); ISessionFactoryImplementor imp = (ISessionFactoryImplemen tor)SessionFactory; IDbConnection conn = imp.ConnectionProvider.GetConnection (); IDbCommand cmd = imp.ConnectionProvider.GetConnection().C reateCommand(); cmd.CommandText = "pagination"; cmd.CommandType = CommandType.StoredProcedure; IDbDataParameter parameter = cmd.CreateParameter(); parameter.ParameterName = "@tblName"; parameter.Value = tableName; cmd.Parameters.Add(parameter); parameter = cmd.CreateParameter(); parameter.ParameterName = "@PrimaryKey"; parameter.Value = Primarykeyname; cmd.Parameters.Add(parameter); parameter = cmd.CreateParameter(); parameter.ParameterName = "@strGetFields"; parameter.Value = colName; cmd.Parameters.Add(parameter); parameter = cmd.CreateParameter(); parameter.ParameterName = "@fldName"; parameter.Value = orderCol; cmd.Parameters.Add(parameter); parameter = cmd.CreateParameter(); parameter.ParameterName = "@PageSize"; parameter.Value = pageSize; cmd.Parameters.Add(parameter); parameter = cmd.CreateParameter(); parameter.ParameterName = "@PageIndex"; parameter.Value = pageIdx; cmd.Parameters.Add(parameter); parameter = cmd.CreateParameter(); parameter.ParameterName = "@OrderType"; parameter.Value = orderType; cmd.Parameters.Add(parameter); parameter = cmd.CreateParameter(); parameter.ParameterName = "@strWhere"; parameter.Value = condition; cmd.Parameters.Add(parameter); try { cmd.Connection = conn; IDataReader rs = cmd.ExecuteReader(); // 分割列 string[] cols = SplitsColumnNames(colName, ','); // 数据表 DataTable dt = new DataTable(tableName); foreach (string col in cols) dt.Columns.Add(col); // 取数据 while (rs.Read()) { // 创建行 DataRow row = dt.NewRow(); for (int i = 0; i < cols.Length; i++) row[cols[i]] = rs.GetValue(i); // 插入行 dt.Rows.Add(row); } // 返回结果集 return dt; } catch (Exception ex) { ILog log = LogManager.GetLogger(typeof(DaoTemplate)); log.Error(ex.Message, ex); return null; } finally { imp.CloseConnection(conn); } } #endregion /// /// 将字符里的列表分解出来 /// /// #region internal static string[] SplitsColumnNames(string co lumns, char separator) internal static string[] SplitsColumnNames(string columns, ch ar separator) { return columns.Split(new char[] { separator }); } #endregion } } 这是内个配合使用的 sql server 分页存储过程,原来从网上摘的,动手改过两次以适配 distinct 取数据: -- 获取指定页的数据 CREATE PROCEDURE pagination @tblName varchar(255), -- 表名 @PrimaryKey varchar(100), --主键 @strGetFields varchar(1000) = '*', -- 需要返回的列 @fldName varchar(255)='', -- 排序的字段名 @PageSize int = 10, -- 页尺寸 @PageIndex int = 1, -- 页码 @doCount bit = 0, -- 返回记录总数, 非 0 值则返回 @OrderType bit = 0, -- 设置排序类型, 非 0 值则降序 @strWhere varchar(1500) = '' -- 查询条件 (注意: 不要加 where) AS declare @strSQL varchar(5000) -- 主语句 declare @strTmp varchar(110) -- 临时变量 declare @strOrder varchar(400) -- 排序类型 if @doCount != 0 begin if @strWhere !='' set @strSQL = "select count(*) as Total from " + @tblName + " where " +@strWhere else set @strSQL = "select count(*) as Total from " + @tblName + "" end --以上代码的意思是如果@doCount 传递过来的不是 0,就执行总数统计。以下的所有代码都 是@doCount 为 0 的情况 else begin if @OrderType != 0 begin --set @strTmp = "<(select min" set @strTmp = " not in " set @strOrder = " order by [" + @fldName +"] desc" --如果@OrderType 不是 0,就执行降序,这句很重要! end else begin --set @strTmp = ">(select max" set @strTmp = " not in " set @strOrder = " order by [" + @fldName +"] asc" end if @PageIndex = 1 begin if @strWhere != '' set @strSQL = "select top " + str(@PageSize,3) +" "+@strGetFields + " from " + @tblName + " where " + @strWhere + @strOrder else set @strSQL = "select top " + str(@PageSize,3) +" "+@strGetFields + " from "+ @tblName + " "+ @strOrder --如果是第一页就执行以上代码,这样会加快执行速度 end else begin --以下代码赋予了@strSQL 以真正执行的 SQL 代码 set @strSQL = "select top " + str(@PageSize,3) +" "+@strGetFields + " from " + @tblName + " where [" + @PrimaryKey + "]" + @strTmp + " (select to p " + str((@PageIndex-1)*@PageSize,3) + " ["+ @PrimaryKey + "] fro m " + @tblName + "" + @strOrder + ") "+ @strOrder if @strWhere != '' set @strSQL = "select top " + str(@PageSize,3) +" "+@strGetFields + " from " + @tblName + " where [" + @PrimaryKey + "]" + @strTmp + " (select top " + str((@PageIndex-1)*@PageSize,3) + " [" + @PrimaryKey + "] from " + @tblName + " where " + @strWhere + " " + @strOrder + ") and " + @strWhere + " " + @strOrder end end print @strSQL exec (@strSQL) GO 最后,要让这个环境在程序中生效,得在 web.config 中加载它:
先写到这里吧,博客园的编辑器对机器配置要求不低,粘贴几段代码,界面几乎不能动弹, 做罢了。下一回,将介绍怎么应用这个集成的环境。 .net 企业级架构实战之 5——基于接口的访问层实现 前几节的内容比较务虚,这一节主要讲讲怎么应用 Spring.net 和 nHibernate 及我们写 的模板,来搭建一个数据访问层,以及在页面中的调用。 先来看一个层级图: 这里有一个 model(实体)层,一个 DAO(数据访问)层,中间还有一个 Interface(接 口)层。 这又回到了最初的探索:接口的做用,一是隐藏实现的细节;二是更利于装配——在 spring.net 的配置文件中,你可以随时装配一个不同的实现,只要它完成接口规定的方法,好 处不言而喻——于页面而言,它并不知道谁来实现了这些功能,它只知道接口的存在(你们都去 实现接口吧,我不关心谁在做这件事,要的只是结果!) ◆欧克,现在页面需要一个对用户表访问的东西(TB_USER_MAIN 表,对应的实体是 woodigg.model.USERMAIN),先做一个接口,在 woodigg.Interface 层添加一个接口: using System; using System.Collections.Generic; using System.Text; using System.Data; using woodigg.model; namespace woodigg.Interface.DAO { /// /// user main DAO Interface /// public interface IUserMainDAO { /// /// 存 /// bool Save(USERMAIN mb); /// /// 事务级添加 /// /// /// /// bool TransactionSave(USERMAIN art, string from); /// /// 更新 /// bool Update(USERMAIN mb); /// /// 事务级删除 /// bool Delete(USERMAIN mb); /// /// 读 /// USERMAIN LoadFromId(int id); /// /// 搜索 /// IList SearchByWhere(string where); /// /// 搜索排序 /// IList SearchByWhereOrder(string where, string prope rtyName, bool ascending); /// /// 分页 /// DataTable GetPagerByStoreProc(string Columns, int pageSize, i nt pageIdx , string OrderColumn, bool orderType, string condition); /// /// 获取记录数 /// int GetRecordCount(string where); } } ◆显然,它的功能都是上一节那个 DaoTemplate 泛型模板已经有的,要实现真的很 easy——编译刚才的 woodigg.Interface 项目,并在 woodigg.DAO 层中引用此项目,然后 添加一个实现: using System; using System.Collections.Generic; using System.Text; using System.Data; using Spring.Context; using Spring.Context.Support; using Spring.Dao; using Spring.Data.NHibernate.Support; using woodigg.model; using woodigg.Interface.DAO; using Spring.Transaction.Interceptor; namespace woodigg.DAO { /// /// user main DAO /// public class UserMainDaoSpring : IUserMainDAO { #region 注入的 DaoTemplate private DaoTemplate _DaoTemplate; public DaoTemplate DaoTemplate { get { return _DaoTemplate; } set { _DaoTemplate = value; } } #endregion private const string TableName = "TB_USER_MAIN"; // 表名 private const string PrimaryKey = "Id"; //主 键 public UserMainDaoSpring() { } /// /// 存 /// public bool Save(USERMAIN art) { return DaoTemplate.Save(art); } /// /// 事务级添加 /// /// /// 从哪来 /// [Transaction()] public bool TransactionSave(USERMAIN art, string from) { if (DaoTemplate.Save(art)) { USEROTHER ot = new USEROTHER(); ot.UserId = art.Id; ot.UserFrom = from; return DaoTemplate.Save(ot); } else return false; } /// /// 更新 /// /// /// public bool Update(USERMAIN art) { return DaoTemplate.Update(art); } /// /// 删除 /// /// [Transaction()] public bool Delete(USERMAIN art) { DaoTemplate.Delete("UserId=" + art.Id); return DaoTemplate.Delete(art); } /// /// 读 /// public USERMAIN LoadFromId(int id) { return DaoTemplate.LoadFromId(id); } /// /// 查 /// public IList SearchByWhere(string where) { return DaoTemplate.Search(where); } /// /// 查排序 /// public IList SearchByWhereOrder(string where,strin g propertyName,bool ascending) { return DaoTemplate.SearchWithOrder(where,proper tyName,ascending); } /// /// 分页 /// /// /// /// /// /// /// public DataTable GetPagerByStoreProc(string Columns,int pageS ize, int pageIdx , string OrderColumn, bool orderType, string condition) { return DaoTemplate.GetPageEntitesByStoredProc (TableName, PrimaryKey, Columns, OrderColumn, pageSiz e, pageIdx, orderType, condition); } /// /// 获取记录数 /// /// public int GetRecordCount(string where) { return DaoTemplate.GetRecordCount(where); } } } ◆为这种类型的 DAL 制作一个配置文件 business.xml,以便在需要时,由 spring.net 的对象工厂实例化并派发它: ◆至此,一个所谓的 DAL 打造完成,把它应用到.aspx 页面中去(当然,实例中比这复杂 得多,以后我们会在 ASP.NET MVC 中应用这些,页面只是个壳)。为了.aspx 页面,做一个 配置文件 pageConfig.xml 吧,在这个 xml 里,登记所有需要注入的页面路径,以便植入实现 了接口的 DAL: 假设这页面在/下,叫 test.aspx: 如前所述,byName 的意思是:页面上写什么接口,就自动去配置环境中查找相应接口实 现,并把它在工厂中的实例注入页面中。 ◆然后,我们整合一下应用环境,把它配置到 web.config 中:
◆最后,test.aspx.cs 中,这样引用前面基于 IUserMainDAO 接口的实现,就能让它跑 起来了: #region 注入对象 private IUserMainDAO _UserMainDaoSpring; public IUserMainDAO UserMainDaoSpring { get { return _UserMainDaoSpring; } set { _UserMainDaoSpring = value; } } #endregion 看起来,似乎是繁琐到发指的一些工作,不过形成习惯了应该就会喜欢上它的,眼下的麻烦 只是为了避免未来更大的麻烦,图难图其易图巨图其细嘛~ 下一节,我们来看看 web services 怎么注入。 .net 企业级架构实战之 6——Spring.net 管理 web services 先引用一段 spring.net framework 帮助文档里的话: “虽然目前.NET 对 web 服务支持的非常好,Spring.NET 认为还是有几个方面可以改进。 . 服务端 首先,.NET 在.asmx 文件中保存 Web 服务请求和服务对象的关联关系,这些.asmx 文件不管 有用没用都得放在那儿。 第二,Spring.NET 希望能通过 IoC 容器对 web 服务进行依赖注入。一般说来 web 服务总会 依赖其它服务对象,所以,如果能用配置方式来选择服务对象,这个功能就相当强大了。 最后,目前在.NET 中 Web 服务的创建完全是一个实现(特定类型)的过程。多数服务(虽不 能说是全部)都应实现为使用粗粒度服务接口的普通类型,并且,某个对象能否发布为远程对象、 web 服务还是企业(COM+)组件应该只与配置有关,而不应该取决于它的实现方式。 .消除对.asmx 文件的依赖 ASP.NET 用.aspx 文件来保存表示层代码,用 code-behind 文件中的类保存应用逻辑,.aspx 与类代码文件各有分工;但 web 服务却不同,Web 服务的逻辑完全是在 code-behind 的类中 实现的。.asmx 文件并没有什么真正的用途,实际上,这个文件既没有必要存在、也不应该存 在。 在将 WebServiceFactoryHandler 类注册为响应*.asmx 请求的 HTTP Handler 之后,开发 人员就可以在 IoC 容器中用标准的 Spring.NET 对象定义来发布 Web 服务。 ◆欧克,意思说得很明白,在 web.config 中注册一个 httpHandlers 节,以接管.asmx 请求: ◆既然不存在也不需要物理文件.asmx 了,那就需要使用 web services 代理,来模 拟.asmx 页,这就是 Spring.Web.Services.WebServiceExporter 类的工作,它能显式导出 web services。 举例说明一下:现有一个 web services——SongSrvService(是一个类),它有一个方法 GetSongUrl,用以获取一首曲子的地址,如果要把它导出为 web services,应为此建立一个 配置,并在 spring.net 环境中加载它: 这里,看一下 services 运行的截屏: 可以看到,在 WebServiceExporter 导出类配置的节信息,都会忠实地显示在 services 描述中。如 Description(描述)、MemberAttributes(方法描述)等。 ◆欧克,回溯一步,我们来看看这个被代理导出的 web services 实现类,应该是什么样子? 在 spring.net 1.1.0.2 中,如果要让一个类被显式导出为 web services,那么,它首先 应该是实现某个接口的,意即——它的功能,全在一个接口被描述过。 在 bll 层新建一个 SongServices 类,其中包括一个 services 接口和一个实现类: using System; using System.Web; using System.Collections; using woodigg.model; using woodigg.Interface.DAO; namespace woodigg.bll.Services { public interface ISongServices { string GetSongUrl(int id); } public class SongServices : ISongServices { /// /// 注入的工具类 /// private ISongDAO _SongDaoSpring; public ISongDAO SongDaoSpring { get { return _SongDaoSpring; } set { _SongDaoSpring = value; } } private string message; public string Message { set { message = value; } } public SongServices() { } /// /// web services 之 GetSongUrl /// /// song id /// url public string GetSongUrl(int id) { SONG sg = SongDaoSpring.LoadFromId(id); if (sg != null) return woodigg.DAO.Tool.ParameterFactory.WebUploadMus icDomain + sg.Url.ToString(); else return string.Empty; } } } 欧克,这就是 Spring.net 建议的 web services 管理方式。小结一下: 1、这种方式,省掉了一个.asmx 页面; 2、整合了其他的 spring.net 下的对象,如方便了注入、拦截; 3、开发难度并未增加,图示说明: .net 企业级架构实战之 7——Spring.net 整合 Asp.net mvc 既然提到 mvc,就不得不说说微软在 asp.net 上的一个重要创造——postback 机制。 但凡用 asp.net 做 web 开发的人,都和这个机制打过交道吧。asp.net 页面 aspx 是其于这种 自回发机制运转的。 我们平时用到的 web 控件,诸如 TextBox、Button 等,无一不是基于 postback 进行封装, 在最终生成的 html 页中,它们还是会被转化成为或者。 然后我们可以查看一下页面的源文件代码,会发现.net framework 为我们写了不少东西:比如 __doPostBack()这个 js 方法,用于提交窗体;WebForm_OnSubmit()方法会进行窗体的验 证;一个长长的__VIEWSTATE 隐藏字段保存窗体上控件的状态信息等(令人发指)……等等。 (详述请参见种玉堂的《再认识 asp.net 的 postback 机制:探索__doPostBack 的来龙去 脉 》)。 多么让人又爱又恨的发明! postback 确实方便了开发者,尤其是小型应用,手到擒来;但性 能呢?不敢恭维!尤其那一长串__VIEWSTATE,每每看到,都觉得揪心。 所以,在项目中的前端表现界面,决定启用 mvc 实现。 原来使用 monoRail,感觉很好,但一直没有解决与 Spring.net 整合问题;而后来干脆听说, monoRail 已经停止维护这个项目,只得作罢。幸好,此时还有一个选择——asp.net Mvc。 经过一段时间的尝试,终于把它与 Spring.net 整合在了一起,这意味着,mvc 能共享到 Spring.net 中的所有对象,无缝集成。 由于 Asp.net Mvc 是 ASP.NET 3.5 Extensions Preview 的一个部分,能不能在 IIS6+Asp.net 2.0 framework 下良好运行,也一度让人担忧,可喜的是,网上很多人都这么 干,并且成功了。也就是说,如果决定在.net 2.0 下使用 asp.net mvc(本实例使用的版本为 asp.net mvc Preview 4),除引用 mvc 的 dll 外(Microsoft.Web.Mvc.dll、 System.Web.Mvc.dll 等),还需额外的引用一个.net 3.5 的 dll(System.core.dll),此处 提供这个包的下载: Asp.net mvc preview 4 和 System.core.dll(v3.5): /Files/moye/Asp.Net_MVC_4.rar 简单说,.net mvc 将视图(View)与控制器(Controller)进行了分离,表现与逻辑的关注点 不再纠缠于一团。而 Model 的传递,微软提供了一套 DataDictionary(ViewDataDictionary、 TempDataDictionary 等)。同时,像 monoRail 一样,.net mvc 在页面中也可以其于模板 书写表达式,比如:你可以在 Controller 中传递一个 IList过来,这边接收到对象拆箱后, 循环迭代输出一个行数等同的。 关于 asp.net mvc 的详尽原理,此处不累述,有兴趣可参阅 乱世重典的《Asp.net Mvc Framework 系列》。 欧克,回到重点,说说整合的事: 以上图示的结构: 将 Controller 单拿出来,做一个层,用以响应 Web/Views 中视图的请求。整合的目标就是: 让这些 Controller 能够被注入,运行于 Spring.net 容器中。 通过学习 Fredrik Normén 的经验:《ASP.Net MVC Framework - Create your own IControllerFactory and use Spring.Net for DI 》,我们能知道,要创建自己的 ControllerFactory,必须实现 System.Web.Mvc 的 IControllerFactory 接口。原型如(.net mvc Preview 4,版本不同接口也可能不一样): using System; using System.Web.Routing; namespace System.Web.Mvc { public interface IControllerFactory { IController CreateController(RequestContext context, string c ontrollerName); void DisposeController(IController controller); } } 在实现的 CreateController 方法中,将负责对不同的 Views 派发对应的 Controller。这里做 一个假设,/Views 目录中有一个叫 User 的目录,那框架就认为,这是一个 User 视图集合(具 体表现在 url 上,如/User/home、/User/login 等),应该有一个叫 UserController 的控制器 对此目录中的所有页面进行响应。 欧克,实践一把: ◆在 Web 的/Views 创建一个 web.config 文件,并这样配置(它的作用:接管对.aspx 页的 请求流程): ◆在/Views 中创建一个叫 User 的目录,并在其下添加一个 Home.aspx 页。当然这样是不能 用的,需要修改一下页面和.cs: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Home.aspx.cs " Inherits="mvcApp.Views.User.Home" %> User Home using System; using System.Collections.Generic; using System.Web; using System.Web.Mvc; namespace mvcApp.Views.User { public partial class Home : ViewPage { } } ◆此时,还需要一个 Controller 来负责响应 User 视图中的请求,如前结构图所示,在 woodigg.controllers 层中,创建一个 UserController 类,担当控制器角色: using System; using System.Text; using System.Collections.Generic; using woodigg.model; using woodigg.Interface.DAO; using System.Data; using System.Web; using System.Web.Routing; using System.Web.Mvc; using System.IO; using woodigg.controllers; using woodigg.DAO.Tool; using woodigg.bll.Tool; namespace woodigg.controllers.Controllers { public class UserControllers : Controller { #region 注入对象 private IUserMainDAO _UserMainDaoSpring; public IUserMainDAO UserMainDaoSpring { get { return _UserMainDaoSpring; } set { _UserMainDaoSpring = value; } } #endregion /*--------------------- USER ---------------------*/ /// /// Home 页 /// [HandleError] [OutputCache(Duration = 60, VaryByParam = "id")] #region ActionResult Home(int id) public ActionResult Home(int id) { ViewData["Title"] = "用户中心 -- Haphere.com"; return View(); } #endregion } } ◆在控制器中,有一个 Home 方法,它刚好与 Home.aspx 同名,所以它就是负责响应 Home 页请求的方法。Home 方法有一个整型的参数,说明这个请求也是传递参数的,形如 http://localhost/User/Home/1(在.net 2.0 url 没这么完美,它形如 http://localhost/User.mvc/Home/1),那么1就是这个 id 参数,然后就去查数据库吧,1 号用户的数据将它展示出来,当然这个示例没有取任何东西,只是传递了一个 ViewData 数据。 而我们也注意到,这里还进行了注入,欧克,开始整合 Spring.net 和.net mvc 控制器工厂 吧: ◆做一个 spring.net 配置文件,controllers.xml,还放在 web 的/config 目录中: ◆然后,为实现 IControllerFactory,在 bll 层中创建一个 SpringControllerFactory 类(一 旦此 bll 程序集被引用,所有 Views 请求都将视这个实现类为 Handler,控制器由它派发,这 是.net mvc 一个很妙的设计): using System; using System.Collections.Generic; using System.Text; using Spring.Core.IO; using Spring.Objects.Factory; using Spring.Objects.Factory.Xml; using System.Web.Mvc; using System.Web.Routing; using System.IO; using woodigg.DAO.Tool; using Spring.Context; using Spring.Context.Support; namespace woodigg.bll.Handler { /// /// 控制器工厂 /// public class SpringControllerFactory : IControllerFactory { /// /// 实现接口 /// /// /// /// public IController CreateController(RequestContext context, s tring name) { if (File.Exists(ParameterFactory.CfgControllersFilePath)) { //配置上下文 IApplicationContext configContext = new XmlApplicatio nContext( ParameterFactory.CfgFilePath, ParameterFactory.CfgBusinessFilePath, ParameterFactory.CfgControllersFilePat h); //首字母大写的控制器名字 string controllName = GetFirstUpcaseName(name) + "Con troller"; //返回对应的对象:IOC return (IController)configContext.GetObject(controllN ame); } else { IControllerFactory fa = ControllerBuilder.Current.Get ControllerFactory(); return fa.CreateController(context, name); } } public void DisposeController(IController controller) { } //首字母大写 private string GetFirstUpcaseName(string name) { string temp = name.Replace(".mvc","").ToLower(); //.net 2. 0 下的情况 string first = temp[0].ToString(); return temp.Replace(first, first.ToUpper()); } } } ◆这里需要说明的是 ParameterFactory:一个静态参数工厂。它将当前站点的 SERVER 端物 理目录存在 static string 类型的变量中,此处用到的三个变量 CfgFilePath、 CfgBusinessFilePath、CfgControllersFilePath,分别为三个 spring.net xml 配置文件所在 的物理路径。 显然,当 Views/User/Home.aspx 发出请求时,它会在/config/controllers.xml 中找到 UserController 控制器,并不算完——UserController 请求注入 UserMainDaoSpring 对象, 它又会在/config/business.xml 中找到这个对象的定义。完了吗?那得看 UserMainDaoSpring 是否也有注入别的对象了。 这种情况会乱吗?一点也不,即使你不用 spring.net 整合它们,也一样会用到这些对象,只是 使用的流程会有一些不同罢了。那么,在这个工厂中,只要捋清楚,你会用到哪些配置就行了(对 这些配置 xml 文件,还是按用途分类比较好,如页面一个,访问层一个,web services 一个…… 然后,去管理它们!)。
还剩81页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 6 金币 [ 分享pdf获得金币 ] 7 人已下载

下载pdf

pdf贡献者

liefdiy

贡献于2012-10-27

下载需要 6 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!