Мне любопытно узнать, возможно ли переопределить атрибут [Обязательный], который был установлен на модели. Я уверен, что в большинстве случаев это простое решение этой проблемы, любые участники?
Можно ли переопределить требуемый атрибут свойства в модели?
Ответ 1
Зависит от того, что именно вы делаете. Если вы работаете с подклассом, используя модель с обязательным атрибутом в качестве базы, вы можете сделать это:
Переопределите свойство с ключевым словом 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);
}
Ответ 2
Метод @HackedByChinese в порядке, но он содержит проблему
public class BaseModel
{
[Required]
public string RequiredProperty { get; set; }
}
public class DerivativeModel : BaseModel
{
new public string RequiredProperty { get; set; }
}
Этот код дает вам ошибку проверки в ModelState
EVEN, если вы используете 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; }
}
У меня есть базовая модель без атрибутов проверки и производных классов
Ответ 3
Вы можете использовать специальный атрибут проверки (он может быть получен из 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));
Ответ 4
Я попробовал ответить Махмуду, но это не сработало для меня без нескольких изменений. Добавляя это как ответ, чтобы я мог дать код, который сделал, если он помогает кому-то другому, но полный кредит Махмуду Хбубати - я поддержал ваш ответ.
В моей ситуации у меня был базовый класс DTO с свойством DbGeography, который был необходим для проекта MVC, который использовал пользовательский шаблон EditorTemplate и DisplayTemplate для типа DbGeography. Но для размещения модели в контроллере Web API я хотел бы, чтобы поля широты/долготы были добавлены в подкласс этого DTO, который будет использоваться для создания и установки экземпляра класса DbGeography для установки значения в свойстве DbGeography. Проблема заключалась в том, что я не мог сделать свойство DbGeography не обязательным только для подкласса.
Когда логическое значение передавалось в конструкторе с использованием подхода Mahmoud, оно никогда не меняло значение по умолчанию для меня. Возможно, это связано с тем, что я использую Web API и регистрирую атрибут с помощью подхода factory, как показано ниже (в методе 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));
Ответ 5
Да, это возможно с помощью класса 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);