将依赖项注入自定义模型绑定器并使用 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>());
}
这样做实际上是创建 2 个独立的 IPostRepository
实例,而不是共享实例。在将依赖项注入模型绑定程序时,我缺少一些东西。我上面的代码基于 NInject.Web.Mvc wiki 但我都尝试过。
当我使用第二种方法时,IPostRepository
将仅在第一个 Web 请求时共享,此后它将默认不共享实例。然而,当我真正开始工作时,我使用的是默认的 DependencyResolver,因为我一生都无法弄清楚如何使用 NInject 做同样的事情(因为内核隐藏在NInjectMVC3 类)。我这样做是这样的:
ModelBinders.Binders.Add(typeof(string),
DependencyResolver.Current.GetService<CustomModelBinder>());
我怀疑这第一次起作用的原因是因为这不是通过 NInject 解决它,所以生命周期实际上是由 MVC 直接处理的(尽管这意味着我不知道它是如何解决依赖关系的) )。
那么我该如何正确注册我的模型绑定器并让 NInject 注入依赖项呢?
I'm using NInject with NInject.Web.Mvc.
To start with, I've created a simple test project in which I want an instance of IPostRepository
to be shared between a controller and a custom model binder during the same web request. In my real project, I need this because I'm getting IEntityChangeTracker
problems where I effectively have two repositories accessing the same object graph. So to keep my test project simple, I'm just trying to share a dummy repository.
The problem I'm having is that it works on the first request and that's it. The relevant code is below.
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>());
}
Doing it this way is actually creating 2 separate instances of IPostRepository
rather than the shared instance. There's something here that I'm missing with regards to injecting a dependency into my model binder. My code above is based on the first setup method described in the NInject.Web.Mvc wiki but I have tried both.
When I did use the second method, IPostRepository
would be shared only for the very first web request, after which it would default to not sharing the instance. However, when I did get that working, I was using the default DependencyResolver
as I couldn't for the life of me figure out how to do the same with NInject (being as the kernel is tucked away in the NInjectMVC3 class). I did that like so:
ModelBinders.Binders.Add(typeof(string),
DependencyResolver.Current.GetService<CustomModelBinder>());
I suspect the reason this worked the first time only is because this isn't resolving it via NInject, so the lifecycle is really being handled by MVC directly (although that means I have no idea how it's resolving the dependency).
So how do I go about properly registering my model binder and getting NInject to inject the dependency?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
MVC 对多个请求重用 ModelBinder。这意味着它们的生命周期比请求范围更长,因此不允许依赖于请求范围生命周期较短的对象。
使用 Factory 来为 BindModel 的每次执行创建 IPostRepository
The ModelBinders are reused by MVC for multiple requests. This means they have a longer lifecycle than request scope and therefore aren't allowed to depend on objects with the shorter request scope life cycle.
Use a Factory instead to create the IPostRepository for every execution of BindModel
实际上,启动并运行 Ninject 工厂扩展非常简单,但从现有答案中我并不清楚这一点。
工厂扩展插件是先决条件,可以通过 NUGet 安装:
您只需将工厂注入到模型绑定程序中的某个位置,例如:
然后为工厂创建一个接口。工厂的名称和方法的名称实际上根本不重要,只是返回类型。 (很高兴知道您是否想注入 NHibernate 会话,但又不想担心为
ISessionFactory
引用正确的命名空间,了解GetCurrentRepository
是否使它实际上做了什么,在上下文中更清楚):然后,假设您的
IPostRepository
已经由 Ninject 正确管理,则该扩展只需调用.ToFactory() 方法。然后,您只需在需要的代码中调用您的工厂方法:(
更新:显然,如果会话中尚不存在该服务,则命名您的工厂函数
GetXXX
实际上会失败。所以您实际上这样做必须稍微小心地命名该方法。)It's actually really simple to get the Ninject factory extension up and running, but that wasn't clear to me from the existing answers.
The factory extensions plugin is a prerequisite, which can be installed via NUGet:
You just need the factory injected into your model binder somewhere, eg:
Then create an interface for the factory. The name of the factory and the name of the method doesn't actually matter at all, just the return type. (Good to know if you want to inject an NHibernate session but don't want to have to worry about referencing the correct namespace for
ISessionFactory
, also useful to know ifGetCurrentRepository
makes what it actually does more clear in context):Then, assuming your
IPostRepository
is already being managed by Ninject correctly, the extension will do everything else for you just by calling the .ToFactory() method.Then you just call your factory method in the code where you need it:
(Update: Apparently naming your factory function
GetXXX
will actually fail if the service doesn't already exist in the session. So you do actually have to be somewhat careful with what you name the method.)我最终按照建议与工厂解决了这个问题。但是,我只是不知道如何使用 Ninject.Extensions.Factory 来完成此操作,这是我更喜欢的。这是我最终得到的结果:
工厂接口:
工厂实现:
工厂的 Ninject 模块:
自定义模型绑定器:
控制器:
连接自定义模型绑定器的 Global.asax:
在我看来,这给了我所需输出:
模型绑定器
操作方法
I eventually managed to solve it with a factory as suggested. However, I just could not figure out how to accomplish this with
Ninject.Extensions.Factory
which is what I would've preferred. Here is what I ended up with:The factory interface:
The factory implementation:
The Ninject module for the factory:
The custom model binder:
The controller:
Global.asax to wire up the custom model binder:
Which in my view, gave me the desired output of:
Model binder
Action method