I am still a bit new to using IOC containers and I'm struggling a bit. I am using ASP.NET MVC 5.2 with Ninject.MVC3. I have an exception filter that basically hands off to a log service:
public class ExceptionLoggerFilter : IExceptionFilter
{
private readonly ILogService _logService;
public ExceptionLoggerFilter(ILogService logService) {
_logService = logService;
}
public void OnException(ExceptionContext filterContext) {
_logService.LogError(filterContext.Exception);
}
}
I would like to use dependency injection to get the instance of the log service. However, the RegisterGlobalFilters method is static:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new ExceptionLoggerFilter(???));
}
}
I could use a static constructor for FilterConfig, but this just doesn't feel like a good solution.
I tried using property injection in the ExceptionLoggerFilter class, but it just didn't work (LogService was always null):
[Inject]
public ILogService LogService { get; set; }
I have read up on using the Factory pattern, but it doesn't seem to help here as I would still need to inject the Factory class into the static constructor of the FilterConfig class. I could be wrong, but this doesn't feel like the right solution either.
For now, I'm using a just-give-me-an-instance method:
var logService = (ILogService)DependencyResolver.Current.GetService(typeof(ILogService));
filters.Add(new ExceptionLoggerFilter(logService));
This works, but is it a good solution? Is there a better way? Is my thinking about dependency injection and IOC containers all messed up?
ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies. For more information specific to dependency injection within MVC controllers, see Dependency injection into controllers in ASP.NET Core.
A view component class: Supports constructor dependency injection. Doesn't take part in the controller lifecycle, therefore filters can't be used in a view component.
Service injection A service can be injected into a view using the @inject directive. You can think of @inject as adding a property to the view, and populating the property using DI. CSHTML Copy.
MVC global filters are just a list of instances. This means that any dependencies such filter has, also become singletons. This means you should be very careful with this approach, because it is really easy to accidentally cause a Captive Dependency in your application. Captive Dependencies are often hard to track and often only popup in testing or production.
Instead, you should create a proxy class that can delegate back to your container/kernel at the time the filter is used, so it can resolve the real filter on the spot. This prevents Captive Dependencies.
Such proxy can look as follows:
public class NinjectExceptionFilterProxy<TExceptionFilter> : IExceptionFilter
where TExceptionFilter : IExceptionFilter
{
private readonly Kernel _kernel;
public ExceptionLoggerFilter(Kernel kernel) {
_kernel = kernel;
}
public void OnException(ExceptionContext filterContext) {
var filter = _kernel.Get<TExceptionFilter>();
filter.OnException(filterContext);
}
}
You can register the proxy filter as follows:
public static void RegisterGlobalFilters(GlobalFilterCollection filters, IKernel kernel)
{
filters.Add(new NinjectExceptionFilterProxy<ExceptionLoggerFilter>(kernel));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With