Joh*_*n H 7 asp.net-mvc dependency-injection ninject model-binding custom-model-binder
我正在使用NInject与NInject.Web.Mvc.
首先,我创建了一个简单的测试项目,我希望IPostRepository在同一Web请求期间在控制器和自定义模型绑定器之间共享一个实例.在我的真实项目中,我需要这个,因为我遇到IEntityChangeTracker问题,我有效地有两个存储库访问相同的对象图.因此,为了简化我的测试项目,我只是想共享一个虚拟存储库.
我遇到的问题是它适用于第一个请求,就是这样.相关代码如下.
NInjectModule:
public class PostRepositoryModule : NinjectModule
{
public override void Load()
{
this.Bind<IPostRepository>().To<PostRepository>().InRequestScope();
}
}
Run Code Online (Sandbox Code Playgroud)
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());
}
}
Run Code Online (Sandbox Code Playgroud)
Global.asax中:
protected override void OnApplicationStarted()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders.Add(typeof(string), kernel.Get<CustomModelBinder>());
}
Run Code Online (Sandbox Code Playgroud)
这样做实际上是创建了2个独立的实例IPostRepository而不是共享实例.这里有一些关于将依赖项注入模型绑定器的问题.我上面的代码是基于NInject.Web.Mvc wiki中描述的第一个设置方法,但我已经尝试了两个.
当我使用第二种方法时,IPostRepository只会为第一个Web请求共享,之后它将默认不共享实例.然而,当我确实工作时,我正在使用默认值,DependencyResolver因为我无法在生活中找出如何使用NInject做同样的事情(因为内核隐藏在NInjectMVC3类中).我这样做是这样的:
ModelBinders.Binders.Add(typeof(string),
DependencyResolver.Current.GetService<CustomModelBinder>());
Run Code Online (Sandbox Code Playgroud)
我怀疑它第一次运行的原因仅仅是因为它没有通过NInject解决它,所以生命周期实际上是由MVC直接处理的(虽然这意味着我不知道它是如何解决依赖性的).
那么我该如何正确注册我的模型绑定器并让NInject注入依赖?
实际上,让Ninject工厂扩展程序启动并运行起来非常简单,但现有答案对我来说并不清楚.
工厂扩展插件是先决条件,可以通过NUGet安装:
Install-Package Ninject.Extensions.Factory
Run Code Online (Sandbox Code Playgroud)
您只需将工厂注入模型绑定器的某个位置,例如:
private IPostRepositoryFactory _factory;
public CustomModelBinder(IPostRepositoryFactory factory) {
_factory = factory;
}
Run Code Online (Sandbox Code Playgroud)
然后为工厂创建一个界面.工厂的名称和方法的名称实际上并不重要,只是返回类型.(很高兴知道你是否想要注入一个NHibernate会话,但不想担心引用正确的命名空间ISessionFactory,也知道GetCurrentRepository在上下文中是否实际做得更清楚):
public interface IPostRepositoryFactory {
IPostRepository CreatePostRepository();
}
Run Code Online (Sandbox Code Playgroud)
然后,假设您IPostRepository已经被Ninject正确管理,扩展将通过调用.ToFactory()方法为您完成其他所有操作.
kernel.Bind<IPostRepository().To<PostRepository>();
kernel.Bind<IPostRepositoryFactory>().ToFactory();
Run Code Online (Sandbox Code Playgroud)
然后,您只需在您需要的代码中调用您的工厂方法:
var repo = _factory.CreatePostRepository();
repo.DoStuff();
Run Code Online (Sandbox Code Playgroud)
(更新:显然命名你的工厂的功能GetXXX.如果服务不已经在会话中实际上会失败,这样你就需要真的有点.你叫什么名字方法小心)
我最终按照建议与工厂解决了这个问题。然而,我只是不知道如何实现这一点,Ninject.Extensions.Factory这正是我所希望的。这是我最终得到的结果:
工厂界面:
public interface IPostRepositoryFactory
{
IPostRepository CreatePostRepository();
}
Run Code Online (Sandbox Code Playgroud)
工厂实施:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
工厂的 Ninject 模块:
public class PostRepositoryFactoryModule : NinjectModule
{
public override void Load()
{
this.Bind<IPostRepositoryFactory>().To<PostRepositoryFactory>();
}
}
Run Code Online (Sandbox Code Playgroud)
自定义模型活页夹:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
控制器:
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());
}
}
Run Code Online (Sandbox Code Playgroud)
Global.asax 连接自定义模型绑定器:
protected override void OnApplicationStarted()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders.Add(typeof(string), kernel.Get<CustomModelBinder>());
}
Run Code Online (Sandbox Code Playgroud)
在我看来,这给了我所需的输出:
Model binder
Action method
| 归档时间: |
|
| 查看次数: |
5286 次 |
| 最近记录: |