Условно проверяющие части модели ASP.NET MVC с DataAnnotations? - PullRequest
9 голосов
/ 30 августа 2009

У меня есть определенные панели на моей странице, которые скрыты при определенных обстоятельствах.

Например, у меня могут быть «адрес выставления счета» и «адрес доставки», и я не хочу проверять «адрес доставки», если установлен флажок «ShippingSameAsBilling».

Я пытаюсь использовать новые возможности DataAnnotations ASP.NET MVC 2 (превью 1) для достижения этой цели.

Мне нужно запретить проверку «адреса доставки», когда он не отображается, и мне нужно найти способ добиться этого. Я говорю в основном на стороне сервера, а не с использованием jquery .

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

Ответы [ 7 ]

6 голосов
/ 30 августа 2009

Для CheckoutModel я использую этот подход (большинство полей скрыты):

[ModelBinder(typeof(CheckoutModelBinder))]
public class CheckoutModel : ShoppingCartModel
{        
    public Address BillingAddress { get; set; }
    public Address ShippingAddress { get; set; }
    public bool ShipToBillingAddress { get; set; }
}

public class Address
{
    [Required(ErrorMessage = "Email is required")]
    public string Email { get; set; }

    [Required(ErrorMessage = "First name is required")]
    public string FirstName { get; set; }

    [Required()]
    public string LastName { get; set; }

    [Required()]
    public string Address1 { get; set; }
}

Пользовательский связыватель модели удаляет все ошибки ModelState для полей, начинающихся с «ShippingAddress», если он их обнаруживает. Тогда TryUpdateModel () вернет true.

    public class CheckoutModelBinder : DefaultModelBinder
    {
        protected override void OnModelUpdated(ControllerContext controllerContext,
                                               ModelBindingContext bindingContext) {

            base.OnModelUpdated(controllerContext, bindingContext);

            var model = (CheckoutModel)bindingContext.Model;

            // if user specified Shipping and Billing are the same then 
            // remove all ModelState errors for ShippingAddress
            if (model.ShipToBillingAddress)
            {
                var keys = bindingContext.ModelState.Where(x => x.Key.StartsWith("ShippingAddress")).Select(x => x.Key).ToList();
                foreach (var key in keys)
                {
                    bindingContext.ModelState.Remove(key);
                }
            }
        }    
    }

Есть ли лучшие решения?

3 голосов
/ 30 августа 2009
2 голосов
/ 29 октября 2009

Убедитесь, что поля, которые вы не хотите проверять, не опубликованы в действии. Мы проверяем только те поля, которые были действительно опубликованы.

Редактировать: (автор вопроса)

Это поведение изменилось в MVC2 RC2:

Система проверки по умолчанию проверяет Вся модель Проверка по умолчанию система в ASP.NET MVC 1.0 и в предварительный просмотр ASP.NET MVC 2 до RC 2 проверены только свойства модели, которые были размещены на сервере. В ASP.NET MVC 2, новое поведение таково, что все свойства модели проверяются, когда модель проверена, независимо от было ли опубликовано новое значение. Приложения, которые зависят от Для поведения ASP.NET MVC 1.0 может потребоваться изменения. Для получения дополнительной информации о это изменение, см. запись Ввод Проверка по сравнению с проверкой модели в ASP.NET MVC в блоге Брэда Уилсона.

2 голосов
/ 28 октября 2009

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

Ограничение интерфейса IDataErrorInfo состоит в том, что объект модели удовлетворяет действительному состоянию просто тогда, когда ни одно из свойств не содержит ошибок. Это означает, что объект valid - это объект, в котором все его свойства также valid . Однако у меня может возникнуть ситуация, когда свойства A, B и C допустимы - тогда весь объект действителен. , но также , если свойство A недопустимо, но B и C Есть, то объект удовлетворяет действительности. У меня просто нет возможности описать это условие / правило с помощью атрибутов IDataErrorInfo interface / DataAnnotations.

Итак, я нашел этот делегатский подход . На момент написания этой статьи многие полезные усовершенствования в MVC еще не существовали, но основная концепция должна вам помочь. Вместо того чтобы использовать атрибуты для определения условий проверки объекта, мы создаем функции-делегаты, которые проверяют более сложные требования, и поскольку они делегированы, мы можем использовать их повторно. Конечно, это больше работы, но использование делегатов означает, что мы сможем написать код правила проверки один раз и , чтобы хранить все правила проверки в одном месте (возможно, на уровне обслуживания) и (бит kool) даже используют MVC 2 DefaultModelBinder для автоматического запуска проверки (без куч проверки в действиях нашего контроллера - как в блоге Скотта говорится, что мы можем делать с DataAnnotations. См. последний абзац перед заголовком «Сильно типизированные помощники пользовательского интерфейса»)

Я уверен, что вы можете немного улучшить подход, предложенный в приведенной выше статье, с помощью анонимных делегатов, таких как Func<T> или Predicate<T>, а написание пользовательских блоков кода для правил проверки позволит включить условия между свойствами (например, условие, на которое вы ссылались, если ваше ShippingSameAsBilling свойство имеет значение true, тогда вы можете игнорировать дополнительные правила для адреса доставки и т. д.).

DataAnnotations служит для создания простых правил проверки объектов действительно просто с очень небольшим кодом. Но по мере развития ваших требований вам придется проверять более сложные правила. Новые виртуальные методы в модуле связывания моделей MVC2 должны и впредь предоставлять нам способы интеграции наших будущих проверочных изобретений в инфраструктуру MVC.

1 голос
/ 19 октября 2010

Я создал механизм связывания частичной модели, который проверяет только отправленные ключи. По соображениям безопасности (если я собираюсь сделать это еще дальше), я бы создал атрибут аннотации данных, который помечает, какие поля разрешено исключать из модели. Затем OnModelUpdated проверяет атрибуты поля, чтобы убедиться в отсутствии нежелательной занижения.

public class PartialModelBinder : DefaultModelBinder
{
    protected override void OnModelUpdated(ControllerContext controllerContext, 
        ModelBindingContext bindingContext)
    {
        // default model binding to get errors
        base.OnModelUpdated(controllerContext, bindingContext);

        // remove errors from filds not posted
        // TODO: include request files
        var postedKeys = controllerContext.HttpContext.Request.Form.AllKeys;
        var unpostedKeysWithErrors = bindingContext.ModelState
            .Where(i => !postedKeys.Contains(i.Key))
            .Select(i=> i.Key).ToList();
        foreach (var key in unpostedKeysWithErrors)
        {
            bindingContext.ModelState.Remove(key);
        }
    }    
}
1 голос
/ 10 ноября 2009

Для более сложных случаев я перешел от простых аннотаций к следующим: Проверка с помощью посетителей и методов расширения .

Если вы хотите использовать свои DataAnnotations, вы должны заменить что-то вроде следующего:

public IEnumerable<ErrorInfo> BrokenRules (Payment payment)
{   
    // snip... 
    if (string.IsNullOrEmpty (payment.CCName))
    {
      yield return new ErrorInfo ("CCName", "Credit card name is required");
    }
}

с методом проверки свойства по имени через DataAnnotations (которого у меня нет atm).

0 голосов
/ 10 ноября 2009

Это не относится к DataAnnotations , но рассматривали ли вы проект Fluent Validation ? Это дает вам точный контроль над вашей валидацией, и если у вас есть валидация между объектами, совокупный объект этих двух объектов поможет вам.

Также, похоже, он был собран с учетом MVC, но у него также есть своя " runtime ", так что вы можете использовать его и в других приложениях .NET, что является еще одним бонусом в моей книге.

...