Friday, June 12, 2009

Learn NInject, FluentNHibernate, NHibernate through Canvas example – 1 ( Set up the NInjector in application start)

Codebettercanvas is a learning application published by Karl Seguin to teach some best practices around very cool oss projects such as NInject, NHibernate, JQuery, log4Net. This is a more advanced example than the previous examples such as NerdDinner, and Contact Manager.  I would definitely suggest going through those examples before reading the source code of this example.

The codebettercanvas didn’t follow the default Asp.Net mvc project structure. The default project structure will have three separate folders called Controllers, Models, and Views. In this example, Karl put controllers, helpers, components and binders in different folder. The model itself is in a separate project.

DefaultArchitecture CropperCapture[1]

I am not sure whether it’s a good idea or not, but I guess the example tries to show us the principle of “separation of concerns'”. The whole idea of MVC is “convention over configuration”, this directory structure change looks like a break from the default convention, so I am not very sure whether this is a good idea or not.

The Global.aspx.cs is the entry point for the asp.net application, just like the Main function in the console program. It will be run at the first time the applications starts. Here is the code.

    public class MvcApplication :  NinjectHttpApplication 
{
public override void Init()
{
Error += OnError;
EndRequest += OnEndRequest;
base.Init();
}

public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("Default","{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "" });
}

protected override void OnApplicationStarted()
{
RegisterAllControllersIn("CodeBetter.Canvas.Web");

log4net.Config.XmlConfigurator.Configure();
RegisterRoutes(RouteTable.Routes);

Factory.Load(new Components.WebDependencies());
ModelBinders.Binders.DefaultBinder = new Binders.GenericBinderResolver(Factory.TryGet<IModelBinder>);

ValidatorConfiguration.Initialize("CodeBetter.Canvas");
HtmlValidationExtensions.Initialize(ValidatorConfiguration.Rules);
}

private void OnEndRequest(object sender, System.EventArgs e)
{
if (((HttpApplication)sender).Context.Handler is MvcHandler)
{
CreateKernel().Get<ISessionSource>().Close();
}
}
private void OnError(object sender, System.EventArgs e)
{
CreateKernel().Get<ISessionSource>().Close();
}

protected override IKernel CreateKernel()
{
return Factory.Kernel;
}
}


Instead of inheriting from the HttpApplication,  it chooses to inherit from NinjectHttpApplication. NinjectHttpApplication is an extender of HttpApplication which is based on NInject. Here is the code from the reflector.



public abstract class NinjectHttpApplication : HttpApplication, IHaveKernel
{
// Fields
private static IKernel _kernel;

// Methods
protected NinjectHttpApplication();
public void Application_Start();
public void Application_Stop();
protected abstract IKernel CreateKernel();
private static string GetControllerName(Type type);
private static bool IsController(Type type);
protected virtual void OnApplicationStarted();
protected virtual void OnApplicationStopped();
public void RegisterAllControllersIn(Assembly assembly);
public void RegisterAllControllersIn(string assemblyName);
public void RegisterAllControllersIn(Assembly assembly, Func<Type, string> namingConvention);
public void RegisterAllControllersIn(string assemblyName, Func<Type, string> namingConvention);

// Properties
public IKernel Kernel { get; }
}

The first call in the OnApplicationStarted() of the MvcApplication class is RegisterAllControllersIn("CodeBetter.Canvas.Web"), and this function is used to register all the controlles inside the assembly of "CodeBetter.Canvas.Web", so NInject knows where to look for it in the later use.

By peeking inside NInject,Web.Mvc, The RegisterAllControllersIn will then call function RegisterAllControllersIn(assembly, new Func<Type, string>(NinjectHttpApplication.GetControllerName)). NinjectHttpApplication.GetControllerName will take a control type name and return its name in string format. The function will expand like this:



public void RegisterAllControllersIn(Assembly assembly, Func<Type, string> namingConvention)
{
foreach (Type type in assembly.GetExportedTypes().Where<Type>(new Func<Type, bool>(NinjectHttpApplication.IsController)))
{
_kernel.Bind<IController>().To(type).InTransientScope().Named(namingConvention(type));
}
}


Basically, it will loop through all the controller types in the assembly, and bind the control type to the control name, which is calculated based on the function namingConvention(type).  This will explain some magic binding in the later section.



The next line is the configuration for log4Net, log4net.Config.XmlConfigurator.Configure(); This configuration is based on the configuration section in web.config



  <log4net>
<appender name="CustomTracer" type="log4net.Appender.TraceAppender, log4net">
<layout type="log4net.Layout.SimpleLayout"/>
</appender>
<root>
<level value="DEBUG"/>
<appender-ref ref="CustomTracer"/>
</root>
<logger name="NHibernate">
<level value="ALL"/>
<appender-ref ref="CustomTracer"/>
</logger>
</log4net>


I am not very familiar with the log4Net, so I won’t explain too much here.



The next line is Factory.Load(new Components.WebDependencies()), which will load all the web dependencies. Factory is a static helper class to work with NInject.



   public static class Factory
{
private static readonly IKernel _kernel = new StandardKernel(new CoreDependencies());

public static IKernel Kernel
{
get { return _kernel; }
}

public static T Get<T>()
{
return _kernel.Get<T>();
}
public static T Get<T>(Type type)
{
return (T) _kernel.Get(type);
}
public static T TryGet<T>()
{
return _kernel.TryGet<T>();
}
public static T TryGet<T>(Type type)
{
return (T)_kernel.TryGet(type);
}

public static void Load(INinjectModule module)
{
_kernel.Load(module);
}
}


and the WebDependencies is inherited from NinjectModule



    public class WebDependencies : NinjectModule
{
public override void Load()
{
Bind<IAuthenticationManager>().To<AuthenticationManager>().InRequestScope();
Bind<ModelBinder<User>>().To<UserBinder>().InRequestScope();
}
}




By binding the WebDependencies , next time, if the application requests IAuthenticationManager, the AuthenticationManager will return to meet the request. If the application requests ModelBinder<User>,  then UserBinder will return. Besides the WebDependencies , the Factory also initializes a static kernel which retains a reference to the CoreDependencies: private static readonly IKernel _kernel = new StandardKernel(new CoreDependencies());



  public class CoreDependencies : NinjectModule
{
public override void Load()
{
var connectionString = string.Format("Data Source={0};Version=3;New=False;", Configuration.GetConfig().ConnectionString);
var configuration = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.ConnectionString(c => c.Is(connectionString)))
.Mappings(m => m.FluentMappings
.AddFromAssemblyOf<User>()
.ConventionDiscovery.AddFromAssemblyOf<TableNameConvention>());

Bind<ISessionSource>()
.To<SessionSource>()
.InRequestScope()
.WithConstructorArgument("config", configuration);

Bind<IUserRepository>().To<UserRepository>().InRequestScope();
Bind<IValidator>().To<Validator>();
Bind<IEncryptor>().To<Encryptor>();
}
}




Next line is to replace the default model binder with a customized model binder as this:  ModelBinders.Binders.DefaultBinder = new Binders.GenericBinderResolver(Factory.TryGet<IModelBinder>); This further demonstrates that Asp.net MVC is highly extensible architecture.



The next two lines are validation configuration



          ValidatorConfiguration.Initialize("CodeBetter.Canvas");
HtmlValidationExtensions.Initialize(ValidatorConfiguration.Rules);

The validation logic deserves another seperate post.



That’s about everything when the application starts, and in my view, it’s the most important step to set everything up.

1 comments:

sasha.nike said...

what about source code?