ASP.NET MVC - 发布具有不同数据类型的自定义字段的表单

标签 asp.net asp.net-mvc viewmodel

在我的 ASP.NET MVC 2 Web 应用程序中,我允许用户创建不同数据类型的自定义输入字段来扩展我们的基本输入表单。虽然很棘手,但从一组自定义字段构建输入表单已经足够简单了。

但是,我现在已经到了要处理此表单的发布的地步,我不确定处理此表单的最佳方法是什么。通常,我们会使用从表单上可用的各种静态类型输入绑定(bind)的强类型输入模型。但是,我不知道如何使用代表不同数据类型的可变数量的输入字段来执行此操作。

一个有代表性的输入表单可能类似于:

  • 我的日期字段:[日期时间输入
    控制]
  • 我的文本字段:[文本输入
    ]
  • 我的文件字段:[文件上传
    控制]
  • 我的号码栏:【数值输入控件】
  • 我的文本字段 2:[文本输入字段]
  • 等等...

  • 我想过的想法是:
  • 将所有内容作为字符串发送(文件输入除外,需要特别处理)。
  • 使用具有“对象”属性的模型并尝试绑定(bind)到该属性(如果可能的话)。
  • 使用正确编码的数据向我的 Controller 发送 json 请求并尝试解析它。
  • 在我的 Controller 发布操作中手动处理表单集合 - 当然是一个选项,但我很想避免这种情况。

  • 以前有没有人解决过这样的问题?如果是这样,您是如何解决的?

    更新:

    我的“基本”表单在另一个输入区域一起处理,因此解决方案不需要为此考虑任何类型的继承魔法。我只是对处理此界面上的自定义字段感兴趣,而不是我的“基本”字段。

    更新 2:

    感谢 ARM 和 smartcaveman;你们俩都为如何做到这一点提供了很好的指导。一旦实现,我将用我的最终解决方案更新这个问题。

    最佳答案

    这就是我开始处理这个问题的方式。基于 FormKey 属性(可以由索引和/或标签确定,具体取决于),自定义模型绑定(bind)器将非常容易构建。

    public class CustomFormModel
    {
        public string FormId { get; set; }
        public string Label { get; set; }
        public CustomFieldModel[] Fields { get; set; }
    }
    public class CustomFieldModel
    {
        public DataType DateType { get; set; } //  System.ComponentModel.DataAnnotations
        public string FormKey { get; set; }
        public string Label { get; set; }
        public object Value { get; set; }
    }
    public class CustomFieldModel<T> : CustomFieldModel
    {
        public new T Value { get; set; }
    }
    

    另外,我注意到下面的评论之一有一个过滤模型绑定(bind)系统。 Automapper 的 Jimmy Bogard 在 http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/03/17/a-better-model-binder.aspx 上发表了一篇关于此方法的非常有用的帖子。 ,后来在 http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/11/19/a-better-model-binder-addendum.aspx 中进行了修订.它对我构建自定义模型绑定(bind)器非常有帮助。

    更新

    我意识到我误解了这个问题,并且他专门询问如何处理“具有代表不同数据类型的可变数量的输入字段”的表单的发布。我认为最好的方法是使用类似于上面的结构,但利用 Composite Pattern .基本上,您需要创建一个类似 IFormComponent 的接口(interface)。并为将要表示的每个数据类型实现它。我编写并评论了一个示例界面,以帮助解释如何实现:
    public interface IFormComponent
    {
        //  the id on the html form field.  In the case of a composite Id, that doesn't have a corresponding 
        //  field you should still use something consistent, since it will be helpful for model binding
        //  (For example, a CompositeDateField appearing as the third field in the form should have an id 
        //  something like "frmId_3_date" and its child fields would be "frmId_3_date_day", "frmId_3_date_month",
        //  and "frmId_3_date_year".
        string FieldId { get; }
    
        //  the human readable field label
        string Label { get; }
    
        //  some functionality may require knowledge of the 
        //  Parent component.  For example, a DayField with a value of "30"
        //  would need to ask its Parent, a CompositeDateField
        //  for its MonthField's value in order to validate
        //  that the month is not "February"
        IFormComponent Parent { get; }
    
        //  Gets any child components or null if the 
        //  component is a leaf component (has no children).
        IList<IFormComponent> GetChildren();
    
        //  For leaf components, this method should accept the AttemptedValue from the value provider
        //  during Model Binding, and create the appropriate value.  
        //  For composites, the input should be delimited in someway, and this method should parse the 
        //  string to create the child components.  
        void BindTo(string value);
    
        //  This method should parse the Children or Underlying value to the 
        //  default used by your business models.  (e.g. a CompositeDateField would 
        //  return a DateTime.  You can get type safety by creating a FormComponent<TValue>
        //  which would help to avoid issues in binding.
        object GetValue();
    
        //  This method would render the field to the http response stream.
        //  This makes it easy to render the forms simply by looping through 
        //  the array.  Implementations could extend this for using an injected 
        //  formatting 
        void Render(TextWriter writer);
    } 
    

    我假设可以通过某种可以作为表单参数包含的 id 访问自定义表单。有了这个假设,模型绑定(bind)器和提供者可能看起来像这样。
    public interface IForm : IFormComponent
    {
        Guid FormId { get; }
        void Add(IFormComponent component);
    }
    public interface IFormRepository
    {
        IForm GetForm(Guid id);
    }
    public class CustomFormModelBinder : IModelBinder   
    {
        private readonly IFormRepository _repository;
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            ValueProviderResult result;
            if(bindingContext.ValueProvider.TryGetValue("_customFormId", out result))
            {
                var form = _repository.GetForm(new Guid(result.AttemptedValue));
                var fields = form.GetChildren();
                //  loop through the fields and bind their values 
                return form;
            }
            throw new Exception("Form ID not found.");
        }
    }
    

    显然,这里的所有代码只是为了说明问题,需要完成和清理以供实际使用。此外,即使完成,这也只会绑定(bind)到 IForm 接口(interface)的实现,而不是强类型业务对象。 (将其转换为字典并使用 CaSTLe DictionaryAdapter 构建强类型代理并不是一个巨大的步骤,但由于您的用户正在网站上动态创建表单,因此您的解决方案中可能没有强类型模型,并且这无关紧要)。希望这有助于更多。

    关于ASP.NET MVC - 发布具有不同数据类型的自定义字段的表单,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4412279/

    相关文章:

    html - Ajax 调用 Controller 后,ASP.NET MVC 显示 View

    wpf - 从一个 View 模型访问另一个 View 模型的属性

    c# - View 模型应该如何与存储库通信?

    css - 单击其他地方后侧面板不会消失

    javascript - 如何从 asp.net 中的 javascript 函数调用服务器端按钮事件?

    c# - 使用 linq 和 c# 查询数据表

    c# - 将Guid.NewGuid添加到SQL数据库问题

    javascript - 在 radTreeView 中选择一个节点

    javascript - Slickgrid 更改我的数据中的 ID 并且不清理空行

    android - 应用程序启动后回压时出现 IllegalArgumentException