【问题标题】:ASP.NET MVC - Custom Model Binder on Interface TypeASP.NET MVC - 接口类型上的自定义模型绑定器
【发布时间】:2010-06-04 00:05:05
【问题描述】:

我不确定这种行为是否是预期的,但是当绑定分配给接口类型时,自定义模型绑定似乎不起作用。有人试过这个吗?

public interface ISomeModel {}
public class SomeModel : ISomeModel {}

public class MvcApplication : HttpApplication {
    protected void Application_Start(object sender, EventArgs e) {
        ModelBinders.Binders[typeof(ISomeModel)] = new MyCustomModelBinder();
    }
}

当我绑定到 SomeModel 类型的模型时,使用上面的代码,MyCustomModelBinder 永远不会被命中;但是,如果我更改上面的代码并将typeof(ISomeModel) 替换为typeof(SomeModel) 并发布与预期完全相同的表单 MyCustomModelBinder 被调用。这看起来对吗?


编辑

在我最初提出这个问题一年多之后,我发现自己又回到了这个困境中,现在我有了一个可行的解决方案。谢谢马特·希丁格!

http://www.matthidinger.com/archive/2011/08/16/An-inheritance-aware-ModelBinderProvider-in-MVC-3.aspx

【问题讨论】:

    标签: asp.net-mvc interface modelbinders


    【解决方案1】:

    我正在尝试解决这个问题,并想出了一个解决方案。我创建了一个名为 InterfaceModelBinder 的类:

    public class InterfaceModelBinder : DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            ModelBindingContext context = new ModelBindingContext(bindingContext);
            var item = Activator.CreateInstance(
                Type.GetType(controllerContext.RequestContext.HttpContext.Request.Form["AssemblyQualifiedName"]));
    
            Func<object> modelAccessor = () => item;
            context.ModelMetadata = new ModelMetadata(new DataAnnotationsModelMetadataProvider(),
                bindingContext.ModelMetadata.ContainerType, modelAccessor, item.GetType(), bindingContext.ModelName);
    
            return base.BindModel(controllerContext, context);
        }
    }
    

    我在我的 Application_Start 中这样注册:

    ModelBinders.Binders.Add(typeof(IFormSubmission), new InterfaceModelBinder.Models.InterfaceModelBinder());
    

    接口和具体实现如下所示:

    public interface IFormSubmission
    {
    }
    
    public class ContactForm : IFormSubmission
    {
        public string Name
        {
            get;
            set;
        }
    
        public string Email
        {
            get;
            set;
        }
    
        public string Comments
        {
            get;
            set;
        }
    }
    

    整个方法的唯一缺点(正如您可能已经收集到的那样)是我需要从某个地方获取 AssemblyQualifiedName,在此示例中,它被存储为客户端的隐藏字段,如下所示:

    <%=Html.HiddenFor(m => m.GetType().AssemblyQualifiedName) %>
    

    我不确定向客户端公开类型名称的缺点是否值得失去这种方法的好处。像这样的 Action 可以处理我所有的表单提交:

    [HttpPost]
    public ActionResult Process(IFormSubmission form)
    {
        if (ModelState.IsValid)
        {
            FormManager manager = new FormManager();
            manager.Process(form);
        }
    
        //do whatever you want
    }
    

    对这种方法有什么想法吗?

    【讨论】:

    • 似乎可以解决问题,但正如您所说,您类型中的程序集名称似乎是您自找麻烦。我目前不确定如何恶意使用它的好用例,但我觉得它很有趣。
    • 或许解决方案是在客户端对类型名称进行散列或加密,然后在模型绑定器中对其进行处理。
    • 只是我花了一个小时探索值得一提的东西——模型前缀似乎没有在这个实现中使用,因为它没有设置为新的绑定上下文。所以我建议使用原始的 bindingContext 设置新的 ModelMetadata ,就像一个魅力。
    【解决方案2】:
    【解决方案3】:

    我不确定它是否直接相关,但是是的,在使用模型绑定和接口时需要考虑一些事情......我在使用默认模型绑定器时遇到了类似的问题,但它可能没有直接关系取决于你做事的方式......

    请看以下内容: ASP.net MVC v2 - 调试模型绑定问题 - BUG? ASP.net MVC v2 - Debugging Model Binding Issues - BUG?

    【讨论】:

    • 我的继承结构和你的有点不同,但看起来这可能是一个相关的问题。我应该认为模型绑定系统可以推断出实现接口的类型也应该受分配给该接口的模型绑定器的约束,只要没有其他类型绑定到比接口更具体的类型(同一类,或基类)。我想我可以明白为什么那会是一个灰色区域。
    • 这也是我的感觉......我希望它能够正常工作,但事实证明,当使用 asp.net MVC 以这种方式处理接口时(尽管严格来说,它们不是 MVC 限制)如果他们愿意,可以允许)如果您不考虑这些界面问题,您可能会遇到问题/意外行为......因此灰色区域......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-27
    • 1970-01-01
    • 2018-01-02
    相关资源
    最近更新 更多