背景:在我的MVC post-back操作方法中,我接收的是命令对象,而不是视图模型。其想法是,这些命令对象(大致相当于事务脚本)将被设置并准备在进入动作方法时执行,模型绑定器具有在执行过程中使用的设置参数:
public class MyCommand : IMyCommand
{
// In this case Value1 and Value2 are being set by the default model binder from posted form values - wonderful :)
public String Value1 { get; set; }
public String Value2 { get; set; }
public CommandResult ExecuteCommand()
{
// Does something awesome...
}
}
为了使事情稍微复杂一点,我的命令对象具有依赖项(服务、存储库等),这些依赖项在它们各自的构造函数中是必需的;所以我必须创建一个自定义模型绑定器,它使用默认的DependencyResolver(已经用我的IoC容器设置好了)来构建模型对象:
public class DependencyModelBinder : DefaultModelBinder
{
protected override Object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
return DependencyResolver.Current.GetService(modelType);
}
}
并在Global.asax.cs
中设置如下:
ModelBinders.Binders.DefaultBinder = new DependencyModelBinder();
同样,这一切都很好,依赖项被注入构造函数,然后默认的模型绑定器接管,像往常一样设置属性。
问题:我遇到的问题是,我的所有命令对象都有一个"SessionId"GUID参数(来自cookie),他们做的第一件事就是尝试使用注入的服务从该id解析会话对象。
public class MyCommand : IMyCommand
{
public MyCommand (ISessionRepository sessionRepository) { ... }
public Guid SessionId { get; set; } // Set by model binder from a cookie...
public CommandResult Execute()
{
Session session = SessionRepository.Get(SessionId);
if (session == null)
// Do something not so awesome...
}
}
我想删除这种重复,所以我创建了第二个模型绑定器,它将负责在存储库中进行查找,这意味着我的命令对象可以直接具有Session
属性(删除会话存储库的构造函数依赖项)。
public class SessionModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var sessionRepository = DependencyResolver.Current.GetService<ISessionRepository>();
return sessionRepository.Get((Guid)controllerContext.HttpContext.Request["SessionId"]);
}
}
我的Global.asax.cs
文件现在看起来是这样的:
ModelBinders.Binders.DefaultBinder = new DependencyModelBinder();
ModelBinders.Binders.Add(typeof(Session), new SessionModelBinder());
在单独测试了SessionModelBinder之后,我知道它是有效的。但是,当将它与DependencyModelBinder结合使用时,永远不会调用它。如何让MVC在构建模型对象时使用我的DependencyModelBinder,但在绑定它们的会话属性时让它使用我的SessionModelBinder?或者有人知道更好的方法吗?
您可以在原始模型绑定器中使用GetPropertyValue方法为Session属性提供一个值:
public class DependencyModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
return DependencyResolver.Current.GetService(modelType);
}
protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
{
if (propertyDescriptor.Name == "Session")
{
var sessionRepository = DependencyResolver.Current.GetService<ISessionRepository>();
return sessionRepository.Get(controllerContext.HttpContext.Request["SessionId"]);
}
return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
}
}