ASP.Net MVC2 CustomModelBinder не работает ... Изменено с MVC1 - PullRequest
0 голосов
/ 19 мая 2010

(мои извинения, если это кажется многословным - пытаюсь предоставить весь соответствующий код)

Я только что обновился до VS2010, и теперь у меня возникают проблемы при попытке заставить работать новый CustomModelBinder.

В MVC1 я бы написал что-то вроде

public class AwardModelBinder: DefaultModelBinder
{
    :
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // do the base binding to bind all simple types
        Award award = base.BindModel(controllerContext, bindingContext) as Award;

        // Get complex values from ValueProvider dictionary
        award.EffectiveFrom = Convert.ToDateTime(bindingContext.ValueProvider["Model.EffectiveFrom"].AttemptedValue.ToString());
        string sEffectiveTo = bindingContext.ValueProvider["Model.EffectiveTo"].AttemptedValue.ToString();
        if (sEffectiveTo.Length > 0)
            award.EffectiveTo = Convert.ToDateTime(bindingContext.ValueProvider["Model.EffectiveTo"].AttemptedValue.ToString());
        // etc

        return award;
    }
}

Конечно, я бы зарегистрировал пользовательское связующее в Global.asax.cs:

    protected void Application_Start()
    {
        RegisterRoutes(RouteTable.Routes);

        // register custom model binders
        ModelBinders.Binders.Add(typeof(Voucher), new VoucherModelBinder(DaoFactory.UserInstance("EH1303")));
        ModelBinders.Binders.Add(typeof(AwardCriterion), new AwardCriterionModelBinder(DaoFactory.UserInstance("EH1303"), new VOPSDaoFactory()));
        ModelBinders.Binders.Add(typeof(SelectedVoucher), new SelectedVoucherModelBinder(DaoFactory.UserInstance("IT0706B")));
        ModelBinders.Binders.Add(typeof(Award), new AwardModelBinder(DaoFactory.UserInstance("IT0706B")));
    }

Теперь в MVC2 я обнаружил, что мой вызов base.BindModel возвращает объект, в котором все равно нулю, и я просто не хочу повторять все поля формы, отображаемые новая функция ValueProvider.GetValue ().

Google не находит соответствий для этой ошибки, поэтому я предполагаю, что я делаю что-то не так.

Вот мой фактический код:

Мой объект домена (определите, что вам нравится в инкапсулированных дочерних объектах - я знаю, что для них тоже понадобятся пользовательские связыватели, но также появятся три "простых" поля (т. Е. Базовые типы) Id, TradingName и BusinessIncorporated. обратно ноль):

public class Customer
{
    /// <summary>
    /// Initializes a new instance of the Customer class.
    /// </summary>
    public Customer() 
    {
        Applicant = new Person();
        Contact = new Person();
        BusinessContact = new ContactDetails();
        BankAccount = new BankAccount();
    }

    /// <summary>
    /// Gets or sets the unique customer identifier.
    /// </summary>
    public int Id { get; set; }

    /// <summary>
    /// Gets or sets the applicant details.
    /// </summary>
    public Person Applicant { get; set; }

    /// <summary>
    /// Gets or sets the customer's secondary contact.
    /// </summary>
    public Person Contact { get; set; }

    /// <summary>
    /// Gets or sets the trading name of the business.
    /// </summary>
    [Required(ErrorMessage = "Please enter your Business or Trading Name")]
    [StringLength(50, ErrorMessage = "A maximum of 50 characters is permitted")]
    public string TradingName { get; set; }

    /// <summary>
    /// Gets or sets the date the customer's business began trading.
    /// </summary>
    [Required(ErrorMessage = "You must supply the date your business started trading")]
    [DateRange("01/01/1900", "01/01/2020", ErrorMessage = "This date must be between {0} and {1}")]
    public DateTime BusinessIncorporated { get; set; }

    /// <summary>
    /// Gets or sets the contact details for the customer's business.
    /// </summary>
    public ContactDetails BusinessContact { get; set; }

    /// <summary>
    /// Gets or sets the customer's bank account details.
    /// </summary>
    public BankAccount BankAccount { get; set; }
}

Метод моего контроллера:

    /// <summary>
    /// Saves a Customer object from the submitted application form.
    /// </summary>
    /// <param name="customer">A populate instance of the Customer class.</param>
    /// <returns>A partial view indicating success or failure.</returns>
    /// <httpmethod>POST</httpmethod>
    /// <url>/Customer/RegisterCustomerAccount</url>
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult RegisterCustomerAccount(Customer customer)
    {
        if (ModelState.IsValid)
        {
            // save the Customer

            // return indication of success, or otherwise
            return PartialView();
        }
        else
        {
            ViewData.Model = customer;

            // load necessary reference data into ViewData
            ViewData["PersonTitles"] = new SelectList(ReferenceDataCache.Get("PersonTitle"), "Id", "Name");

            return PartialView("CustomerAccountRegistration", customer);
        }
    }

Мой пользовательский переплет:

public class CustomerModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        ValueProviderResult vpResult = bindingContext
            .ValueProvider.GetValue(bindingContext.ModelName);
        // vpResult is null

        // MVC2 - ValueProvider is now an IValueProvider, not dictionary based anymore
        if (bindingContext.ValueProvider.GetValue("Model.Applicant.Title") != null)
        {
            // works
        }

        Customer customer = base.BindModel(controllerContext, bindingContext) as Customer;
        // customer instanitated with null (etc) throughout

        return customer;
    }
}

Регистрация моего переплета:

    /// <summary>
    /// Application_Start is called once when the web application is first accessed.
    /// </summary>
    protected void Application_Start()
    {
        RegisterRoutes(RouteTable.Routes);

        // register custom model binders
        ModelBinders.Binders.Add(typeof(Customer), new CustomerModelBinder());

        ReferenceDataCache.Populate();
    }

... и фрагмент с моей точки зрения (это может быть проблема с префиксом?)

    <div class="inputContainer">
        <label class="above" for="Model_Applicant_Title" accesskey="t"><span class="accesskey">T</span>itle<span class="mandatoryfield">*</span></label>
        <%= Html.DropDownList("Model.Applicant.Title", ViewData["PersonTitles"] as SelectList, "Select ...", 
            new { @class = "validate[required]" })%>
        <% Html.ValidationMessageFor(model => model.Applicant.Title); %>
    </div>
    <div class="inputContainer">
        <label class="above" for="Model_Applicant_Forename" accesskey="f"><span class="accesskey">F</span>orename / First name<span class="mandatoryfield">*</span></label>
        <%= Html.TextBox("Model.Applicant.Forename", Html.Encode(Model.Applicant.Forename),
                            new { @class = "validate[required,custom[onlyLetter],length[2,20]]", 
                                title="Enter your forename",
                                maxlength = 20, size = 20, autocomplete = "off",
                                  onkeypress = "return maskInput(event,re_mask_alpha);"
                            })%>
    </div>
    <div class="inputContainer">
        <label class="above" for="Model_Applicant_MiddleInitials" accesskey="i">Middle <span class="accesskey">I</span>nitial(s)</label>
        <%= Html.TextBox("Model.Applicant.MiddleInitials", Html.Encode(Model.Applicant.MiddleInitials),
                            new { @class = "validate[optional,custom[onlyLetter],length[0,8]]",
                                  title = "Please enter your middle initial(s)",
                                  maxlength = 8,
                                  size = 8,
                                  autocomplete = "off",
                                  onkeypress = "return maskInput(event,re_mask_alpha);"
                            })%>
    </div>

Ответы [ 2 ]

1 голос
/ 20 мая 2010

После загрузки и сборки с использованием исходного кода MVC2 RTM (спасибо Крэйгу за эту ссылку) я смог просмотреть код MVC и обнаружил, что в методе BindProperty (в строке 178 файла DefaultModelBinder.cs) естьтест:

protected virtual void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) {
    // need to skip properties that aren't part of the request, else we might hit a StackOverflowException
    string fullPropertyKey = CreateSubPropertyName(bindingContext.ModelName, propertyDescriptor.Name);
    if (!bindingContext.ValueProvider.ContainsPrefix(fullPropertyKey)) {
        return;
    }
    :

... что словарь ValueProvider содержит ключи с префиксом, который, по сути, является свойством ModelName для bindingContext связывателя пользовательской модели.

В моем случае - bindingContext.ModelName был выведен как «customer» (из моего типа объекта домена, я полагаю), и поэтому тест в строке 181 всегда завершался неудачей, поэтому выход из BindProperty без привязки значения моей формы.

Вот мой новый пользовательский код связывания модели:

public class CustomerModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // vitally important that we set what is the prefix of values specified in view 
        // (usually "Model" if you've rendered a strongly-typed view after setting ViewData.Model)
        bindingContext.ModelName = "Model"; 
        Customer customer = base.BindModel(controllerContext, bindingContext) as Customer;

        return customer;
    }
}

Надеюсь, это поможет всем, у кого есть подобные проблемы.

Большое спасибо Крейгу за помощь.

1 голос
/ 19 мая 2010

Привязка модели значительно изменилась в MVC 2. Она полна «ошибок» - даже больше, чем в MVC 1. Например, пустое значение в вашей форме приведет к сбою привязки . Ничто из этого не подтверждено документально. Реально, единственный хороший способ диагностировать этот материал - это собрать с исходным кодом MVC и проследить через привязку.

Я просто рад, что исходный код доступен; Я был бы потерян без этого.

...