我一直在寻找模型绑定来解决我遇到的具体问题。我尝试了一些在各种博客和计算器的答案中描述的方法,但我并没有真正到达那里。复杂的ASP.NET MVC绑定
首先,我会提出我的模型:
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,所以这对我来说相当重要。标记表仅仅是这样的:
public class TagList : List<Tag>
{
}
我创造了这个类能够轻松而不必把UIHint属性为它创建一个EditorTemplate。现在我为我的taglist编辑器使用select2.js库,它处理现有标签中的ajax搜索等等。这里的问题是Select2绑定到一个隐藏字段,它将a分隔各个标签值,并根据它是现有标签还是新标签使用文本或ID生成列表,如1,tag,34,my new tag
。这个输入我想翻译成TagList。
所以具体的问题是: 我该如何模型绑定这个单一的隐藏的输入到我的模型的TagList属性,并能够轻松地重用这种行为为我所有的模型?
EDIT =>添加EditorTemplate和该模型绑定器的代码我创建基于关安德烈的答案
我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>
我的模型绑定器(集作为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或特别? – IvanL
@IvanL,因为这种粘合剂不不会干扰任何其他类型,只需替换默认值即可:'ModelBinders.Bind ers.DefaultBinder = new TagListModelBinder();' – Andrei
我试过解决方案,但我遇到了异常“参数从类型'System.String'转换为类型'ROCME.EventDrive.Backend.Models.Shared.Tag'失败因为没有类型转换器可以在这些类型之间进行转换。“尽管我发现预期的绑定发生了。此外,我需要以不同的方式处理ValueProviderResult,因为.ToString()会导致类型名称而不是实际使用的值。 – IvanL