一个自定义模型绑定器用于 CreateModel,另一个用于 BindModel?

发布于 2025-01-07 05:59:45 字数 2458 浏览 1 评论 0原文

背景: 在我的 MVC 回发操作方法中,我接收的是命令对象而不是视图模型。这个想法是,这些命令对象(大致相当于事务脚本)将在进入操作方法时设置并准备好执行,模型绑定器设置了在执行过程中使用的参数

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?或者有人知道更好的方法吗?

Background:
In my MVC post back action methods I am receiving command objects rather than view models. The idea is that these command objects (which roughly equate to transaction scripts) will be set up and ready to execute upon entering the action method, with the model binder having set parameters which are used during the execution process:

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...
    }
}

To make things a little more complex, my command objects have dependencies (services, repositories etc) which are required in their respective constructors; so I had to create a custom model binder which used the default DependencyResolver (which was already set up with my IoC container) to construct the model objects:

public class DependencyModelBinder : DefaultModelBinder
{
    protected override Object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        return DependencyResolver.Current.GetService(modelType);
    }
}

And set up in Global.asax.cs like so:

ModelBinders.Binders.DefaultBinder = new DependencyModelBinder();

Again this all works fine, the dependencies are injected into the constructor and then the default model binder takes over to set the properties as usual.

The Issue:
The problem I have is that all of my command objects have a 'SessionId' GUID parameter (which comes from a cookie), and the first thing they do is try to resolve a session object from this id using an injected service.

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...
    }
}

I wanted to remove this repetition, so I created a second model binder which would take care of this lookup in the repository, meaning my command objects could have a Session property directly (removing the constructor dependency for the session repository).

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"]);
    }
}

My Global.asax.cs file now looking like so:

ModelBinders.Binders.DefaultBinder = new DependencyModelBinder();
ModelBinders.Binders.Add(typeof(Session), new SessionModelBinder());

Having tested the SessionModelBinder in isolation, I know it works. However when using it in conjunction with the DependencyModelBinder, it is never called. How can I get MVC to use my DependencyModelBinder when constructing model objects, but have it use my SessionModelBinder when binding session properties on them? Or does anyone know a better approach to this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

帝王念 2025-01-14 05:59:45

您可以在原始模型绑定程序中使用 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);
    }
}

You could use the GetPropertyValue method in your original model binder to provide a value for the Session property:

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);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文