2014-04-02 146 views
2

我一直在尋找模型綁定來解決我遇到的具體問題。我嘗試了一些在各種博客和計算器的答案中描述的方法,但我並沒有真正到達那裏。複雜的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); 
    } 

回答

3

最好的方法是繼承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) 
     } 
    } 
} 
+0

我應該爲標記列表類型註冊此類型DefaultModelBinder或特別? – IvanL

+0

@IvanL,因爲這種粘合劑不不會干擾任何其他類型,只需替換默認值即可:'ModelBinders.Bind ers.DefaultBinder = new TagListModelBinder();' – Andrei

+0

我試過解決方案,但我遇到了異常「參數從類型'System.String'轉換爲類型'ROCME.EventDrive.Backend.Models.Shared.Tag'失敗因爲沒有類型轉換器可以在這些類型之間進行轉換。「儘管我發現預期的綁定發生了。此外,我需要以不同的方式處理ValueProviderResult,因爲.ToString()會導致類型名稱而不是實際使用的值。 – IvanL

0

如果您將此數據重新發布到ajax調用中,您可以準備數據,以便將其綁定到您的模型屬性TagList。

我假設標籤類的結構是

public class Tag 
{ 
    public string Name {get; set;} 
    public string Id {get; set;} 
} 

雖然調用Ajax,你可以做

  1. 拆分上逗號隱藏字段的值(,)
  2. 將每個標籤的值加上

    分割列表中的每個標籤

    data.push(「name」:「TagList [count]。名稱」, 「值」, 「名稱綁定」);

    data.push( 「名稱」: 「標記表第[count] .ID」, 「值」, 「標識綁定」);

    算上++

而且隨着Ajax請求發佈此數據,並希望這應該綁定到你的模型。

+0

我沒有使用ajax發佈我的數據,它是直接表單提交 – IvanL