I have a custom WebViewPage
, and I would like SimpleInjector to resolve some dependencies in it. It seems like I can't use constructor injection due to the way MVC instantiates this class.
I've enabled explicity property injection as described in the SimpleInjector documentation. But the properties are not getting injected - they're always null
.
custom WebViewPage class:
public abstract class CustomWebViewPage<T> : WebViewPage<T>
{
[Import]
public IMyDependency Dependency { get; set; }
public void UseDependency()
{
Dependency.DoStuff(); // Dependency is null
}
}
IOC setup:
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
container.Options.PropertySelectionBehavior = new ImportPropertySelectionBehavior();
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.Register<IMyDependency, MyDependency>();
Do I need to wire anything else up to get this working? I've tried registering the custom WebViewPage with SimpleInjector (mapping WebViewPage<>
to CustomWebViewPage<>
and making my class non-abstract), but that didn't work either.
Just to be clear, all other property and constructor dependency injection is working in the application - just not for this particular class.
I agree with Remo Gloor's answer and the statement from Stefan:
But as the others said you shouldn't do view injection anyway. Your view should be dumb and just render the view model to HTML. Anything requiring a dependency should be done in the controller or a service.
That said, in case you really, really, really need this (which you shouldn't), I think it's a matter of creating a custom IViewEngine
.
WARNING: The code below is NOT!!! tested AT ALL*
internal class PropertyInjectionViewEngineDecorator : IViewEngine
{
private readonly ConcurrentDictionary<Type, Registration> registrations = new ConcurrentDictionary<Type, Registration>();
private readonly IViewEngine decoratee;
private readonly Func<Type, Registration> createRegistration;
public PropertyInjectionViewEngineDecorator(IViewEngine decoratee, Container container)
{
this.decoratee = decoratee;
this.createRegistration = type => Lifestyle.Transient.CreateRegistration(type, container);
}
public ViewEngineResult FindPartialView(ControllerContext context, string partialViewName, bool useCache) =>
this.InitializeView(this.decoratee.FindPartialView(context, partialViewName, useCache));
public ViewEngineResult FindView(ControllerContext context, string viewName, string masterName, bool useCache) =>
this.InitializeView(this.decoratee.FindView(context, viewName, masterName, useCache));
public void ReleaseView(ControllerContext controllerContext, IView view) =>
this.decoratee.ReleaseView(controllerContext, view);
private ViewEngineResult InitializeView(ViewEngineResult result)
{
if (result.View != null)
{
Registration registration = this.registrations.GetOrAdd(result.View.GetType(), this.createRegistration);
registration.InitializeInstance(result.View);
}
return result;
}
}
You can hook this custom PropertyInjectionViewEngineDecorator
into the MVC pipeline as follows:
var razor = ViewEngines.Engines.Single(e => e.GetType() == typeof(RazorViewEngine));
ViewEngines.Engines.Remove(razor);
ViewEngines.Engines.Add(new PropertyInjectionViewEngineDecorator(razor, container));