【问题标题】:Inject a dependency into a custom model binder and using InRequestScope using Ninject将依赖项注入自定义模型绑定器并使用 InRequestScope 使用 Ninject
【发布时间】:2012-02-29 11:44:24
【问题描述】:

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

全球.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 注入依赖项?

【问题讨论】:

标签: asp.net-mvc dependency-injection ninject model-binding custom-model-binder


【解决方案1】:

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

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

【讨论】:

  • 感谢您的回答。阅读您的文章后,我升级到 NInject 3.0.0 rc3 以使用工厂扩展。我设法解决了一些安装问题和调试符号问题,但我什至无法将依赖项注入我的控制器。我得到了标准的无参数构造函数异常。在运行了一些单元测试后,我确认模块加载正确,所以我不知道问题出在哪里。我希望你不介意,但我的代码和两个测试在这里:pastebin.com/wvNQBFLq
  • 我使用默认模板设置了一个新的 MVC 应用程序,添加了您的代码并且应用程序运行完美。您确定使用“Install-Package Ninject.MVC3 -pre”将所有内容更新到 Ninject 3.0.0 吗?
  • 嗨 Remo,非常感谢您帮我检查。我刚刚在这里重新创建了我的项目(明确确保软件包是使用 install-package Ninject.MVC3 -pre 安装的),现在我直接从 NInject 获得了一个异常:pastebin.com/nHJ3qqyM 你可以上传你拥有的项目吗让我看看?
  • 难道你是从 NinjectHttpApplication 派生并同时使用 App_Start/NinjectWebCommon.cs 吗?
  • 呸,就是这样。我忘记了它会在您安装软件包时创建文件夹。对于那个很抱歉。现在可以了,但我似乎仍然无法共享我的存储库实例。我再次阅读了您的文章,并注意到了一些事情。这部分特别是:“这告诉 Ninject 它应该为指定的接口自动实现一个工厂类,并将这个工厂的一个新实例注入到每个请求工厂接口实例的对象中。”这意味着我仍然不知道如何在活页夹和控制器之间共享存储库。
【解决方案2】:

启动并运行 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 实际上会失败。因此,您实际上必须有点小心你命名的方法。)

【讨论】:

  • 感谢您发布此信息。尽管该项目现已完成并已部署,但我非常感谢您添加此答案。这是我以后会参考的东西。
【解决方案3】:

我最终设法按照建议通过工厂解决了这个问题。但是,我只是不知道如何使用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-01-02
    • 1970-01-01
    • 2011-04-06
    • 2012-06-06
    • 2017-01-31
    • 2015-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多