Spring.NET教程(十九)整合NHibernate和ASP.NET MVC(基礎篇)
今天帶給大家的就是期待以久的ASP.net MVC與Spring.NET和NHibernate的組合,視圖打造.NET版的SSH(Spring-Struts-Hibernate)。是不是聽到名字都很興奮?我認為目前的ASP.NET MVC比Struts在某些功能上要好用的多,而且代碼量要少,這就是我一直熱衷於ASP.NET MVC的原因。
我們接著昨天的例子學習。昨天我們成功測試了帶事務的業務層。接下來就是將業務層的對象註入到Controller中。我們先在Controller中寫好要註入的屬性。
HomeController
public class HomeController : Controller
{
public ICompanyManager CompanyManager { get; set; }
public IUserManager UserManager { get; set; }
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
ViewData["Company"] = CompanyManager.LoadAll();
return View();
}
}
我們知道,對Controller依賴註入需要新建一個ControllerFactory。我們實現System.Web.Mvc.IControllerFactory接口即可。實際上就是替換現有的ControllerFactory,讓Spring.NET容器管理Controller。包含Spring.NET容器配置的Controller使用新建的ControllerFactory,沒有包含Spring.NET容器配置的Controller使用原有的DefaultControllerFactory。
SpringControllerFactory
* www.cnblogs.com/GoodHelper
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Spring.Context;
using Spring.Context.Support;
using System.Web.MVC;
namespace Controllers
{
/// <summary>
/// Spring.net ControllerFacotry
/// </summary>
public class SpringControllerFactory : IControllerFactory
{
/// <summary>
/// Default ControllerFactory
/// </summary>
private static DefaultControllerFactory defalutf = null;
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
//get spring context
WebApplicationContext ctx = ContextReGIStry.GetContext() as WebApplicationContext;
string controller = controllerName + "Controller";
//查找是否配置該Controller
if (ctx.ContainsObject(controller))
{
object controllerf = ctx.GetObject(controller);
return (IController)controllerf;
}
else
{
if (defalutf == null)
{
defalutf = new DefaultControllerFactory();
}
return defalutf.CreateController(requestContext, controllerName);
}
}
public void ReleaseController(IController controller)
{
//get spring context
IApplicationContext ctx = ContextRegistry.GetContext();
if (!ctx.ContainsObject(controller.GetType().Name))
{
if (defalutf == null)
{
defalutf = new DefaultControllerFactory();
}
defalutf.ReleaseController(controller);
}
}
}
}
在Global.asax.cs的Application_Start方法下增加一些代碼,註冊SpringControllerFactory類。
Global.asax.cs
protected void Application_Start()
{
log4net.Config.XMLConfigurator.Configure();
ControllerBuilder.Current.SetControllerFactory(typeof(Controllers.SpringControllerFactory));
ReGISterRoutes(RouteTable.Routes);
}
然後我們以HomeController為例,增加一些方法。
HomeController
public class HomeController : Controller
{
public ICompanyManager CompanyManager { get; set; }
public IUserManager UserManager { get; set; }
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.net MVC!";
ViewData["Company"] = CompanyManager.LoadAll();
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SaveUser(User user)
{
user.CurrentCompany = CompanyManager.Get(user.CurrentCompany.CompanyID);
UserManager.Save(user);
return RedirectToAction("GetCompany", "Home", new { id = user.CurrentCompany.CompanyID });
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SaveCompany(Company company)
{
CompanyManager.Save(company);
return RedirectToAction("Index", "Home");
}
public ActionResult GetCompany(int id)
{
ViewData["Company"] = CompanyManager.Get(id);
return View();
}
public ActionResult About()
{
return View();
}
Controllers.XML
<?XML version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springFramework.net">
<object id="HomeController" type="Controllers.HomeController, Controllers" singleton="false">
<property name="CompanyManager" ref="CompanyManager"/>
<property name="UserManager" ref="UserManager"/>
</object>
</objects>
這裏的HomeController我們部署了非singleton模式。
最後我們配置Web.config。我總結了一下,有兩個要註意的地方:
1.需要在appSettings節點處配置SessionFactory的id
2.需要配置httpModules,因為這關系到SessionFactory的作用域,直接影響對象的“延遲加載”等一系列問題。實際上SessionFactory的開關有Spring.Data.NHibernate.LocalSessionFactoryObject來控制。實現原理是Spring.NET會在HttpApplication.BeginRequest的事件中註冊打開SessionScope的動作,並在HttpApplication.EndRequest的事件中註冊關閉SessionScope的動作。這就意味著SessionFactory完全由Spring.NET來管理,我們不需要使用using語句來強制關閉Session。
Web.config
<?xml version="1.0"?>
<!--
Note: As an alternative to hand editing this file you can use the
web admin tool to configure settings for your application. Use
the Website->ASP.Net Configuration option in Visual Studio.
A full list of settings and comments can be found in
machine.config.comments usually located in
\Windows\Microsoft.Net\Framework\v2.x\Config
-->
<configuration>
<configSections>
<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<sectionGroup name="WebServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="Everywhere"/>
<section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
</sectionGroup>
</sectionGroup>
</sectionGroup>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
<sectionGroup name="common">
<section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
</sectionGroup>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.WebContextHandler, Spring.Web" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
<section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" />
</sectionGroup>
<section name="databaseSettings" type="System.Configuration.NameValueSectionHandler"/>
</configSections>
<log4net debug="true">
<appender name="LogFileAppender" type="log4net.Appender.FileAppender">
<param name="File" value="Logs\Application.log.txt" />
<param name="datePattern" value="MM-dd HH:mm" />
<param name="AppendToFile" value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />
</layout>
</appender>
<appender name="HttpTraceAppender" type="log4net.Appender.ASPNetTraceAppender">
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />
</layout>
</appender>
<appender name="EventLogAppender" type="log4net.Appender.EventLogAppender">
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />
</layout>
</appender>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="File" value="Logs/Log.txt" />
<param name="AppendToFile" value="true" />
<param name="MaxSizeRollBackups" value="10" />
<param name="MaximumFileSize" value="100K" />
<param name="RollingStyle" value="Size" />
<param name="StaticLogFileName" value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />
</layout>
</appender>
<root>
<level value="ALL" />
<appender-ref ref="RollingLogFileAppender" />
</root>
</log4net>
<spring>
<parsers>
<parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
<parser type="Spring.Transaction.Config.TxNamespaceParser, Spring.Data" />
</parsers>
<context>
<resource uri="config://spring/objects" />
<resource uri="assembly://Repository/Repository/Repository.xml" />
<resource uri="assembly://Manager/Manager/Manager.xml" />
<resource uri="assembly://Controllers/Controllers/Controllers.xml"/>
</context>
<objects xmlns="http://www.springframework.net" />
</spring>
<!--數據庫連接字符串-->
<databaseSettings>
<add key="db.datasource" value="." />
<add key="db.user" value="sa" />
<add key="db.password" value="" />
<add key="db.database" value="SpringNet_Lesson18" />
</databaseSettings>
<!--很重要-->
<appSettings>
<add key="Spring.Data.NHibernate.Support.OpenSessionInViewModule.SessionFactoryObjectName" value="NHibernateSessionFactory" />
</appSettings>
<connectionStrings/>
<system.web>
<!--
Set compilation debug="true" to insert debugging
symbols into the compiled page. Because this
affects performance, set this value to true only
during development.
-->
<compilation debug="true">
<assemblies>
<add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.Abstractions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.MVC, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Data.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="MvcContrib"/>
</assemblies>
</compilation>
<!--
The <authentication> section enables configuration
of the security authentication mode used by
ASP.NET to identify an incoming user.
-->
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880"/>
</authentication>
<membership>
<providers>
<clear/>
<add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="ApplicationServices" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression="" applicationName="/"/>
</providers>
</membership>
<profile>
<providers>
<clear/>
<add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="ApplicationServices" applicationName="/"/>
</providers>
</profile>
<roleManager enabled="false">
<providers>
<clear/>
<add connectionStringName="ApplicationServices" applicationName="/" name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
<add applicationName="/" name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
</providers>
</roleManager>
<!--
The <customErrors> section enables configuration
of what to do if/when an unhandled error occurs
during the execution of a request. Specifically,
it enables developers to configure html error pages
to be displayed in place of a error stack trace.
<customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
<error statusCode="403" redirect="NoAccess.htm" />
<error statusCode="404" redirect="FileNotFound.htm" />
</customErrors>
-->
<pages>
<controls>
<add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</controls>
<namespaces>
<add namespace="System.Web.Mvc"/>
<add namespace="System.Web.Mvc.Ajax"/>
<add namespace="System.Web.Mvc.Html"/>
<add namespace="System.Web.Routing"/>
<add namespace="System.Linq"/>
<add namespace="System.Collections.Generic"/>
<add namespace="MvcContrib.UI.Tags"/>
<add namespace="MvcContrib.UI"/>
<add namespace="MvcContrib.UI.Html"/>
<add namespace="MvcContrib.UI.Html.Grid"/>
<add namespace="MvcContrib"/>
</namespaces>
</pages>
<httpHandlers>
<remove verb="*" path="*.asmx"/>
<add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false"/>
<add verb="*" path="*.mvc" validate="false" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpHandlers>
<httpModules>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<!--很重要-->
<add name="OpenSessionInView" type="Spring.Data.NHibernate.Support.OpenSessionInViewModule, Spring.Data.NHibernate21"/>
<add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web" />
</httpModules>
</system.web>
<system.codedom>
<compilers>
<compiler language="C#;cs;csharp" extension=".cs" warningLevel="4" type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<providerOption name="CompilerVersion" value="v3.5"/>
<providerOption name="WarnAsError" value="false"/>
</compiler>
<compiler language="VB;vbs;visualbasic;VBScript" extension=".vb" warningLevel="4" type="Microsoft.VisualBasic.VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<providerOption name="CompilerVersion" value="v3.5"/>
<providerOption name="OptionInfer" value="true"/>
<providerOption name="WarnAsError" value="false"/>
</compiler>
</compilers>
</system.codedom>
<system.web.extensions/>
<!--
The system.webServer section is required for running ASP.NET AJAX under Internet
Information Services 7.0.It is not necessary for previous version of IIS.
-->
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="true">
<remove name="ScriptModule"/>
<remove name="UrlRoutingModule"/>
<add name="ScriptModule" preCondition="managedHandler" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</modules>
<handlers>
<remove name="WebServiceHandlerFactory-Integrated"/>
<remove name="ScriptHandlerFactory"/>
<remove name="ScriptHandlerFactoryAppServices"/>
<remove name="ScriptResource"/>
<remove name="MvcHttpHandler"/>
<remove name="UrlRoutingHandler"/>
<add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="MvcHttpHandler" preCondition="integratedMode" verb="*" path="*.mvc" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd" type="System.Web.HttpForbiddenHandler, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
</handlers>
</system.webServer>
</configuration>
我們運行一下程序。
我個人認為Spring.NET在SessionFactory和事務控制上對Nhibernate支持的很好,減少了我們的代碼量和增加了擴增性,這就是為什麽Nhibernate與Spring.NET能成為一對很好組合的原因。
Spring.NET教程(十九)整合NHibernate和ASP.NET MVC(基礎篇)