I'm using a service from my OnApplicationStarted inside my Global.ascx.cs file. Is there a way to dependency inject the repository from there?
My code:
public class MvcApplication : NinjectHttpApplication
{
//Need to dependency inject this.
private IBootStrapService bootService;
protected override void OnApplicationStarted()
{
//Used to set data such as user roles in database on a new app start.
bootService.InitDatabase();
base.OnApplicationStarted();
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
internal class SiteModule : NinjectModule
{
public override void Load()
{
//I set my bindings here.
Bind<IBootStrapService>().To<BootStrapService>();
Bind<IUserRepository>().To<SqlServerUserRepository>()
.WithConstructorArgument("connectionStringName", "MyDb");
}
}
}
So how do I get ninject to do DI right inside the app start? As you can see, I setup my bindings in the SiteModule class.
You could override the CreateKernel method where you would register your modules:
protected override IKernel CreateKernel()
{
return new StandardKernel(
new INinjectModule[]
{
new SiteModule()
}
);
}
This will not automatically inject the bootService field though. You could instantiate it like this:
protected override void OnApplicationStarted()
{
base.OnApplicationStarted();
//Used to set data such as user roles in database on a new app start.
var bootService = Kernel.Get<IBootStrapService>();
bootService.InitDatabase();
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
Related
Umbraco v7.5.8
I have bunch of problems with DI setup (shown below).
1) Neither OnApplicationInitialized, nor OnApplicationStarted (and other) events firing if constructor takes parameter(s).
2) Backoffice is broken. It's not possible to access a content node. Exception message is:
An error occurred when trying to create a controller of type 'ContentController'. Make sure that the controller has a parameterless public constructor.
// Application handlers
public class UmbracoApplicationEventHandler : IApplicationEventHandler
{
private IMenuManager _menuManager;
public UmbracoApplicationEventHandler(IMenuManager menuManager)
{
_menuManager = menuManager;
}
public void OnApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
ContentService.Saving += UpdateMenu;
}
private void UpdateMenu(IContentService sender, SaveEventArgs<IContent> saveEventArgs)
{
_menuManager.UpdateMenu();
}
}
// Unity config:
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<IMenuManager, MenuManager>();
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}
// Owin Startup:
public class UmbracoStandardOwinStartup : UmbracoDefaultOwinStartup
{
public override void Configuration(IAppBuilder app)
{
//ensure the default options are configured
base.Configuration(app);
UnityConfig.RegisterComponents();
}
}
Please read: https://our.umbraco.org/documentation/reference/using-ioc.
You need to register and build your container on OnApplicationStarted event, not earlier if you want to make it work with Umbraco.
I want to implement additional global filters in a Sitecore 8 MVC application. It seems like I need to override the default Sitecore InitializeGlobalFilters pipeline. But the lack of documentation makes me nervous. Am I going to break something by overriding this?
Solved by overriding Sitecore's pipeline.
First, created new pipeline:
[UsedImplicitly]
public class InitializeGlobalFilters
{
public virtual void Process(PipelineArgs args)
{
this.AddGlobalFilters(args);
}
protected virtual void AddGlobalFilters(PipelineArgs args)
{
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
GlobalFilters.Filters.Add(this.CreateRequestFilter());
}
protected virtual object CreateRequestFilter()
{
return (object)new PipelineBasedRequestFilter();
}
}
Then add patch config that inserted this in the correct location in the pipeline (mimicing where Sitecore had it:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<initialize>
<processor type="Sitecore.Mvc.Pipelines.Loader.InitializeGlobalFilters, Sitecore.Mvc">
<patch:attribute name="type">DD.Platform.Sc.Pipelines.MvcPipelines.InitializeGlobalFilters, DD.Platform.Sc</patch:attribute>
</processor>
</initialize>
</pipelines>
</sitecore>
</configuration>
We used in a one project filters.
In the global.asax we have:
protected void Application_Start()
{
...
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
...
}
In the App_Start folder we have FilterConfig class
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
}
We have also a filter class
public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
....
base.OnActionExecuting(filterContext);
}
}
and everything works fine, we didn't ovveride InitializeGlobalFilters pipeline.
For the record, I went back to the Sitecore.Mvc.dll and decompiled it so I could see what the previously registered Sitecore.Mvc.Pipelines.Loader.InitialzeGlobalFilters.cs class was doing. Here it is. I am going to go ahead in my implementation, and configure both of them, rather safe than sorry. I also decompiled the PipelineBasedRequestFilter.cs which also looked like logic i'd rather leave in place.
// Decompiled with JetBrains decompiler
// Type: Sitecore.Mvc.Pipelines.Loader.InitializeGlobalFilters
// Assembly: Sitecore.Mvc, Version=1.1.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 4DE79547-0FD4-45A0-BD18-FFEF21AA18FC
// Assembly location: C:\P2\packages\Sitecore.Mvc.NoReferences.8.2.180406\lib\NET452\Sitecore.Mvc.dll
using Sitecore.Mvc.Filters;
using Sitecore.Pipelines;
using System.Web.Mvc;
namespace Sitecore.Mvc.Pipelines.Loader
{
[UsedImplicitly]
public class InitializeGlobalFilters
{
public virtual void Process(PipelineArgs args)
{
this.AddGlobalFilters(args);
}
protected virtual void AddGlobalFilters(PipelineArgs args)
{
GlobalFilters.Filters.Add(this.CreateRequestFilter());
}
protected virtual object CreateRequestFilter()
{
return (object) new PipelineBasedRequestFilter();
}
}
}
PipelineBasedRequestFilter.cs
// Decompiled with JetBrains decompiler
// Type: Sitecore.Mvc.Filters.PipelineBasedRequestFilter
// Assembly: Sitecore.Mvc, Version=1.1.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 4DE79547-0FD4-45A0-BD18-FFEF21AA18FC
// Assembly location: C:\P2\packages\Sitecore.Mvc.NoReferences.8.2.180406\lib\NET452\Sitecore.Mvc.dll
using Sitecore.Diagnostics;
using Sitecore.Mvc.Diagnostics;
using Sitecore.Mvc.Pipelines;
using Sitecore.Mvc.Pipelines.MvcEvents.ActionExecuted;
using Sitecore.Mvc.Pipelines.MvcEvents.ActionExecuting;
using Sitecore.Mvc.Pipelines.MvcEvents.Exception;
using Sitecore.Mvc.Pipelines.MvcEvents.ResultExecuted;
using Sitecore.Mvc.Pipelines.MvcEvents.ResultExecuting;
using System.Web.Mvc;
namespace Sitecore.Mvc.Filters
{
public class PipelineBasedRequestFilter : IActionFilter, IResultFilter, IExceptionFilter
{
public virtual void OnActionExecuted(ActionExecutedContext actionExecutedContext)
{
Assert.ArgumentNotNull((object) actionExecutedContext, nameof (actionExecutedContext));
using (TraceBlock.Start("Action Executed"))
PipelineService.Get().RunPipeline<ActionExecutedArgs>("mvc.actionExecuted", new ActionExecutedArgs(actionExecutedContext));
}
public virtual void OnActionExecuting(ActionExecutingContext actionExecutingContext)
{
Assert.ArgumentNotNull((object) actionExecutingContext, nameof (actionExecutingContext));
using (TraceBlock.Start("Action Executing"))
PipelineService.Get().RunPipeline<ActionExecutingArgs>("mvc.actionExecuting", new ActionExecutingArgs(actionExecutingContext));
}
public virtual void OnException(ExceptionContext exceptionContext)
{
Assert.ArgumentNotNull((object) exceptionContext, nameof (exceptionContext));
using (TraceBlock.Start("Exception event"))
PipelineService.Get().RunPipeline<ExceptionArgs>("mvc.exception", new ExceptionArgs(exceptionContext));
}
public virtual void OnResultExecuted(ResultExecutedContext resultExecutedContext)
{
Assert.ArgumentNotNull((object) resultExecutedContext, nameof (resultExecutedContext));
using (TraceBlock.Start("Result Executed event"))
PipelineService.Get().RunPipeline<ResultExecutedArgs>("mvc.resultExecuted", new ResultExecutedArgs(resultExecutedContext));
}
public virtual void OnResultExecuting(ResultExecutingContext resultExecutingContext)
{
Assert.ArgumentNotNull((object) resultExecutingContext, nameof (resultExecutingContext));
using (TraceBlock.Start("Result Executing event"))
PipelineService.Get().RunPipeline<ResultExecutingArgs>("mvc.resultExecuting", new ResultExecutingArgs(resultExecutingContext));
}
}
}
Edit:
Oh and one more thing, coincidentally I figured out what the PipelineBasedRequestFilter.cs is used for! Turns out it looks like it's being used for some of the profiling in the Sitecore Debugger!
http://www.glass.lu/Blog/Archive/Using%20the%20Sitecore%20Debug%20Tool%20Part%201
I was surprised to find that at least one of my objects created by Ninject is not disposed of at the end of the request, when it has been defined to be InRequestScope
Here's the object I'm trying to dispose:
Interface:
public interface IDataContext : IDisposable
{
MessengerEntities context { get; set; }
}
MessengerEntities is Entity Framework's implementation of ObjectContext -- my context object.
Then I create a concrete class like so:
public class DataContext : IDataContext
{
private MessengerEntities _context = new MessengerEntities();
public MessengerEntities context
{
get
{
return _context;
}
set
{
_context = value;
}
}
#region IDisposable Members
public void Dispose()
{
context.Dispose();
}
#endregion
}
And then I have a Ninject controller factory like so (this is modeled on the Steve Sanderson MVC 2 book):
public class NinjectControllerFactory : DefaultControllerFactory
{
// a Ninject "kernel" is the thing that can supply object instances
private IKernel kernel = new StandardKernel(new MessengerServices());
// ASP.NET MVC calls this to get the controller for each request
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
return null;
return (IController)kernel.Get(controllerType);
}
private class MessengerServices : NinjectModule
{
public override void Load()
{
Bind<IDataContext>().To<DataContext>().InRequestScope();
Bind<IArchivesRepository>().To<ArchivesRepository>().InRequestScope();
Bind<IMessagesRepository>().To<MessagesRepository>().InRequestScope();
}
}
}
Now, when I put a breakpoint at the call to context.Dispose() in the DataContext object and run the debugger, that code never gets executed.
So, the evidence suggests that Ninject does not dispose of objects when they go out of scope, but simply creates new objects and relies on the garbage collector to get rid of them at a time of its choosing.
My question is: should I be concerned about this? Because I am -- I would think Ninject would dispose of any object that implements IDisposable.
UPDATE: I downloaded the Ninject Mvc extensions (for MVC 3) and this is now how I'm doing the MvcApplication and the binding, and it does seem to be disposing of my context object.
In global.asax:
public class MvcApplication : NinjectHttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected override Ninject.IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());
return kernel;
}
protected override void OnApplicationStarted()
{
base.OnApplicationStarted();
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}
and
public class EFBindingModule : NinjectModule
{
public override void Load()
{
Bind<IDataContext>().To<DataContext>().InRequestScope();
Bind<IArchivesRepository>().To<ArchivesRepository>().InRequestScope();
Bind<IMessagesRepository>().To<MessagesRepository>().InRequestScope();
}
}
Everything else remains the same.
Ninject will dispose your objects as soon as the request object is collected by the GC. But normally this takes some time. But there is a way to force early disposal after the request ended. The best way is to use Ninject.Web.MVC http://www.planetgeek.ch/2010/11/13/official-ninject-mvc-extension-gets-support-for-mvc3/ instead of implementing your own ControllerFactory. The other way is to configure your application to use the OnePerRequestModule.
I'm trying to learn a bit about MVC and have come across a problem when using Ninject. I want to bind repositories but keep getting the 'Object reference not set to an instance of an object' error.
I have created my NinjectControllerFactory:
public class NinjectControllerFactory : DefaultControllerFactory
{
// A Ninject "kernel" is the thing that can supply object instances
private IKernel kernel = new StandardKernel(new SportsShopServices());
// ASP .NET MVC calls this to get the controller for each request
protected override IController GetControllerInstance(RequestContext context, Type controllerType)
{
if (controllerType == null)
return null;
return (IController) kernel.Get(controllerType);
}
// Configure how abstract sevice types are mapped to concrete implementations
private class SportsShopServices : NinjectModule
{
public override void Load()
{
Bind<IProductRepository>().To<SqlProductsRepository>()
.WithConstructorArgument("connectionString",
ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString);
}
}
}
and my controller :
public class ProductsController : Controller
{
private IProductRepository productsRepository;
// Constructor used with Ninject
public ProductsController(IProductRepository _productsRepository)
{
this.productsRepository = _productsRepository;
}
public ViewResult List()
{
return View(productsRepository.Products.ToList());
}
}
I have modified the Web.config file to provide the db connection string and the Global.asax file Application_Start() method to include:
ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
I am working on an example from the PRO ASP .NET MVC 2 book but just can't get this work, been trying all day.
If you just want out-out-the-box ninject functionality, you are doing too much by creating your own controller factory.
all you need is the following in global.asax
public class MvcApplication : NinjectHttpApplication
{
protected override IKernel CreateKernel()
{
var modules = new INinjectModule[]
{
new ServiceModule()
};
return new StandardKernel(modules);
}
protected override void OnApplicationStarted()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
RegisterAllControllersIn(Assembly.GetExecutingAssembly());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
internal class ServiceModule : NinjectModule
{
public override void Load()
{
// controllers
this.Bind<Controllers.AccountController>().ToSelf();
this.Bind<Controllers.HomeController>().ToSelf();
// Repository
Bind<Controllers.IFormsAuthentication>().To<Controllers.FormsAuthenticationService>();
Bind<Controllers.IMembershipService>().To<Controllers.AccountMembershipService>();
}
}
}
I've seen a lot of different ways of configuring Ninject with ASP.NET MVC, but the implementation seems to change slightly with each release of the MVC framework. I'm trying to inject a RavenDB session into my repository. Here is what I have that's almost working.
public class MvcApplication : NinjectHttpApplication
{
...
protected override void OnApplicationStarted()
{
base.OnApplicationStarted();
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
protected override IKernel CreateKernel()
{
return new StandardKernel(new MyNinjectModule());
}
public static IDocumentSession CurrentSession
{
get { return (IDocumentSession)HttpContext.Current.Items[RavenSessionKey]; }
}
...
}
public class MyNinjectModule : NinjectModule
{
public override void Load()
{
Bind<IUserRepository>().To<UserRepository>();
Bind<IDocumentSession>().ToConstant(MvcApplication.CurrentSession);
}
}
When it tries to resolve IDocumentSession, I get the following error.
Error activating IDocumentSession using binding from IDocumentSession to constant value
Provider returned null.
Activation path:
3) Injection of dependency IDocumentSession into parameter documentSession of constructor of type UserRepository
Any ideas on how to make the IDocumentSession resolve?
ToConstant(MvcApplication.CurrentSession) is evaluated at application start. What you want is delayed evaluation ToMethod(ctx => MvcApplication.CurrentSession)