问题是 Ninject 从来没有机会在 ActionFilter 上“做这件事”,这在 MVC 内部由FilterAttributeFilterProvider
. 您需要做的是告诉 MVC 使用自定义FilterAttributeFilterProvider
,您可以在过滤器执行之前拦截过滤器。请允许我演示一下:
假设我有这个接口和实现:
public interface IFoo
{
}
public class Foo : IFoo
{
}
然后我有一个 ActionFilter:
public class MyActionFilterAttribute : ActionFilterAttribute
{
public MyActionFilterAttribute()
{
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//do something with Foo
base.OnActionExecuting(filterContext);
}
[Inject]
public IFoo Foo { get; set; }
}
然后我们有一个控制器:
public class HomeController : Controller
{
[MyActionFilter]
public ActionResult Index()
{
return View();
}
}
如果您现在按原样运行它,显然 Foo 在 MyActionFilter 中仍然为空,所以让我们继续......
让我们设置 Ninject DependencyResolver:
public class NinjectDependencyResolver : IDependencyResolver
{
private readonly IKernel _kernel;
public NinjectDependencyResolver(IKernel kernel)
{
_kernel = kernel;
}
public object GetService(Type serviceType)
{
return _kernel.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return _kernel.GetAll(serviceType);
}
}
现在让我们在 Global.asax 中使用它:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
DependencyResolver.SetResolver(new NinjectDependencyResolver(GetKernel()));
}
private IKernel GetKernel()
{
var kernel = new StandardKernel();
kernel.Bind<IFoo>().To<Foo>();
return kernel;
}
越来越近了,但 MVC 在创建动作过滤器时仍然无法使用 Ninject 内核。这就是我们将实现这一目标的地方。
第一的:
public class NinjectFilterProvider : FilterAttributeFilterProvider
{
private readonly IKernel _kernel;
public NinjectFilterProvider(IKernel kernel)
{
_kernel = kernel;
}
public override IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
var filters = base.GetFilters(controllerContext, actionDescriptor);
foreach (var filter in filters)
{
_kernel.Inject(filter.Instance);
}
return filters;
}
}
这里发生的是我们正在创建一个自定义 FilterAttributeFilterProvider 类。在 OnActionExecuting 方法中,在我们通过基本实现获得所有过滤器之后,我们可以调用 Ninjects Inject 方法,该方法将检查实例并查看它是否可以向其中注入任何东西(使用 Inject 属性)。
最后一个难题是为我们的自定义 FilterAttributeFilterProvider 设置绑定:
全球.asax:
private IKernel GetKernel()
{
var kernel = new StandardKernel();
kernel.Bind<IFoo>().To<Foo>();
//use our custom NinjectFilterProvider
kernel.Bind<IFilterProvider>().To<NinjectFilterProvider>();
return kernel;
}
现在,当 MVC 获取一个IFilterProvider
(它通过 DependencyResolver 自动获取)时,它不会获取默认值FilterAttributeFilterProvider
,而是获取我们的自定义NinjectFilterProvider
,因此我们的 Foo 实例将填充到自定义操作过滤器中。