How do I inject dependencies into the global.asax.cs, i.e. the MvcApplication class?
Having previously used the Service Locator (anti-)pattern for dependency injection, I am trying to follow best practice advice in my latest MVC application by using an IOC container (specifically Unity.Mvc3 because it comes with an implementation of the IDependencyResolver out of the box) and constructor injection.
Everything seems quite straight forward so far except for a couple of snags, one of which is in the global.asax.cs (the other is for custom attributes but there's aleady a question on SO covering that).
The HttpApplication event handlers in the MvcApplication class such as:
Application_Start()
Application_EndRequest(object sender, EventArgs e)
Application_AcquireRequestState(object sender, EventArgs e)
may require external dependencies, e.g. a dependency on an ILogService. So how do I inject them without resorting to the service locator (anti-)pattern of e.g.
private static ILogService LogService
{
get
{
return DependencyResolver.Current.GetService<ILogService>();
}
}
Any help/advice greatly appreciated!
The class in your global.asax.cs is your Composition Root, so you can't (and shouldn't) inject anything into it from the outside.
However, there's only one instance of the MvcApplication class, so if you need a service in one of its methods, you can just declare it as a member field - e.g:
public class MvcApplication : System.Web.HttpApplication
{
private readonly ILogService log;
public MvcApplication()
{
this.log = new MyLogService();
}
protected void Application_Start()
{
// ...
this.log.Log("Application started");
}
}
You must use AutofacConfig.Resolve<T>() instead using DependencyResolver.Current.GetService<T>() to get your services without errors.
Related
I have an MVC3 project that I am using Ninject to inject an Entity Framework context into. I am using the Ninject package (3.0.0.15), Ninject.MVC3 (3.0.0.6), and Ninject.Web.Common (3.0.0.7). Everything is working really great, except when I try to inject into a WebForms code behind file. I am assuming that this is because I don't have something wired in correctly, but am not sure at how to wire it in. Ninject is also not working in files that Razor instantiates.
Here is my code for my Code Behind:
[Inject]
public IDbContext DataContext { get; set; }
The Context property comes out null every time. It worked just fine until I updated to Ninject 3.0.
My start method is as follows:
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
Bootstrapper.Initialize(CreateKernel);
}
Any ideas on how to make Ninject inject the DataContext into the WebForm and into classes instantiated by Razor?
For this to work you need to install the Ninject.Web NuGet (the latest version at the time of this writing is 3.0.0.5) and then have your webform derive from Ninject.Web.PageBase instead of System.Web.UI.Page:
public partial class WebForm1 : Ninject.Web.PageBase
{
[Inject]
public IDbContext Ctx { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
}
}
Also notice that I used Ctx as property name because there's already a property called Context on the System.Web.UI.Page class that you are hiding (you should have gotten a compile time warning).
I am fairly new to Ninject as well and DI in general. I use NHibernate as my ORM for my MVC app and have been quite happy with my results. That is, until I upgraded from Ninject 2.1 to 2.2.
Now, I get errors within my NinjectWebsiteApplication class due to using Ninject’s Kernel as a resource locator.
Example:
void NinjectWebsiteApplication_BeginRequest(object sender, System.EventArgs e)
{
ILogger logger = Kernel.Get<ILogger>();
logger.Debug(“**********REQUEST BEGIN****************”);
logger.Debug(“**** URL = ” + Request.Url.AbsolutePath);
}
Example 2:
protected override void OnApplicationStarted()
{
var bootstrapper = Kernel.Get<Bootstrapper>();
bootstrapper.RegisterAllAreas();
AreaRegistration.RegisterAllAreas();
......
(More stuff here, like AutoMapper mappings, etc.)
......
}
*The Bootstrapper class is a class I created where I register my routes, global filters, etc.
In both of the above examples, I receive a warning about the Kernel.Get() functions that states the following:
'Ninject.Web.Mvc.NinjectHttpApplication.Kernel' is obsolete: "Do not use Ninject as Service Locator"
After conducting several searches on this, the general consensus is that this is true.
I am trying to work around this, but am at a bit of a loss as to what to do.
I loaded the newest Ninject.Web.Mvc NuGet package which creates the NinjectMVC3 static class under the App_Start folder. I see that they're referencing Microsoft.Web.Infrastructure.DynamicModuleHelper, but I don't see where that fits in to what I'm trying to do.
If anyone has any hints that will help me fix my little mess, I would greatly appreciate it!
The way to deal with the first is not to use the NinjectWebsiteApplication_BeginRequest event but to write a custom global action filter:
public class LogActionFilterAttribute : ActionFilterAttribute
{
private readonly ILogger _logger;
public LogActionFilterAttribute(ILogger logger)
{
_logger = logger;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
_logger.Debug("**********REQUEST BEGIN****************");
_logger.Debug("**** URL = " + filterContext.HttpContext.Request.Url.AbsolutePath);
}
}
and then in your App_Start/NinjectMVC3.cs:
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ILogger>().To<Logger>();
kernel.BindFilter<LogActionFilterAttribute>(FilterScope.Global, 1);
}
Don't forget to add using Ninject.Web.Mvc.FilterBindingSyntax; in order to bring the BindFilter<> extension method into scope.
And since you have access to the kernel inside the RegisterServices method which happens at application startup you could wire up everything else including your bootstrapper, ...
As far as your Global.asax is concerned you no longer use any Ninject specific stuff in it. You should not derive from NinjectApplication.
The WebActivator infrastructure allows you to have a separate initialization method.
Simple question I guess, but I've spent an hour trying to get a base class for my controllers to have a few services injected via property injection. The properties were scoped protected at first, but the objects kept coming back null, once I changed the scope to public it worked. Is there anyway to have the properties be protected and get the IoC to work?
Here is my setup.
public class BaseController : Controller
{
[Inject]
protected LoggingInterface.ILogger<BaseController> Logger { set; get; }
[Inject]
protected IRepository Repository { set; get; }
protected override void OnAuthorization(AuthorizationContext filterContext)
{
....
base.OnAuthorization(filterContext);
}
}
and the boot-strapper in the NinjectMVC3 App_Start
private static void RegisterServices(IKernel kernel)
{
kernel.Bind(typeof(LoggingInterface.ILogger<>)).To(typeof(Log4NetLogger<>));
kernel.Bind<IRepository>().To<Repository>();
kernel.Bind<IUserService>().To<UserService>();
}
Thank you,
Stephen
You cannot inject into properties that do not have public setter. Both your Logger and Repository properties are protected so no way for Ninject to assign them a value. You will have to change their setter visibility if you want to achieve this. Or use constructor injection. While this would make perfect sense for the repository property which seems required it wouldn't make sense for the logger property. So I guess you will have to make it public.
Pretty sure the InjectNonPublic flag on the NinjectSettings allows you to configure it to do what you want - closest link I can find quickly
Whether it's going to be supported for any significant length of time, I don't know - injecting privates is just a bad idea (along with Property Injection and associated scoundrels :P)
I use the nuget template way of ninjectning my MVC3 app,
which means I have use WebActivator to invoke a method on a static class that in turn creates a Ninject bootstrapper and hooks up to MVC3.
That works fine for Controller, adapters etc. But I want to have another Webactivator activated class which gets its dependencies using Ninject.
I got it to work with a poor mans solution, but I would prefer a more elegant solution.
First I make sure my Webactivator class uses the PostApplicationStartMethod invoke, since the Ninject module uses the PreApplicationStartMethod I can ensure that ninject has been loaded and is ready to go.. THen in the Start method I do
var workers = DependencyResolver.Current.GetServices<IWorker>();
To get my dependencies, the whole class looks like this
[assembly: WebActivator.PostApplicationStartMethod(typeof(SHB.DALA.Web.App_Start.WorkflowRunner), "Start")]
namespace SHB.DALA.Web.App_Start
{
public static class WorkflowRunner
{
public static void Start()
{
var workers = DependencyResolver.Current.GetServices<IWorker>();
//Do stuff with worker collection
}
}
}
There must be a more elegant solution right?
WebActivator (ASP.NET really) doesn't have any knowledge of Ninject project and therefore cannot have any parameters injected. You would need a Ninject WebActivator extension (the same way you have a Ninject MVC extension) to achieve it. But frankly this is a bit of a catch-22: you want WebActivator to setup Ninject and at the same time Ninject to setup WebActivator.
I can think of 2 possible scenarios for you:
leave the code as it is - I honestly don't know why you don't like your WorkflowRunner class. It is a nice, small class, no other code has any dependency on it, You obtain your references through a DependencyResolver which abstracts you from Ninject itself, your workflow initialization is nicely encapsulated there. I do not smell anything wrong here, really.
Initialize your workflows in the other WebActivator class, where setup Ninject. You know there that your Ninject is initialized and you can still keep workflow initialization code in a separate class.
I would obviously choose 1. if I were you.
If you already have the Ninject bootstrapper working, are you sure you need another solution? For non-controller dependencies, I use a BindingFactory class which has a GetInstance() method. This just calls the Get() method on the Kernel object.
public class BindingFactory
{
private static readonly IKernel Kernel = new StandardKernel(new DefaultServices());
public static T GetInstance<T>()
{
return Kernel.Get<T>();
}
public static IController GetControllerInstance(Type controllerType)
{
return Kernel.Get(controllerType) as IController;
}
}
I then use a NinjectControllerFactory which utilises the BindingFactory.
public class NinjectControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext context, Type controllerType)
{
if (controllerType == null)
return null;
return BindingFactory.GetControllerInstance(controllerType);
}
}
So I'm thinking you could just adapt your current implementation.
I have a question for the Ioc gurus out there.
I am working with a co-worker to wrap our minds around Castle Windsor IoC. We are having a difference of opinion about static Domain Service objects within asp.Net webforms. We have a static factory called BLServiceFactory in our Infrastructure layer that retrieves the container.
public sealed class BLServiceFactory
{
private static BLServiceFactory _instance = new BLServiceFactory();
IWindsorContainer _container = new WindsorContainer();
public static BLServiceFactory Instance
{
get
{return _instance;}
}
public T Create<T>()
{
return (T)_container[typeof(T)];
}
private BLServiceFactory()
{
_container.AddComponent("DataContext", typeof(DAL.DataContextFactory), typeof(DAL.CPContextFactory));
_container.AddComponent("Repository", typeof(DAL.IRepository<>), typeof(DAL.Repository<>));
_container.AddComponent("UserManager", typeof(BL.IUserManager), typeof(BL.UserManager));
_container.AddComponent("RoleService", typeof(BL.IRoleService), typeof(BL.RoleService));
}
}
We are pulling instances from the factory in our code behinds like this.
public partial class PrintList : System.Web.UI.Page
{
private static readonly ISchoolManager _schoolService = BLServiceFactory.Instance.Create<ISchoolManager>();
Models.TechSchool _tech;
protected void Page_Load(object sender, EventArgs e)
{
_tech = _schoolService.GetSchoolForTechPrep(Profile.UserName);
}
protected void DoOtherStuff...
{
_schoolService.Foo(_tech);
}
}
To me this looks like we will be serving up the same instance to every session. That would indeed be bad! My co-worker thinks that since all of our Domain Services are marked Transient, each page request will get a new instance.
I have also read a bit about memory leaks due to objects marked transient not released for garbage collection. Has this been addressed in the latest release of Castle Windsor, or should I be explicitly releasing objects? Of course as it stands now, all the objects are static and this would be irrelevant.
The BLServiceFactory is a service locator. I recommend using CommonServiceLocator instead of your own if you're going to use a service locator. Component registration does not belong inside the service locator.
Now, in the code you posted, there is no mention of those components being transient, unless you marked them with the [Transient] attribute. If you didn't, those components will be singletons, which is the default lifestyle in Windsor.
Since the variable _schoolService in PrintList is static, the same instance of ISchoolManager will be used for all requests to the PrintList page. If you really want it to be transient, remove the "static" keyword.
About releasing components, see this article.
BTW: AddComponent-style registration is deprecated, use Register() instead.