Как предотвратить проверку связей в связывателе модели? - PullRequest
1 голос
/ 16 июля 2010

Например, если у меня есть класс с именем Order с полем, ссылающимся на Customer, а затем форма Order с выпадающим списком (<% = Html.DropDownListFor (e => e.Customer.ID, new SelectList (...))%>) для установки Customer связыватель модели создаст пустого Customer только с установленным идентификатором.Это нормально работает с NHibernate, но когда проверка добавляется в некоторые поля класса Customer, связыватель модели скажет, что эти поля обязательны для заполнения.Как я могу помешать связующему с моделью проверить эти ссылки?

Спасибо!

1 Ответ

0 голосов
/ 02 февраля 2011

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

Переопределение, которое вы ищете в DefaultModelBinder, - это GetPropertyValue.Это вызывается для всех свойств в модели, и по умолчанию вызывается DefaultModelBinder.BindModel - точка входа во весь процесс.

Упрощенная модель:

public class Organization
{
    public int Id { get; set; }

    [Required]
    public OrganizationType Type { get; set; }
}

public class OrganizationType
{
    public int Id { get; set; }

    [Required, MaxLength(30)]
    public string Name { get; set; }
}

Представление:

<div class="editor-label">
    @Html.ErrorLabelFor(m => m.Organization.Type)
</div>
<div class="editor-field">
     @Html.DropDownListFor(m => m.Organization.Type.Id, Model.OrganizationTypes, "-- Type")
</div>

Связыватель модели:

public class OrganizationModelBinder : DefaultModelBinder
{
    protected override object GetPropertyValue(
        ControllerContext controllerContext, 
        ModelBindingContext bindingContext, 
        System.ComponentModel.PropertyDescriptor propertyDescriptor, 
        IModelBinder propertyBinder)
    {
        if (propertyDescriptor.PropertyType == typeof(OrganizationType))
        {
            var idResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + ".Id");
            if (idResult == null || string.IsNullOrEmpty(idResult.AttemptedValue))
            {
                return null;   
            }

            var id = (int)idResult.ConvertTo(typeof(int));
            // Can validate the id against your database at this point if needed...
            // Here we just create a stub object, skipping the model binding and
            // validation on OrganizationType
            return new OrganizationType { Id = id };
        }

        return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
    }
}

Обратите внимание, что в представлении мы создаем DropDownListFor для модели.foo.bar. Id .В связывателе модели убедитесь, что это также добавлено к названию модели.Вы можете отключить его от обоих, но тогда у DropDownListFor есть несколько проблем с поиском выбранного значения без предварительного выбора его в списке выбора, который вы отправляете.

Наконец, вернувшись в контроллер, обязательно присоедините это свойство.в контексте вашей базы данных (если вы используете Entity Framework, другие могут работать по-другому).В противном случае он не отслеживается, и контекст попытается добавить его при сохранении.

Контроллер:

public ActionResult Create(Organization organization)
{
    if (ModelState.IsValid)
    {
        _context.OrganizationTypes.Attach(organization.Type);
        _context.Organizations.Add(organization);
        _context.SaveChanges();

        return RedirectToAction("Index");
    }

    // Build up view model...
    return View(viewModel);
}
...