自定义验证属性 MVC2

发布于 2024-10-01 03:13:00 字数 2532 浏览 5 评论 0原文

我有一个自定义验证属性,用于检查两个属性是否具有相同的值(例如密码和重新输入密码):

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
    public class EqualToPropertyAttribute : ValidationAttribute
    {
        public string CompareProperty { get; set; }

        public EqualToPropertyAttribute(string compareProperty)
        {
            CompareProperty = compareProperty;
            ErrorMessage = string.Format(Messages.EqualToError, compareProperty);
        }

        public override bool IsValid(object value)
        {
            if (value == null)
            {
                return true;
            }
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
            var property = properties.Find(CompareProperty, true);
            var comparePropertyValue = property.GetValue(value).ToString();

            return comparePropertyValue == value.ToString();
        }
    }

我有一个视图模型类,其中包含注册表单的所有字段,如下所示:

public class SignUpViewModel
    {
        [Required]
        [StringLength(100)]
        public string Username { get; set; }

        [Required]
        [Password]
        public string Password { get; set; }

        [Required]
        [DisplayText("RetypePassword")]
        [EqualToProperty("Password")]
        public string RetypePassword { get; set; }

        [Required]
        [StringLength(50)]
        [DisplayText("FirstName")]
        public string FirstName { get; set; }

        [Required]
        [StringLength(100)]
        [DisplayText("LastName")]
        public string LastName { get; set; }

        [Required]
        [DisplayText("SecurityQuestion")]
        public int SecurityQuestionID { get; set; }

        public IEnumerable<SelectListItem> SecurityQuestions { get; set; }

        [Required]
        [StringLength(50)]
        public string Answer { get; set; }
}

下面是我的控制器代码:

public virtual ActionResult Index()
    {
        var signUpViewModel = new SignUpViewModel();

        signUpViewModel.SecurityQuestions = new SelectList(questionRepository.GetAll(),"SecurityQuestionID", "Question");
        return View(signUpViewModel);
    }

        [HttpPost]
        public virtual ActionResult Index(SignUpViewModel viewModel)
        {
            // Code to save values to database
        }

当我输入表单值并点击“提交”时,尝试获取属性描述符 var property =properties.Find(CompareProperty, true); 的代码行返回 null。谁能帮助我理解为什么会发生这种情况?

I have a custom validation attribute which checks to see if two properties have the same values or not (like password and retype password):

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
    public class EqualToPropertyAttribute : ValidationAttribute
    {
        public string CompareProperty { get; set; }

        public EqualToPropertyAttribute(string compareProperty)
        {
            CompareProperty = compareProperty;
            ErrorMessage = string.Format(Messages.EqualToError, compareProperty);
        }

        public override bool IsValid(object value)
        {
            if (value == null)
            {
                return true;
            }
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
            var property = properties.Find(CompareProperty, true);
            var comparePropertyValue = property.GetValue(value).ToString();

            return comparePropertyValue == value.ToString();
        }
    }

I have a view model class which has all the fields for the signup form as follows:

public class SignUpViewModel
    {
        [Required]
        [StringLength(100)]
        public string Username { get; set; }

        [Required]
        [Password]
        public string Password { get; set; }

        [Required]
        [DisplayText("RetypePassword")]
        [EqualToProperty("Password")]
        public string RetypePassword { get; set; }

        [Required]
        [StringLength(50)]
        [DisplayText("FirstName")]
        public string FirstName { get; set; }

        [Required]
        [StringLength(100)]
        [DisplayText("LastName")]
        public string LastName { get; set; }

        [Required]
        [DisplayText("SecurityQuestion")]
        public int SecurityQuestionID { get; set; }

        public IEnumerable<SelectListItem> SecurityQuestions { get; set; }

        [Required]
        [StringLength(50)]
        public string Answer { get; set; }
}

Below is my controller code:

public virtual ActionResult Index()
    {
        var signUpViewModel = new SignUpViewModel();

        signUpViewModel.SecurityQuestions = new SelectList(questionRepository.GetAll(),"SecurityQuestionID", "Question");
        return View(signUpViewModel);
    }

        [HttpPost]
        public virtual ActionResult Index(SignUpViewModel viewModel)
        {
            // Code to save values to database
        }

When I enter the form values and hit submit the line of code which tries to get the property descriptor var property = properties.Find(CompareProperty, true); is returning null. Can anyone help me understand why this is happening?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

与他有关 2024-10-08 03:13:00

因为 IsValid()object value 参数不是整个模型,而只是您的string RetypePassword

它需要是影响整个模型对象的属性,而不仅仅是属性。

尽管我建议您使用 PropertiesMustMatchAttribute

[PropertiesMustMatch("Password", "RetypePassword", 
  ErrorMessage = "The password and confirmation password do not match.")]
public class SignUpViewModel
{
   [Required]
   [StringLength(100)]
   public string Username { get; set; }

   [Required]
   [Password]
   public string Password { get; set; }

   [Required]
   [DisplayText("RetypePassword")]
   public string RetypePassword { get; set; }

   //...
}

编辑

该属性实际上不是 ASP.NET MVC2 框架的一部分,而是在默认的 MVC 2 项目模板中定义。

[AttributeUsage( AttributeTargets.Class, AllowMultiple = true, Inherited = true )]
public sealed class PropertiesMustMatchAttribute : ValidationAttribute {
    private const string _defaultErrorMessage = "'{0}' and '{1}' do not match.";
    private readonly object _typeId = new object();

    public PropertiesMustMatchAttribute( string originalProperty, string confirmProperty )
        : base( _defaultErrorMessage ) {
        OriginalProperty = originalProperty;
        ConfirmProperty = confirmProperty;
    }

    public string ConfirmProperty { get; private set; }
    public string OriginalProperty { get; private set; }

    public override object TypeId {
        get {
            return _typeId;
        }
    }

    public override string FormatErrorMessage( string name ) {
        return String.Format( CultureInfo.CurrentUICulture, ErrorMessageString,
            OriginalProperty, ConfirmProperty );
    }

    public override bool IsValid( object value ) {
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties( value );
        object originalValue = properties.Find( OriginalProperty, true /* ignoreCase */).GetValue( value );
        object confirmValue = properties.Find( ConfirmProperty, true /* ignoreCase */).GetValue( value );
        return Object.Equals( originalValue, confirmValue );
    }
}

顺便说一句,MVC 3 有一个 CompareAttribute 这正是你想要的。

public class SignUpViewModel
{
    [Required]
    [StringLength(100)]
    public string Username { get; set; }

    [Required]
    [Password]
    public string Password { get; set; }

    [Required]
    [DisplayText("RetypePassword")]
    [Compare("Password")] // the RetypePassword property must match the Password field in order to be valid.
    public string RetypePassword { get; set; }

    // ...
}

Because the object value parameter of IsValid() is not the entire model, but just your string RetypePassword.

It would need to be an attribute that affects the whole model object, and not just a property.

Although I would suggest you to use the PropertiesMustMatchAttribute.

[PropertiesMustMatch("Password", "RetypePassword", 
  ErrorMessage = "The password and confirmation password do not match.")]
public class SignUpViewModel
{
   [Required]
   [StringLength(100)]
   public string Username { get; set; }

   [Required]
   [Password]
   public string Password { get; set; }

   [Required]
   [DisplayText("RetypePassword")]
   public string RetypePassword { get; set; }

   //...
}

Edit

That attribute is actually not part of the ASP.NET MVC2 framework, but defined in the default MVC 2 project template.

[AttributeUsage( AttributeTargets.Class, AllowMultiple = true, Inherited = true )]
public sealed class PropertiesMustMatchAttribute : ValidationAttribute {
    private const string _defaultErrorMessage = "'{0}' and '{1}' do not match.";
    private readonly object _typeId = new object();

    public PropertiesMustMatchAttribute( string originalProperty, string confirmProperty )
        : base( _defaultErrorMessage ) {
        OriginalProperty = originalProperty;
        ConfirmProperty = confirmProperty;
    }

    public string ConfirmProperty { get; private set; }
    public string OriginalProperty { get; private set; }

    public override object TypeId {
        get {
            return _typeId;
        }
    }

    public override string FormatErrorMessage( string name ) {
        return String.Format( CultureInfo.CurrentUICulture, ErrorMessageString,
            OriginalProperty, ConfirmProperty );
    }

    public override bool IsValid( object value ) {
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties( value );
        object originalValue = properties.Find( OriginalProperty, true /* ignoreCase */).GetValue( value );
        object confirmValue = properties.Find( ConfirmProperty, true /* ignoreCase */).GetValue( value );
        return Object.Equals( originalValue, confirmValue );
    }
}

On a side note, MVC 3 has a CompareAttribute that does exactly what you want.

public class SignUpViewModel
{
    [Required]
    [StringLength(100)]
    public string Username { get; set; }

    [Required]
    [Password]
    public string Password { get; set; }

    [Required]
    [DisplayText("RetypePassword")]
    [Compare("Password")] // the RetypePassword property must match the Password field in order to be valid.
    public string RetypePassword { get; set; }

    // ...
}
你是我的挚爱i 2024-10-08 03:13:00

我不确定为什么你的代码不起作用,但通过 GetType() 你可以获得预期的结果:

var property = value.GetType().GetProperty(CompareProperty);
var comparePropertyValue = property.GetValue(value, null).ToString();

I'm not sure why your code doesn't work, but through GetType() you can get the expected result:

var property = value.GetType().GetProperty(CompareProperty);
var comparePropertyValue = property.GetValue(value, null).ToString();
再浓的妆也掩不了殇 2024-10-08 03:13:00

根据 http://msdn.microsoft.com/en-us/library/ ybh0y4fd.aspx TypeDecroptor.GetProperties“返回指定组件的属性集合。”它还接着说:

组件的属性可以与类的属性不同,
因为如果组件已定位,站点可以添加或删除属性。

所以在我看来,这并不是获取类属性的正确方法。我认为@Pieter 的方法更符合您真正寻找的方法。

According to http://msdn.microsoft.com/en-us/library/ybh0y4fd.aspx the TypeDescroptor.GetProperties "Returns the collection of properties for a specified component." it also goes on to say:

The properties for a component can differ from the properties of a class,
because the site can add or remove properties if the component is sited.

So it appears to me that this isn't really the right method to be using to get the properties of a class. I think @Pieter's method is more what you're really looking for.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文