Использование аннотаций данных со сходными моделями и одним и тем же представлением для различной проверки - PullRequest
2 голосов
/ 18 ноября 2011

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

Я пытался использовать родительский интерфейс в качестве модели представления, однако мои валидаторы не запускаются (как я ожидаю, я предполагаю, что, поскольку сам интерфейс не имеет никаких аннотаций, ничего не сработает.) Есть ли способ, чтобы страница динамически выбирала один класс или другой в зависимости от того, какую страницу я использую вместо интерфейса?

В качестве дополнительного примечания это делается в ASP.net MVC 3 с помощью Razor.

Ответы [ 3 ]

2 голосов
/ 19 ноября 2011

Вы можете достичь того, что вы хотите, с одним классом и немного латерального мышления.

Сначала создайте свой класс с запрошенной проверкой. Затем создайте пользовательский ModelValidatorProvider, унаследованный от DataAnnotationsModelValidatorProvider, например:

public class MyMetadataValidatorProvider : DataAnnotationsModelValidatorProvider
{
    protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
    {
        var vals = base.GetValidators(metadata, context, attributes);
        // check to see if any keys have been inserted
        if (context.Controller.ViewData.Keys.Count > 0)
        {
            // check if we have a key named "NoValidate" with a value of true
            // do not return the validtors if we do
            if ((bool)context.Controller.ViewData.FirstOrDefault(k => k.Key == "NoValidate").Value) 
            {
                // we do not want to return our validators, return an empty list
                return new List<ModelValidator>();
            }
        }
        else
        {
            // check if the form has a key named "NoValidate" with a value of true
            // do not return the validtors if we do
            if (context.HttpContext.Request.Form["NoValidate"].ToLowerInvariant() == "true") 
            {
                // we do not want to return our validators, return an empty list
                return new List<ModelValidator>();
            }
        }

        // we want to return our validators
        return vals;
    }
}

Далее зарегистрируйте пользовательский ModelValidatorProvider в Application_Start в Global.asax.cs, например:

ModelValidatorProviders.Providers.Clear();
ModelValidatorProviders.Providers.Add(new MyMetadataValidatorProvider());

Затем добавьте к вашему представлению следующее (это будет определять, будут ли возвращаться валидаторы при отправке формы в формате POST):

@Html.Hidden("NoValidate", ViewData.FirstOrDefault(k => k.Key == "NoValidate").Value)

Наконец, добавьте действия, подобные следующим:

public ActionResult Index()
{
    var model = new MyModel();
    // this will set validation to appear
    ViewData.Add("NoValidate", false);
    // this will suppress validation 
    ViewData.Add("NoValidate", true);
    return View(model);
}

[HttpPost]
public ActionResult Index(MyModel model)
{
    // we DO want validation, so let's test for it in addition
    // to testing if the ModelState is valid
    if (Request.Form["NoValidate"].ToLowerInvariant() != "true" && ModelState.IsValid)
    {
        ModelState.Clear();
        var newmodel = new MyModel();
        ViewData.Add("NoValidate", true);
        return View(newmodel);
    }

    ViewData.Add("NoValidate", false);
    return View(model);
}

Обратите внимание, что вы можете контролировать, будет ли проверка появляться в вашем действии GET, установив ключ NoValidate в ViewData, как вы хотите. На POST валидация определяется значением формы для NoValidate.

ВАЖНОЕ ПРИМЕЧАНИЕ: В вашем действии, которое требует проверки, вам нужно добавить тест, чтобы подтвердить, что в Форме нет ключа NoValidate или его значение не равно True, чтобы обеспечить что пользователь не может избежать проверки.

UPDATE

Сначала у меня была валидация, появляющаяся только тогда, когда выполнялись определенные условия. Предполагалось, что это была ПЛОХАЯ ИДЕЯ, поэтому теперь проверка будет подавлена ​​только при соблюдении условий.

0 голосов
/ 18 ноября 2011

Не могли бы вы использовать один класс? Вы можете создать фильтр, который позволит вам управлять ошибками проверки для действия. В вашем случае вы можете добавить атрибут к действию Сохранить и игнорировать требуемые ошибки, но проверки будут выполнены для действия отправки / завершения. Этот пример отбросит все ошибки.

public class DontValidateEmailAttribute : ActionFilterAttribute {

  public override void OnActionExecuting(ActionExecutingContext filterContext) {
    var modelState = filterContext.Controller.ViewData.ModelState; 
    var incomingValues = filterContext.Controller.ValueProvider;

    foreach (var key in modelState.Keys) 
      modelState[key].Errors.Clear();

  }
}

Я изучил эту технику у Pro ASP NET MVC Стива Сандерсона 3 . Он использует технику для проверки модели, которая имеет обязательные поля, но ввод данных является многошаговым мастером. Если значение не было возвращено в форме сообщения, он удаляет ошибки для этого свойства.

0 голосов
/ 18 ноября 2011

Каждый вид должен быть строго типизирован для отдельной модели вида.Затем каждая модель представления имеет логику проверки (аннотации) или наследуется от базы, на которой имеется требуемая проверка.

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

Вы можете использовать AutoMapper для простого отображения между каким-то конкретным объектом, который реализует ваш интерфейс, и вашими ViewModels.

...