将依赖项注入到自定义模型绑定器中,并使用Ninject使用InRequestScope



我正在将NInject与NInject.Web.Mvc.一起使用

首先,我创建了一个简单的测试项目,在该项目中,我希望在同一个web请求期间,在控制器和自定义模型绑定器之间共享IPostRepository的实例。在我的实际项目中,我需要这个,因为我遇到了IEntityChangeTracker问题,我实际上有两个存储库访问同一个对象图。因此,为了使我的测试项目保持简单,我只是尝试共享一个虚拟存储库。

我遇到的问题是,它能处理第一个请求,仅此而已。相关代码如下。

NInjectModule:

public class PostRepositoryModule : NinjectModule
{
    public override void Load()
    {
        this.Bind<IPostRepository>().To<PostRepository>().InRequestScope();
    }
}

CustomModelBinder:

public class CustomModelBinder : DefaultModelBinder
{
    [Inject]
    public IPostRepository repository { get; set; }
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        repository.Add("Model binder...");
        return base.BindModel(controllerContext, bindingContext);
    }
}
public class HomeController : Controller
{
    private IPostRepository repository;
    public HomeController(IPostRepository repository)
    {
        this.repository = repository;
    }
    public ActionResult Index(string whatever)
    {
        repository.Add("Action...");
        return View(repository.GetList());
    }
}

Global.asax:

protected override void OnApplicationStarted()
{
    AreaRegistration.RegisterAllAreas();
    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
    ModelBinders.Binders.Add(typeof(string), kernel.Get<CustomModelBinder>());
}

这样做实际上是创建IPostRepository的两个独立实例,而不是共享实例。在向模型绑定器中注入依赖项方面,我缺少一些东西。我上面的代码是基于NInject.Web.Mvc wiki中描述的第一种设置方法,但我都尝试过。

当我使用第二种方法时,IPostRepository将仅针对第一个web请求共享,之后它将默认为不共享实例。然而,当我真正做到这一点时,我使用了默认的DependencyResolver,因为我一辈子都不知道如何使用NInject5(因为内核隐藏在NInjectMVC3类中)。我是这样做的:

ModelBinders.Binders.Add(typeof(string),
    DependencyResolver.Current.GetService<CustomModelBinder>());

我怀疑这第一次起作用的原因只是因为它没有通过NInject来解决它,所以生命周期实际上是由MVC直接处理的(尽管这意味着我不知道它是如何解决依赖关系的)。

那么,我该如何正确注册我的模型绑定器并让NInject注入依赖项呢?

MVC对多个请求重用ModelBinder。这意味着它们的生命周期比请求范围长,因此不允许依赖于请求范围生命周期较短的对象。

使用Factory为BindModel 的每次执行创建IPostRepository

实际上,启动并运行Ninject工厂扩展非常简单,但从现有的答案来看,我并不清楚这一点。

工厂扩展插件是一个先决条件,可以通过NUGet:安装

Install-Package Ninject.Extensions.Factory

你只需要把工厂注入你的模型活页夹的某个地方,例如:

private IPostRepositoryFactory _factory;
public CustomModelBinder(IPostRepositoryFactory factory) {
    _factory = factory;
}

然后为工厂创建一个接口。工厂的名称和方法的名称实际上根本不重要,只是返回类型。(很高兴知道你是否想注入一个NHibernate会话,但不想担心引用ISessionFactory的正确名称空间,也很高兴知道GetCurrentRepository是否在上下文中更清楚地表明了它的实际作用):

public interface IPostRepositoryFactory { 
    IPostRepository CreatePostRepository();
}

然后,假设你的IPostRepository已经被Ninject正确地管理了,那么这个扩展只需要调用.ToFactory()方法就可以为你做其他的事情。

kernel.Bind<IPostRepository().To<PostRepository>();
kernel.Bind<IPostRepositoryFactory>().ToFactory();

然后,您只需在需要的代码中调用工厂方法:

var repo = _factory.CreatePostRepository();
repo.DoStuff();

(更新:显然,如果会话中还不存在服务,那么将工厂函数命名为GetXXX实际上会失败。因此,您实际上必须在命名方法时稍微小心一点。)

我最终按照建议用一家工厂解决了这个问题。然而,我只是不知道如何使用Ninject.Extensions.Factory来实现这一点,这正是我想要的。以下是我最终得到的:

工厂接口:

public interface IPostRepositoryFactory
{
    IPostRepository CreatePostRepository();
}

工厂实施:

public class PostRepositoryFactory : IPostRepositoryFactory
{
    private readonly string key = "PostRepository";
    public IPostRepository CreatePostRepository()
    {
        IPostRepository repository;
        if (HttpContext.Current.Items[key] == null)
        {
            repository = new PostRepository();
            HttpContext.Current.Items.Add(key, repository);
        }
        else
        {
            repository = HttpContext.Current.Items[key] as PostRepository;
        }
        return repository;
    }
}

工厂的Ninject模块:

public class PostRepositoryFactoryModule : NinjectModule
{
    public override void Load()
    {
        this.Bind<IPostRepositoryFactory>().To<PostRepositoryFactory>();
    }
}

自定义模型绑定器:

public class CustomModelBinder : DefaultModelBinder
{
    private IPostRepositoryFactory factory;
    public CustomModelBinder(IPostRepositoryFactory factory)
    {
        this.factory = factory;
    }
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        IPostRepository repository = factory.CreatePostRepository();
        repository.Add("Model binder");
        return base.BindModel(controllerContext, bindingContext);
    }
}

控制器:

public class HomeController : Controller
{
    private IPostRepository repository;
    public HomeController(IPostRepositoryFactory factory)
    {
        this.repository = factory.CreatePostRepository();
    }
    public ActionResult Index(string whatever)
    {
        repository.Add("Action method");
        return View(repository.GetList());
    }
}

Global.asax连接自定义模型绑定器:

protected override void OnApplicationStarted()
{
    AreaRegistration.RegisterAllAreas();
    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
    ModelBinders.Binders.Add(typeof(string), kernel.Get<CustomModelBinder>());
}

在我看来,这给了我想要的输出:

Model binder
Action method

相关内容

  • 没有找到相关文章

最新更新