我正在将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