Spring.NET Validation Framework setup with ASP.NET MVC - asp.net-mvc

I'm starting a new project for work, and I decided I want to give MVC a shot. It's a small internal site for a commute challenge.
I want to use Spring.NET for Validation. I have used Spring.NET before in Web Forms, but with no code behind as in traditional ASP.NET, how do I use the Page Validation framework Spring.NET provides?
Edit 1:
In an attempt to try this myself, here is what I have:
Web.Config
<?xml version="1.0"?>
<configuration>
<configSections>
<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>
</configSections>
<appSettings>
<add key="RouteValidator" value="RouteValidator"/>
<add key="UserValidator" value="UserValidator"/>
</appSettings>
<spring>
<context>
<resource uri="config://spring/objects"/>
<resource uri="~/Config/Spring.Web.cfg.xml" />
<resource uri="~/Config/Spring.Validation.cfg.xml" />
</context>
<parsers>
<parser type="Spring.Validation.Config.ValidationNamespaceParser, Spring.Core" />
</parsers>
</spring>
<system.web>
<httpModules>
<add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web" />
</httpModules>
</system.web>
</configuration>
Spring.Web.Cfg.xml
<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd">
<description>
Foo MVC Controller declarations.
</description>
<object id="HomeController" type="Foo.MVC.Web.Controllers.HomeController, Foo.MVC.Web"></object>
<object id="AccountController" type="Foo.MVC.Web.Controllers.RouteController, Foo.MVC.Web"></object>
<object id="RouteController" type="Foo.MVC.Web.Controllers.RouteController, Foo.MVC.Web"></object>
<object id="Spring.Web.UI.Controls.ValidationError" abstract="true">
<property name="Renderer">
<object type="Spring.Web.UI.Validation.IconValidationErrorsRenderer, Spring.Web">
<property name="IconSrc" value="validation-error.gif"/>
</object>
</property>
</object>
<object id="Spring.Web.UI.Controls.ValidationSummary" abstract="true">
<property name="Renderer">
<object type="Spring.Web.UI.Validation.DivValidationErrorsRenderer, Spring.Web">
<property name="CssClass" value="validationError"/>
</object>
</property>
</object>
<object id="standardPage" abstract="true">
<property name="MasterPageFile" value="~/Views/Shared/Site.master"/>
<property name="CssRoot" value="~/Content/"/>
<property name="ImagesRoot" value="~/Content"/>
</object>
</objects>
My validation file is very standard and basically a copy and paste from another project, therefore I didn't include it.
Now the problem I have is how do I use it? How do I get application context? My web forms project users Spring.Web.UI.Page, but I'm worried because the default pages in MVC derive from System.Web.Mvc.ViewPage, so that isn't going to work.
Or am I just not able to use Spring.NET's framework for MVC quite yet?
Thanks!
Thanks for any assistance.

You definitely can use Spring with ASP.Net MVC. You need to register that you are using it in the Global.ascx class then the framework will create Controllers based on what you have defined in your config file.
public class MvcApplication : System.Web.HttpApplication
{
...Routes stuff...
protected void Application_Start()
{
ControllerBuilder.Current.SetControllerFactory(typeof(ControllerFactory));
RegisterRoutes(RouteTable.Routes);
}
}
public class ControllerFactory : IControllerFactory
{
public IController CreateController(RequestContext requestContext, string controllerName)
{
return IoC.Resolve<IController>(controllerName);
}
public void ReleaseController(IController controller)
{
//This is a sample implementation
//If pooling is used write code to return the object to pool
if (controller is IDisposable)
{
(controller as IDisposable).Dispose();
}
controller = null;
}
}
public static class IoC
{
static readonly IObjectFactory Factory
= new XmlObjectFactory(new FileSystemResource
(HttpContext.Current.Server.MapPath("~/Config/Spring.config")));
public static T Resolve<T>(string name)
{
return (T)Factory.GetObject(name);
}
}
Just make sure that the path to your spring config file is correct! This was adapted from this link.
On a wider note, this approach does not allow you to Spring the page classes, and being an MVC architecture, where views are pretty dumb classes, does not really support rich validation in the view itself in the manner you suggest. Look at either including the validation in the Model (post-back) in JQuery.

In best of my knowledge, the Spring Validation is not supported up to the latest release of ASP.NET MVC (1.0) and Spring.NET framework (1.3).
As far as incorporating Spring.NET with MVC, you can use MvcContrib project and come to the same code-base as posted by Colin Desmond (but you don't have to do the dirty work yourself).

Related

Using MvcSiteMapProvider with exising StaticSiteMapProvider class

I'm converting as webforms app to MVC. The previous programmer built a class that inherits the StaticSiteMapProvider class to create his sitemap. He then placed this control on the page
<asp:SiteMapDataSource ID="sitemapSQLMenuProvider" runat="server" ShowStartingNode="False" />
and initialized it like this
Dim oSqlSiteMapProvider As New SQL.SiteMap.Provider.SQLSiteMapProvider(Session("KPISystemUserID"), PagePath)
oSqlSiteMapProvider.Initialize("MySiteMap", Nothing)
SiteMapDataSource1.Provider = oSqlSiteMapProvider.
He then used the menu control and specified the data source
<asp:Menu ID="menuMaster" runat="server" DataSourceID="sitemapSQLMenuProvider"/>
I created a class in my MVC project and copied the code over to create the SQLSiteMapProvider class. It created nodes... subnodes.. etc. Is there an easy way with the MvcSiteMapProvider package from Nuget to simply specify this class as where to get the Nodes from? All the documentation just keeps trying to get you to use a static XML file (which i can't use because our menus come from the DB).
It looks like the webform programmer set up his provider like this in the web.config
<siteMap defaultProvider="KPIMap" enabled="false">
<providers>
<clear />
<add name="KPIMap" type="System.Web.XmlSiteMapProvider" siteMapFile="~/System/MY.sitemap" />
</providers>
</siteMap>
and that xml file was simply
<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
<siteMapNode url="" title="" description="">
<siteMapNode url="" title="" description="" />
<siteMapNode url="" title="" description="" />
</siteMapNode>
</siteMap>
You'd think they would have added a simple way to plug in the name of a StaticSiteMapProvider class and be done with it.

Log4net logging not working - asp.net mvc

My log4net configuration is this,
<?xml version="1.0" encoding="utf-8" ?>
<log4netConfiguration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<appSettings>
<add key="log4net.Config" value="log4net.config" />
</appSettings>
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="C:\my_logs/my_web_logs/my_log_%date{ddMMyyyy}.log" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="5" />
<maximumFileSize value="30MB" />
<datePattern value="yyyyMMdd" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%4t %d{ABSOLUTE} %-5p %m%n"/>
<!--<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] – %message%newline" />-->
</layout>
</appender>
<root>
<level value="INFO" />
<appender-ref ref="RollingFileAppender" />
</root>
</log4net>
</log4netConfiguration>
I have a Logger helper class as,
public static class Logger
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public static log4net.ILog Log
{
get { return log; }
}
}
In my assembly info, I have this entry,
// Configure log4net using the .config file
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", Watch = true)]
log4net.config is the config file added to the web project.
In the code I log using the logger class,
Logger.Log.Info("User visits Sign In Page.");
Logging has been working when I set up the above setting. But suddenly logging has stopped working.
But when I created a new asp.net mvc website with above settings, logging works for that.
I tried with IIS Express and Local IIS. In both cases logging works for the test application I have created.
I cannot figure out why it's not logging? How can I diagnose this? What are the possible issues?
Solved by myself, reason was "for some reason" log4net configuration was not loaded from assembly info. Still I do not know why that happens.
I tried so many fixes proposed by different posts. Finally fixed the issue.
Solution mentioned in this post helped me to solve the issue.
I have added following configuration,
<!--These settings load the log4net configuration.-->
<add key="log4net.Config" value="log4net.config"/>
<add key="log4net.Config.Watch" value="True"/>
It starts logging!
Then I removed following line from assembly info,
// Configure log4net using the .config file
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", Watch = true)]
The reason is that Log4Net tries to load the config from the assembly that first uses LogManager.GetLogger(). If it's one of your class libraries it will simply ignore the attribute in all other assemblies.
The easiest way to fix it is to invoke LogManager in your start file (like Program.cs or Global.asax):
var logger = LogManager.GetLogger(typeof(Program));
logger.Info("Application started.");
//rest of app init.
Doing that will get you the expected behavior with the assembly attribute.
I found log4net won't load the webconfig configuration unless you call log4net.Config.XmlConfigurator.Configure(); on start up.
protected void Application_Start()
{
// your other codes
log4net.Config.XmlConfigurator.Configure(); // must have this line
Logger = log4net.LogManager.GetLogger(typeof(MvcApplication));
}
I had a similar issue: I would not get any logging output, when I ran my assembly from IIS or IISExpress.
However, none of the answers above worked for me.
In my case the solution was to specify the path to the config file as an absolute path. It turned out that IISExpress does not set the current directory to the bin folder and log4net would not find the config file, so I had to use this workaround:
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
// remove file:// part from uri
UriBuilder ub = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(ub.Path);
var fi = new FileInfo(Path.Combine(Path.GetDirectoryName(path), "Logging.config.xml"));
if (fi.Exists)
{
log4net.Config.XmlConfigurator.ConfigureAndWatch(fi);
var logger = log4net.LogManager.GetLogger(typeof(WebApiApplication));
logger.Info("Application started.");
}
else
{
throw new FileNotFoundException("log4net config file not found", fi.FullName);
}

NHibernate + Spring.NET lazy loading failed - no session

I use Spring.NET AOP for transaction and session management with NHibernate. When user makes several requests too quick - lazy loading is failed with exception "no session or session was closed".
I use SpringSessionContext as CurrentSessionContext in NHibernate configuration
public class FluentSessionFactory : LocalSessionFactoryObject
{
protected override ISessionFactory NewSessionFactory(Configuration config)
{
var conf = Fluently
.Configure()
.Database(
MsSqlConfiguration
.MsSql2008
.ConnectionString(c => c.FromConnectionStringWithKey("MyConnection"))
// TODO: use ExposeConfiguration method
.CurrentSessionContext<SpringSessionContext>()
)
.Mappings(
m => m.FluentMappings
.AddFromAssembly(this.GetType().Assembly)
)
.BuildSessionFactory();
return conf;
}
}
In xml config:
<object id="SessionFactory" type="IndustryTracker.NHibernateRepository.FluentSessionFactory, IndustryTracker.NHibernateRepository">
<property name="DbProvider" ref="DbProvider" />
</object>
and OpenSessionInView module
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="true">
<add name="OpenSessionInView" type="Spring.Data.NHibernate.Support.OpenSessionInViewModule, Spring.Data.NHibernate31"/>
</modules>
</system.webServer>
Application implements next workflow for getting entities from db: View -> Controller -> Manager -> Repository and same to other side. So session is created per request, transaction - per call to manager.
<object id="TransactionManager" type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate31">
<property name="DbProvider" ref="DbProvider"/>
<property name="SessionFactory" ref="SessionFactory"/>
</object>
<tx:advice id="TxAdvice" transaction-manager="TransactionManager">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<object id="Pointcut" type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop">
<property name="patterns">
<list>
<value>MyAppication.Managers.AccountManager</value>
<value>MyAppication.Managers.CompanyManager</value>
</list>
</property>
</object>
<aop:config>
<aop:advisor advice-ref="TxAdvice" pointcut-ref="Pointcut"/>
</aop:config>
What are possible reasons of such behaviour and how can I solve this problem(Not.LazyLoad() and NHibernateUtil.Initialize() are not acceptable variants in my context)?
After some search I found that problem was in configuration. There was spring WebSupportModule missing http module in config, so the correct one is:
<httpModules>
<add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
<add name="OpenSessionInView" type="Spring.Data.NHibernate.Support.OpenSessionInViewModule, Spring.Data.NHibernate31"/>
</httpModules>
So Marijn was right - it was weak wiring to spring.
1. Session factory configured for OpenSessionInViewModule?
You might have forgotten to configure the session factory for the OpenSessionInViewModule:
<appSettings>
<add key="Spring.Data.NHibernate.Support.OpenSessionInViewModule.SessionFactoryObjectName"
value="SessionFactory"/>
</appSettings>
This has to be done in the app settings.
2. Correct FluentNHibernate based spring session factory?
You seem to be configuring your session factory in code. Have you tried configuring the session factory like described in the docs and on BennyM 's blog? Your NewSessionFactory method returns a session factory from straight from Fluent NHibernate, bypassing all spring.net support.
3. Is your session factory transaction aware?
<object id="SessionFactory"
type="IndustryTracker.NHibernateRepository.FluentSessionFactory, IndustryTracker.NHibernateRepository">
<property name="DbProvider" ref="DbProvider" />
<!-- provides integation with Spring's declarative transaction management features -->
<property name="ExposeTransactionAwareSessionFactory" value="true" />
</object>
4. Does your controller have dependencies with scope="application" or no scope definition?
I might have been looking in the wrong direction here. If your controller has dependencies of application scope, it would mean that quick requests could interfere. The default is "scope="application"; so you'd want to check collaborators without scope definitions too. See the docs on web scopes.

Spring Framework basics

I'm quite new to Spring Framework. Could someone please help me understand the spring configuration below?
<?xml version="1.0"?>
<configuration>
<spring>
<context>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<object type="Test.aspx">
<property name="AService" ref="AService" />
<property name="BService" ref="BService" />
</object>
</objects>
</spring>
</configuration>
Basically questions in my mind are:
What does this line means:
<resource uri="config://spring/objects" />
and this:
<object type="Test.aspx">
<property name="AService" ref="AService" />
<property name="BService" ref="BService" />
</object>
Does config: means configuration file?
Does ref means Classes in C#?
<resource uri="config://spring/objects" /> means that the spring container should read a configuration section from an application configuration file (app.config or web.config).
<object ... is an object definition; this defines an object in your container. An object can have dependencies. In your case, the Test.aspx page has properties named AService and BService. The container will set these properties to the objects defined elsewhere in your container.
What might be a bit confusing here is the double usage of ="AService" in <property name="AService" ref="AService" />:
name=: refers to the name of the property on your class Test, there is a property defined as public IMyService AService { get; set; }
ref= : refers to another object defined in your container, there is an object definition like <object id="AService" type="MyNamespace.MyClass, MyAssembly" /> somewhere in your configuration.
The "Instantiating the container" section of the spring docs does a good job of explaining this further.

Log4Net on MVC.NET using Ninject

I'm trying to set up Log4Net in a MVC.net project with Ninject.
I've reached the point where I'm able to see the Ninject debug output but I am still not seeing any logging output.
This is all running on my localhost so there shouldn't be any issues with security.
The setup I have is as follows:
In the AssemblyInfo.cs I have added this to point log4net to Web.config for configuration
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "Web.config", Watch = true)]
In the Web.config I've added the following sections
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,Log4net"/>
</configSections>
...
<appSettings>
<add key="log4net.Internal.Debug" value="true"/>
</appSettings>
...
<log4net>
<appender name="FileAppender" type="log4net.Appender.FileAppender">
<file value="log-file.txt" />
<appendToFile value="true" />
<encoding value="utf-8" />
<layout type="log4net.Layout.SimpleLayout" />
</appender>
<root>
<level value="ALL" />
<appender-ref ref="FileAppender" />
</root>
</log4net>
In my base controller, from which all the other controllers in the system inherit I have the following:
[Inject]
public ILogger logger { get; set; }
I then put an explicit throw new Exception("testException") on the login page to force an error which I would have expected to see showing up in the log file.
From the debug trace I see the following on startup.
log4net: log4net assembly [log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821]. Loaded from [C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\portal\63a118bb\781d84b7\assembly\dl3\40d137a6\9fb44da2_c951cc01\log4net.dll]. (.NET Runtime [4.0.30319.235] on Microsoft Windows NT 6.1.7601 Service Pack 1)
log4net: DefaultRepositorySelector: defaultRepositoryType [log4net.Repository.Hierarchy.Hierarchy]
log4net: DefaultRepositorySelector: Creating repository for assembly [Ninject.Extensions.Logging.Log4Net, Version=2.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7]
log4net: DefaultRepositorySelector: Assembly [Ninject.Extensions.Logging.Log4Net, Version=2.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7] Loaded From [C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\portal\63a118bb\781d84b7\assembly\dl3\edd36351\91af46a2_c951cc01\Ninject.Extensions.Logging.Log4Net.dll]
log4net: DefaultRepositorySelector: Assembly [Ninject.Extensions.Logging.Log4Net, Version=2.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7] does not have a RepositoryAttribute specified.
log4net: DefaultRepositorySelector: Assembly [Ninject.Extensions.Logging.Log4Net, Version=2.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7] using repository [log4net-default-repository] and repository type [log4net.Repository.Hierarchy.Hierarchy]
log4net: DefaultRepositorySelector: Creating repository [log4net-default-repository] using type [log4net.Repository.Hierarchy.Hierarchy]
I have the feeling that this is probably a configuration issue but any help would be appreciated.
Injecting an ILog or ILogger instance does not implicitly register any kind of exception handler. It just gives you access to a logger. You still have to tell it what to log and when to log it.
If you want to test your configuration, make a logging call from one of your controller's actions, or use one of the OnAction event invokers. This should probably do what you want:
public class MyController : Controller
{
protected override void OnActionExecuted(ActionExecutedContext context)
{
if (Logger != null)
{
string message = string.Format("Action executed: {0}.{1}",
context.ActionDescriptor.ControllerDescriptor.ControllerName,
context.ActionDescriptor.ActionName);
Logger.Log(typeof(MyController), log4Net.Core.Level.Trace,
message, context.Exception);
}
base.OnActionExecuted(context);
}
[Inject]
public ILogger Logger { get; set; }
}
That will log every controller action - including exceptions. If you only care about exceptions then either check the context.Exception and context.ExceptionHandled properties before logging, or better yet write a custom ActionFilter to do this.

Resources