7

我将 NInject 与 NInject.Web.Mvc 一起使用。

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

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

NInject 模块:

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

自定义模型绑定器:

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());
    }
}

全球.asax:

protected override void OnApplicationStarted()
{
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    ModelBinders.Binders.Add(typeof(string), kernel.Get<CustomModelBinder>());
}

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

当我确实使用第二种方法时,IPostRepository只会为第一个 Web 请求共享,之后默认不共享实例。然而,当我真正开始工作时,我使用的是默认值DependencyResolver,因为我一生都无法弄清楚如何对 NInject 做同样的事情(因为内核隐藏在 NInjectMVC3 类中)。我是这样做的:

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

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

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

4

3 回答 3

8

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

改为使用工厂为 BindModel 的每次执行创建 IPostRepository

于 2012-02-08T02:13:56.893 回答
5

启动并运行 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(更新:如果会话中不存在服务,显然命名你的工厂函数实际上会失败。所以你实际上必须小心你命名方法的内容。)

于 2012-10-08T19:46:49.827 回答
4

我最终设法按照建议通过工厂解决了这个问题。但是,我只是不知道如何做到这一点,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

于 2012-02-10T17:03:35.920 回答