Можно ли переопределить обязательный атрибут для свойства в модели? - PullRequest
17 голосов
/ 18 января 2012

Мне любопытно узнать, возможно ли переопределить атрибут [Обязательный], который был установлен в модели. Я уверен, что у кого-нибудь есть простое решение этой проблемы?

Ответы [ 5 ]

17 голосов
/ 18 января 2012

Зависит от того, что именно вы делаете.Если вы работаете с подклассом, используя модель с атрибутом Required в качестве базы, вы можете сделать это:

Переопределить свойство с ключевым словом new, а не переопределять его.

public class BaseModel
{
    [Required]
    public string RequiredProperty { get; set; }
}


public class DerivativeModel : BaseModel
{
    new public string RequiredProperty { get; set; }

}

Если вы просто хотите связать или проверить модель, но пропустите свойство Required в вашем контроллере, вы можете сделать что-то вроде:

public ActionResult SomeAction()
{
     var model = new BaseModel();

     if (TryUpdateModel(model, null, null, new[] { "RequiredProperty" })) // fourth parameter is an array of properties (by name) that are excluded
     {
          // updated and validated correctly!
          return View(model);
     }
     // failed validation
     return View(model);
}
13 голосов
/ 16 июля 2015

@ HackedByChinese метод в порядке, но он содержит проблему

public class BaseModel
{
    [Required]
    public string RequiredProperty { get; set; }
}

public class DerivativeModel : BaseModel
{
    new public string RequiredProperty { get; set; }
}

Этот код дает вам ошибку проверки в ModelState ДАЖЕ, если вы используете DerivativeModel в форме, override не делает 'Это тоже не работает, поэтому вы не можете удалить атрибут Required, переопределив или обновив его, поэтому я нашел какой-то обходной путь

public class BaseModel
{
    public virtual string RequiredProperty { get; set; }
}

public class DerivativeModel : BaseModel
{
    [Required]
    public override string RequiredProperty { get; set; }
}

public class DerivativeModel2 : BaseModel
{
    [Range(1, 10)]
    public override string RequiredProperty { get; set; }
}

У меня есть базовая модель без атрибутов проверки и производных классов

5 голосов
/ 17 июня 2014

Вы можете использовать пользовательский атрибут проверки (он может быть получен из RequiredAttribute):

 public class RequiredExAttribute : RequiredAttribute
    {
        public bool UseRequiredAttribute { get; protected set; }
        public RequiredExAttribute(bool IsRequired)
        {
            UseRequiredAttribute = IsRequired;
        }
        public override bool IsValid(object value)
        {
            if (UseRequiredAttribute)
                return base.IsValid(value);
            else
            {
                return true;
            }
        }

        public override bool RequiresValidationContext
        {
            get
            {
                return UseRequiredAttribute;
            }
        }
    }

    public class RequiredExAttributeAdapter : RequiredAttributeAdapter
    {
        public RequiredExAttributeAdapter(ModelMetadata metadata, ControllerContext context, RequiredExAttribute attribute)
            : base(metadata, context, attribute) { }

        public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
        {
            if (((RequiredExAttribute)Attribute).UseRequiredAttribute)// required -> return normal required rules
                return base.GetClientValidationRules();
            else// not required -> return empty rules list
                return new List<ModelClientValidationRule>();
        }
    }

, а затем повторно ввести его в Application_Start, используя следующую строку кода:

 DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredExAttribute), typeof(RequiredExAttributeAdapter));
1 голос
/ 13 марта 2017

Да, это возможно с использованием класса MetadataType , например:

[MetadataType(typeof(Base.Metadata))]
public class Base
{    
    public string RequiredProperty { get; set; }

    public class Metadata
    {
        [Required]
        public string RequiredProperty { get; set; }
    }
}

[MetadataType(typeof(Derived.Metadata))]
public class Derived : Base 
{
    public new class Metadata
    {
    }
}

И проверить это:

var type = typeof(Derived);

var metadataType = typeof(Derived.Metadata);

var provider = new AssociatedMetadataTypeTypeDescriptionProvider(type, metadataType);

TypeDescriptor.AddProviderTransparent(provider, type);

var instance = new Derived();

var results = new List<ValidationResult>();

Validator.TryValidateObject(instance,
    new ValidationContext(instance),
    results,
    true);

Debug.Assert(results.Count == 0);
1 голос
/ 10 ноября 2016

Я попробовал ответ Махмуда, но он не сработал для меня без нескольких изменений. Добавив это в качестве ответа, я могу дать код, который помог в случае, если он кому-то поможет, но в полной мере благодарю Махмуда Хбубати - я проголосовал за ваш ответ.

В моей ситуации у меня был базовый класс DTO со свойством DbGeography, которое требовалось для проекта MVC, в котором использовались пользовательские EditorTemplate и DisplayTemplate для типа DbGeography. Но для публикации модели в контроллере Web API я хотел добавить поля широты / долготы в подкласс этого DTO, который будет использоваться для создания и установки экземпляра класса DbGeography для установки значения в свойстве DbGeography. Проблема заключалась в том, что я не мог сделать свойство DbGeography не обязательным только для подкласса.

Когда в конструктор было передано логическое значение с использованием подхода Махмуда, мне никогда не удавалось переопределить значение по умолчанию. Это может быть связано с тем, что я использую Web API и регистрирую атрибут с использованием фабричного подхода, как показано ниже (в методе Global.asax.cs Application_Start):

DataAnnotationsModelValidationFactory factory = (p, a) => new DataAnnotationsModelValidator(
    new List<ModelValidatorProvider>(), new RequiredExAttribute()
);

DataAnnotationsModelValidatorProvider provider = new DataAnnotationsModelValidatorProvider();
provider.RegisterAdapterFactory(typeof(RequiredExAttribute), factory);

Мне пришлось изменить класс атрибута на это:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
...
public class RequiredExAttribute : RequiredAttribute
{
    public bool IsRequired { get; set; }

    public override bool IsValid(object value)
    {
        if (IsRequired)
            return base.IsValid(value);
        else
        {
            return true;
        }
    }

    public override bool RequiresValidationContext
    {
        get
        {
            return IsRequired;
        }
    }
}

public class RequiredExAttributeAdapter : RequiredAttributeAdapter
{
    public RequiredExAttributeAdapter(ModelMetadata metadata, ControllerContext context, RequiredExAttribute attribute)
        : base(metadata, context, attribute) { }

    public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
    {
        if (((RequiredExAttribute)Attribute).IsRequired)// required -> return normal required rules
            return base.GetClientValidationRules();
        else// not required -> return empty rules list
            return new List<ModelClientValidationRule>();
    }
}

Базовый класс:

[RequiredEx(IsRequired = true)]
public virtual DbGeography Location { get; set; }

Подкласс:

[RequiredEx(IsRequired = false)]
public override DbGeography Location { get; set; }

[Required]
public decimal Latitude { get; set; }

[Required]
public decimal Longitude { get; set; }

Обратите внимание, я использовал тот же метод, что и Махмуд, для регистрации атрибута в моем проекте MVC:

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredExAttribute), typeof(RequiredExAttributeAdapter));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...