MVC项目中ViewModel的Unity依赖注入失效问题求助
哈哈,这个问题我太熟了!当年MVC刚流行的时候,好多人都踩过这个Postback时ViewModel DI失效的坑——毕竟MVC默认的模型绑定逻辑根本不会管你的DI容器,只会硬找无参构造函数。你说的用Service Locator+Unity解决的思路完全正确,我帮你回忆具体操作步骤:
MVC在处理Postback时,是通过ModelBinder来实例化ViewModel的,默认的DefaultModelBinder只会尝试调用无参构造函数。我们要做的就是替换这个默认行为,让ModelBinder通过Unity容器(也就是把Unity当作Service Locator)来创建ViewModel实例,这样依赖注入就能生效了。
1. 确保Unity容器已正确注册ViewModel及其依赖
首先确认你的Unity容器里已经注册了ViewModel和它需要的所有服务,比如:
// 在Unity配置类或者Global.asax里 var container = new UnityContainer(); // 注册ViewModel的依赖服务 container.RegisterType<IMyService, MyConcreteService>(); // 注册ViewModel本身 container.RegisterType<MyViewModel>();
2. 创建自定义UnityModelBinder
写一个继承自DefaultModelBinder的自定义绑定器,重写CreateModel方法,让它用Unity容器解析ViewModel:
public class UnityModelBinder : DefaultModelBinder { private readonly IUnityContainer _container; // 构造函数注入Unity容器 public UnityModelBinder(IUnityContainer container) { _container = container; } protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) { // 优先用Unity解析ViewModel实例 try { var model = _container.Resolve(modelType); if (model != null) { // 更新模型元数据,确保后续绑定逻辑正常工作 bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, modelType); return model; } } catch (ResolutionFailedException) { // 如果Unity解析失败(比如没注册),回退到默认逻辑 } return base.CreateModel(controllerContext, bindingContext, modelType); } }
3. 注册自定义ModelBinder到MVC框架
在项目启动时(比如Global.asax的Application_Start方法,或者MVC 5+的Startup类),把默认的ModelBinder替换成我们的自定义实现:
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); // 初始化Unity容器 var container = new UnityContainer(); UnityConfig.RegisterComponents(container); // 替换默认ModelBinder ModelBinders.Binders.DefaultBinder = new UnityModelBinder(container); }
可选:只为特定ViewModel使用该Binder
如果你不想全局替换ModelBinder,也可以给单个ViewModel添加特性标注,指定使用我们的UnityModelBinder:
[ModelBinder(typeof(UnityModelBinder))] public class MyViewModel { private readonly IMyService _myService; // 带依赖的构造函数,没有无参构造 public MyViewModel(IMyService myService) { _myService = myService; } // 其他视图绑定属性... public string UserInput { get; set; } }
这个方案本质上就是把Unity当作Service Locator来用——让ModelBinder在创建ViewModel时,不再自己实例化,而是向Unity容器“请求”一个已经注入好依赖的实例。这样Postback时,MVC就能拿到带有正确依赖的ViewModel,而不会因为找不到无参构造函数报错了。
内容的提问来源于stack exchange,提问作者Nico Haegens

