I'm writin开发者_StackOverflow中文版g a custom ErrorHandler attribute for my MVC project. I would like to inject an implementation of EventViewerLogger into that attribute.
I'm using Ninject 2.2 and it works fine for other features, such as injection repositories and aggregate services through controller constructors.
I understand that I can't inject an implementation of some class into attribute through constructor, therefore I have to inject it into the attribute's property.
Interface is below:
namespace Foo.WebUI.Infrastructure
{
public interface ILogger
{
void Log(Exception e);
}
}
Event viewer logger implementation
namespace Foo.WebUI.Infrastructure
{
/// <summary>
/// Logs exceptions into the Windows Event Viewer
/// </summary>
public class EventViewerLogger: ILogger
{
private EventViewerLogger _logger = null;
EventViewerLogger()
{
_logger = new EventViewerLogger();
}
public void Log(Exception e)
{
_logger.Log(e);
}
}
}
Below is code for error handler:
namespace Foo.WebUI.Handlers
{
/// <summary>
/// Custom error handler with an interface to log exceptions
/// </summary>
public class CustomHandleErrorAttribute: HandleErrorAttribute
{
[Inject]
public ILogger Logger { get; set; }
// Default constructor
public CustomHandleErrorAttribute():base() { }
public override void OnException(ExceptionContext filterContext)
{
Logger.Log(filterContext.Exception);
base.OnException(filterContext);
}
}
}
In global.asax I register the handler and Ninject.
protected void Application_Start()
{
IKernel kernel = new StandardKernel(new NinjectInfrastructureModule());
}
Finally, I have a custom filter provider
namespace Foo.WebUI.Infrastructure
{
public class NinjectFilterProvider: FilterAttributeFilterProvider
{
private readonly IKernel kernel;
public NinjectFilterProvider(IKernel kernel)
{
this.kernel = kernel;
}
public override IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
var filters = base.GetFilters(controllerContext, actionDescriptor);
// Iterate through all the filters and use Ninject kernel to serve concrete implementations
foreach (var filter in filters)
{
kernel.Inject(filter.Instance);
}
return filters;
}
}
}
When I start the application I get the following exception:
Activation path: 2) Injection of dependency ILogger into property Logger of type CustomHandleErrorAttribute 1) Request for CustomHandleErrorAttribute
Suggestions: 1) Ensure that the implementation type has a public constructor. 2) If you have implemented the Singleton pattern, use a binding with InSingletonScope() instead.
Source Error:
Line 27: foreach (var filter in filters)
Line 28: {
Line 29: kernel.Inject(filter.Instance);
Line 30: }
Spent a day on this, learnt a lot about dependecy injection which is great, but what am I doing wrong here?
Ninject.Web.Mvc has this functionality built in called "BindFilter" which lets you map an attribute (that takes some or no constructor args) to a filter (which has its constructor args injected). Additionally, you can use it to copy values from the attribute and inject them as constructor args to the filter if you want. It also lets you change scope on your filters to be per action or per controller etc so that they actually get re-instantiated (normal action filters don't get re-instantiated per request).
Here's an example of how I've used it to do a UoW action filter.
精彩评论