ASP.NET MVC3 ModelBinding Проблема - Получение значений выпадающих в модели? - PullRequest
0 голосов
/ 23 октября 2011

Я потерял больше своих волос, пытаясь решить эту проблему, поэтому я действительно надеялся, что вы поможете?

У меня есть сетка Telerik MVC: Grid in View mode

И у меня есть собственный EditorTemplate для Address (Address.ascx) Grid in Edit mode

Моя модель ImporterDetails:

   public class ImporterViewModel : IEnumerableViewModel
    {
        public int ImporterId { get; set; }

        [Required]
        [DisplayName("Importer Name *")]
        public string ImporterName { get; set; }

        [Required]
        [DisplayName("Importer Address *")]
        public Address ImporterAddress { get; set; }

        public static ImporterViewModel CreateImporter()
        {
            return new ImporterViewModel
                       {
                           ImporterName = Guid.NewGuid().ToString().Substring(0, 5),
                           ImporterAddress = Address.CreateDummyAddress(),
                       };
        }

    }

И AddressViewModel:

[Bind(Exclude = "State, Country")]
public class Address
{
    public int AddressId { get; set; }

    [DisplayName("Address Line 1")]
    [Required]
    public string Line1 { get; set; }

    [DisplayName("Address Line 2")]
    public string Line2 { get; set; }

    [DisplayName("Postcode")]
    [Required]
    [RegularExpression(RegexConstants.AUSTRALIAN_POSTCODE_PATTERN, ErrorMessage = "Invalid post code")]
    public string Postcode { get; set; }

    [DisplayName("State")]
    public State State { get; set; }

    [DisplayName("Suburb")]
    public string Suburb { get; set; }

    [DisplayName("Country")]
    public Country Country { get; set; }

    [Required]
    public int CountryId { get; set; }

    [Required]
    public int StateId { get; set; }

    /// <summary>
    /// Creates a new dummy instance of Address
    /// </summary>
    /// <returns></returns>
    public static Address CreateDummyAddress()
    {
        return new Address
                   {
                       Country = ServiceLocatorFactory.GetCodeServiceLocator<Country>().Get(x => x.CodeValue.ToLower() == "canada"),
                       State = ServiceLocatorFactory.GetCodeServiceLocator<State>().Get(x => x.CodeValue.ToLower() == "nsw"),
                       Line1 = Guid.NewGuid().ToString().Substring(0, 15),
                       Line2 = Guid.NewGuid().ToString().Substring(0, 15),
                       Suburb = "Dandenong",
                       Postcode = "2606",
                   };
    }

    public string AddressStrings
    {
        get
        {
            return ToString();
        }
    }

    public override string ToString()
    {
        // create a blank StringBuilder
        var sb = new StringBuilder();

        // add the first address line
        sb.Append(string.Format("{0}, ", Line1));

        // add the second address line
        sb.Append(string.Format("{0}, ", Line2));

        sb.Append(string.Format("{0}, ", Suburb));
        sb.Append(string.Format("{0} {1}, ", State == null ? string.Empty : State.Description, Postcode));
        sb.Append(string.Format("{0}", Country == null ? string.Empty : Country.Description));

        // and then return it as a single (formatted) string
        return sb.ToString();
    }
}

Вы заметите, что я исключил Штат и Страну, потому что, если я этого не сделаю, когда я вызываю TryUpdateModel (импортер) - я получаю страшное исключение конструктора без параметров. Мой вопрос:

Как мне получить правильный идентификатор штата и страны (или вообще любого раскрывающегося списка) в моих действиях таким образом?

Для полноты картины:

Address.ascx

<div class="formElementGroupVertical">
    <%: Html.LabelFor(m => m.Line1) %>
    <%: Html.EditorFor(m => m.Line1) %>
    <%: Html.ValidationMessageFor(m => m.Line1) %>
</div>
<div class="formElementGroupVertical">
    <%: Html.LabelFor(m => m.Line2) %>
    <%: Html.EditorFor(m => m.Line2) %>
    <%: Html.ValidationMessageFor(m => m.Line2) %>
</div>
<div class="formElementGroupVertical">
    <%: Html.LabelFor(m => m.Suburb) %>
    <%: Html.EditorFor(m => m.Suburb)%>
    <%: Html.ValidationMessageFor(m => m.Suburb)%>
</div>
<div class="formElementGroupVertical">
    <%: Html.LabelFor(m => m.State) %>
    <%: Html.EditorFor(m => m.State) %>
</div>
<div class="formElementGroupVertical">
    <%: Html.LabelFor(m => m.Postcode) %>
    <%: Html.EditorFor(m => m.Postcode)%>
    <%: Html.ValidationMessageFor(m => m.Postcode)%>
</div>
<div class="formElementGroupVertical">
    <%: Html.LabelFor(m => m.Country) %>
    <%: Html.EditorFor(m => m.Country) %>
</div>

Страна:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Web.Common.Models.Country>" %>
<%@ Import Namespace="Web.Common.Models" %>
<%@ Import Namespace="Web.Common.Service" %>
<%: Html.DropDownListFor(m => m.CodeId, new SelectList(ServiceLocatorFactory.GetCodeServiceLocator<Country>().GetAll(), "CodeId", "Description"), "Please Select")%>

И государство идентично Стране, кроме очевидного.

Любая помощь?

Ответы [ 2 ]

2 голосов
/ 23 октября 2011

Краткий ответ:

CountryId не заполняется, поскольку DropDownlistFor имеет значение «For» country => country.CodeId.

Чтобы получить CountryId, вам действительно нужно указать раскрывающийся список:

Html.DropDownListFor(m => m.CountryId, new SelectList(ServiceLocatorFactory.GetCodeServiceLocator<Country>().GetAll(), "CodeId", "Description"), "Please Select")%>

чуть более длинный ответ:

Самый простой способ получить значение раскрывающегося списка - это связать DropDownListFor со свойством вашей модели представления, в котором хранится идентификатор. Затем в вашем контроллере вы сгенерируете (например, через базу данных) объект с этим идентификатором и присоедините его к любой модели в соответствии с вашими бизнес-требованиями.

Это хлопотно, но прямо. В настоящее время не существует автоматического способа связывания полной модели объектов с помощью раскрывающихся списков AFAIK.

В вашем случае ваша модель представления будет иметь:

public class AddressViewModel
{
  public int SelectedCountryId { get; set; }
}

затем используйте DropDownFor() таким образом:

<%: Html.DropDownListFor(m => m.SelectedCountryId, new SelectList(ServiceLocatorFactory.GetCodeServiceLocator<Country>().GetAll(), "CodeId", "Description"), "Please Select")%>

А затем в вашем действии (в псевдокоде):

public ViewResult Save(AddressViewModel addressVM)
{
  var address = new Address() { Country = countriesStore.ById(addressVM.SelectedCountryId) };
  address.Save();

  ...
}

Этот способ также означает, что вам нужно будет использовать модель представления вместо модели домена для ваших представлений. Ваш пример, похоже, использует модель предметной области, поэтому вы можете захотеть изменить это. Когда вы начнете использовать модели представлений, вам также понадобится что-то вроде Automapper , чтобы упростить сопоставление моделей представлений с моделями доменов.

0 голосов
/ 24 октября 2011

Ладно, ребята, мне кажется, что у меня есть временное решение проблемы, и я хотел бы узнать ваше мнение и, возможно, любые другие альтернативы, которые могут сделать это решение более "надежным".

В своем действии я проверяю Request.Form и ищу раскрывающийся список, указанный PropertyName сложного типа. Синтаксис выглядит так:

Поле PropertyName.PropertyName.Value, возвращаемое раскрывающимся списком. Используя это, я затем могу искать репозиторий, если требуется получить экземпляр ComplexType.

Мое действие вставлено ниже для справки:

[AcceptVerbs(HttpVerbs.Post)]
[GridAction]
public ActionResult Update(int id)
{
    // obtain the instance of the importer we wish to update
    var importer = serviceLocator.Get(i => i.ImporterId == id);

    // the address object doesn't bind the State and Country dropdowns so we must manually read these
    var stateCodeId = Request.Form["ImporterAddress.State.CodeId"];
    var countryCodeId = Request.Form["ImporterAddress.Country.CodeId"];

    //Perform model binding (fill the properties and validate the model)
    if (TryUpdateModel(importer))
    {
        // parse the Id fields of the selected values of the dropdowns that we've just read from the request objects
        importer.ImporterAddress.StateId = int.Parse(stateCodeId);
        importer.ImporterAddress.CountryId = int.Parse(countryCodeId);

        // and convert them to their specific code objects
        importer.ImporterAddress.State =
            ServiceLocatorFactory.GetCodeServiceLocator<State>().Get(s => s.CodeId == int.Parse(stateCodeId));
        importer.ImporterAddress.Country =
            ServiceLocatorFactory.GetCodeServiceLocator<Country>().Get(s => s.CodeId == int.Parse(countryCodeId));

        // now save the updated model
        serviceLocator.Update(importer);
    }

    // rebind the grid
    return PartialView(new GridModel(serviceLocator.GetAll().ToList()));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...