用Ninject在MVC 3中注入一个属性



我正试图将一个名为UnitOfWorkAttribute的属性注入到我的ActionFilter中。我有这个代码:

[Inject]
public IUnitOfWork UnitOfWork { get; set; }

在执行之前,我告诉Ninject用解决这个问题

Bind<IUnitOfWork>().To<NHibernateUnitOfWork>().InThreadScope();

我的问题是,在我的UnitOfWorkAttribute类中,每当我尝试使用UnitOfWork属性时,它都会显示为Null。这是我的界面:

public interface IUnitOfWork : IDisposable
{
    void Begin();
    void Commit();
    void Rollback();
}

这是我的混凝土:

public interface INHibernateUnitOfWork : IUnitOfWork
{
    ISession Session { get; }
}
public class NHibernateUnitOfWork : INHibernateUnitOfWork
{
    private readonly ISessionSource sessionSource;
    private ITransaction transaction;
    private ISession session;
    private bool disposed;
    private bool begun;
    public NHibernateUnitOfWork(ISessionSource sessionSource)
    {
        this.sessionSource = sessionSource;
        Begin();
    }
    //.......
}

我正在完成//……下的接口。。。。。。

我在这里做错了什么?

问题是Ninject从来没有机会在ActionFilter上"做它自己的事",而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中显然仍然为null,所以让我们继续。。。。

让我们设置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在创建Action Filters时仍然没有办法使用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:设置绑定

Global.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实例将填充在自定义Action Filter中。

最新更新