c# - 复杂的 ASP.NET MVC 绑定(bind)

标签 c# asp.net-mvc

我一直在研究模型绑定(bind)来解决我遇到的特定问题。我尝试了各种博客和 stackoverflow 答案中描述的一些方法,但我并没有真正做到这一点。

首先我将展示我的模型:

public class CampaignModel
{
    [Required]
    [StringLength(24)]
    [Display(Name = "CampaignModel_Name", Prompt = "CampaignModel_Name", ResourceType = typeof(CampaignResources))]
    public string Name { get; set; }

    [StringLength(255)]
    [Display(Name = "CampaignModel_Description", Prompt = "CampaignModel_Description", ResourceType = typeof(CampaignResources))]
    public string Description { get; set; }

    [Required]
    [DataType(DataType.Date)]
    [Display(Name = "CampaignModel_StartDate", ResourceType = typeof(CampaignResources))]
    public DateTime StartDate { get; set; }

    [Required]
    [DataType(DataType.Date)]
    [Display(Name = "CampaignModel_EndDate", ResourceType = typeof(CampaignResources))]
    public DateTime EndDate { get; set; }

    [Display(Name = "CampaignModel_Tags", Prompt = "CampaignModel_Tags", ResourceType = typeof(CampaignResources))]
    public TagList Tags { get; set; }
}

所以这是一个相当基本的模型,除了最后一个属性 TagList 之外。现在,将来我的模型分配将有一个标签列表,所以这对我来说相当重要。 TagList 就是这样:

public class TagList : List<Tag>
{
}

我创建这个类是为了能够轻松地为其创建 EditorTemplate,而无需放置 UIHint 属性。现在我使用 select2.js 库作为我的标签列表编辑器,它处理现有标签中的 ajax 搜索等。这里的问题是 Select2 绑定(bind)到一个隐藏字段,在该字段中用 分隔各个标签值,并且根据它是现有标签还是新标签,它使用文本或 id 生成一个列表,例如 1,tag, 34,我的新标签。我想将此输入转换为 TagList。

所以具体问题是: 如何在模型中将此单个隐藏输入绑定(bind)到模型上的 TagList 属性中,并能够轻松地为我的所有模型重用此行为?

编辑 => 添加我根据 Andrei 的答案创建的 EditorTemplate 和 ModelBinder 的代码

我的EditorTemplate(省略js)

<div class="form-group">
    @Html.Label(string.Empty, ViewData.ModelMetadata.DisplayName, new { @class = "col-md-3 control-label" })
    <div class="col-md-9">
        @Html.Hidden(string.Empty, ViewData.TemplateInfo.FormattedModelValue, new { @class = "form-control select2" })
    </div>
</div>

我的ModelBinder(在global.asax中设置为DefaultModelBinder)

public class TagListModelBinder : DefaultModelBinder
{
    protected override void BindProperty(
        ControllerContext controllerContext,
        ModelBindingContext bindingContext,
        PropertyDescriptor propertyDescriptor)
    {
        if (propertyDescriptor.PropertyType == typeof(TagList))
        {
            ValueProviderResult value = bindingContext.ValueProvider.GetValue(propertyDescriptor.Name);
            string[] rawTags = value.AttemptedValue.Split(',');
            List<long> tagIds = new List<long>();

            TagList tags = new TagList();
            foreach (string rawTag in rawTags)
            {
                long id;
                if (long.TryParse(rawTag, out id))
                {
                    // Existing tags need to be retrieved from DB
                    tagIds.Add(id);
                }
                else
                {
                    // New tags can simply be added without ID
                    tags.Add(new Tag { Text = rawTag });
                }
            }

            if (tagIds.Count > 0)
            {
                using (TagServiceClient client = new TagServiceClient())
                {
                    List<Services.TagService.Tag> existingTags = client.GetTagsByIds(tagIds);
                    tags.AddRange(existingTags.Select(t => new Tag { Id = t.id, Text = t.text }));
                }
            }

            propertyDescriptor.SetValue(bindingContext.Model, tags);
        }

        base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
    }

最佳答案

到达这里的最佳方法是继承 DefaultModelBinder 并检查您正在处理的属性。如果它是 TagList 类型 - 继续并应用您需要的任何逻辑。否则让默认实现完成工作。

public class TagListModelBinder : DefaultModelBinder
{
    protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor )
    {
        if (propertyDescriptor.PropertyType == typeof(TagList))
        {
            ValueProviderResult value = bindingContext.ValueProvider.GetValue(propertyDescriptor.Name);
            string[] rawTags = value.ToString().Split(',');

            TagList tags = new TagList();
            foreach (string rawTag in rawTags)
            { 
                // for numbers - get them from DB
                // for strings - create new and store in DB
                // then add them to tags
            }

            propertyDescriptor.SetValue(bindingContext.Model, tags);
        }
        else
        {
            base.BindProperty(controllerContext, bindingContext, propertyDescriptor)
        }
    }
}

关于c# - 复杂的 ASP.NET MVC 绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22807038/

相关文章:

c# - 从我的 Controller 获取信息到 onclick 确认(警报)

jquery - VS2013 中浏览器链接的优缺点

javascript - 在 ASP.NET MVC - Javascript 应用程序中处理日期的最佳方式

asp.net-mvc - asp.net mvc - 完全脱离 DB 中的自定义字段构建 View

asp.net-mvc - ASP.NET MVC3,为什么即使在强类型 View 中下拉列表也依赖viewbag

c# - 着色器中的Unity相对方向

c# - 如何在 ASP.NET 样板中设置社交登录?

c# - 如何从 IIS 上托管的 .net 代码访问 key 保管库 secret

c# - 使用 DynamicProxy 拦截对异步方法的调用

c# - 为什么我们使用内部类?