我如何可以使用自定义的验证在DB实体儿童模特属性? [英] How Can I Use Custom Validation Attributes on Child Models of a DB Entity?

查看:178
本文介绍了我如何可以使用自定义的验证在DB实体儿童模特属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

摘要:

我要一个数据注释验证器来引用同一类的另一个属性( TitleAuthorAndPublishingConfiguration )。

不过,DB.SaveChanges()不被直接调用这个类。相反,它被调用这个类的父( WebsiteConfiguration )。

所以 validationContext.ObjectType 将返回 WebsiteConfiguration ,我无法参照 TitleAuthorAndPublishingConfiguration 数据注释验证器内。


WebsiteConfiguration.cs

 公共类WebsiteConfiguration
{
    [键]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)
    公众诠释ID {搞定;组; }    公共TitleAuthorAndPublishingConfiguration TitleAuthorAndPublishing {搞定;组; }    公共BookChaptersAndSectionsConfiguration BookChaptersAndSections {搞定;组; }    公共SocialMediaLoginsConfiguration SocialMediaLogins {搞定;组; }    公共TagGroupsConfiguration TagGroups {搞定;组; }
}公共类TitleAuthorAndPublishingConfiguration
{
    公共字符串BOOKTITLE {搞定;组; }    公共BOOL |评论{搞定;组; }    //如何调用DB.SaveChanges()对父母,当我访问当前模型的属性?
    [RequiredIfOtherFieldIsEnabled(|评论)]
    公共字符串出版商{搞定;组; }
}// ...等子模型...

ApplicationDbContext.cs

  DbSet< WebsiteConfiguration> WebsiteConfiguration {获取;设置;}

示例更新code

 公共无效SeedWebsiteConfiguration()
    {
        VAR titleAuthorAndPublishingConfiguration =新TitleAuthorAndPublishingConfiguration()
        {
            //种子值
        };
        VAR bookChaptersAndSectionsConfiguration =新BookChaptersAndSectionsConfiguration()
        {
            //种子值
        };
        VAR socialMediaLoginConfiguration =新SocialMediaLoginsConfiguration()
        {
            //种子值
        };
        VAR tagGroupsConfiguration =新TagGroupsConfiguration()
        {
            //种子值
        };
        VAR websiteConfiguration =新WebsiteConfiguration()
        {
            TitleAuthorAndPublishing = titleAuthorAndPublishingConfiguration,
            BookChaptersAndSections = bookChaptersAndSectionsConfiguration,
            SocialMediaLogins = socialMediaLoginConfiguration,
            TagGroups = tagGroupsConfiguration
        };
        DB.WebsiteConfiguration.Add(websiteConfiguration);
        DB.SaveChanges();
    }


验证code

 公共类RequiredIfOtherFieldIsEnabledAttribute:ValidationAttribute
{
    私人字符串_ifWhatIsEnabled {搞定;组; }
    公共RequiredIfOtherFieldIsEnabledAttribute(字符串IfWhatIsEnabled)
    {
        _ifWhatIsEnabled = IfWhatIsEnabled;
    }    保护覆盖的ValidationResult的IsValid(对象currentPropertyValue,ValidationContext validationContext)
    {
        VAR isEnabledProperty = validationContext.ObjectType.GetProperty(_ifWhatIsEnabled);
        如果(isEnabledProperty == NULL)
        {
            返回新的ValidationResult(
                的String.Format(未知的属性:{0},_ifWhatIsEnabled)
            );
        }
        VAR isEnabledPropertyValue =(布尔)isEnabledProperty.GetValue(validationContext.ObjectInstance,NULL);        如果(isEnabledPropertyValue ==真)
        {
            如果(String.IsNullOrEmpty(currentPropertyValue.ToString()))
            {
                返回新的ValidationResult(的String.Format(,isEnabledProperty)这个字段,如果{0}启用必需);
            }
        }
        返回ValidationResult.Success;
    }
}


问题


  1. 有没有办法,我从 validationContext

  2. 访问儿童模特属性
  3. 我在我的做法误导?有没有更好的方式来多个模型存储为一个单一的数据库表中的大模型的一部分​​?


我希望不要有多个配置表和调用数据库。 (有在本实施例4子模型,但有可能是10+在下一应用程序。)

上面的设置是否满足在很多方面我的需要。但我不想放弃DataAnnotations的功能上的子模型!


奖金问题

我也碰到过几个帖子像这样的:
<一href=\"http://stackoverflow.com/questions/2493800/how-can-i-tell-the-data-annotations-validator-to-also-validate-complex-child-pro\">How我可以告诉数据注释验证器也验证复杂的子属性?

但是,这是4岁了,如果过什么改变了我不知道。

难道我试图做一些事情,基本上是不可能的(或至少是非常困难的)?


解决方案

  

我是试图做一些事情,基本上是不可能的(或至少
  很困难)?


没有,还有就是与框架以及使用DataAnnotations技术完美集成了一个非常简单的解决方案。

您可以创建自定义 ValidationAttribute 由EF验证调用,调用 Validator.TryValidateObject 里面。这样,当 CustomValidation.IsValid 由EF叫你手工启动子复杂对象的验证等整个对象图。作为奖励,你可以收集所有的错误感谢 CompositeValidationResult

 使用系统;
使用System.ComponentModel.DataAnnotations;
使用System.Collections.Generic;公共类节目
{
    公共静态无效的主要(){
   变种人=新的Person {
      地址=新地址{
         市=超人前传,
         状态=TX,
         邮编=新的Zip code()
      },
      NAME =肯特
   };   VAR语境=新ValidationContext(人,NULL,NULL);
   VAR的结果=新的List&LT;&为ValidationResult GT;();   Validator.TryValidateObject(人,环境,结果,真正的);   PrintResults(结果,0);   Console.ReadKey();
}私有静态无效PrintResults(IEnumerable的&LT;&为ValidationResult GT;的结果,的Int32 indentationLevel){
   的foreach(在结果VAR为ValidationResult){
      Console.WriteLine(validationResult.ErrorMessage);
      Console.WriteLine();      如果(为ValidationResult是CompositeValidationResult){
         PrintResults(((CompositeValidationResult)为ValidationResult)。结果,indentationLevel + 1);
      }
   }
}}公共类ValidateObjectAttribute:ValidationAttribute {
   保护覆盖的ValidationResult的IsValid(对象的值,ValidationContext validationContext){
      VAR的结果=新的List&LT;&为ValidationResult GT;();
      VAR语境=新ValidationContext(值,NULL,NULL);      Validator.TryValidateObject(价值,环境,结果,真正的);      如果(results.Count!= 0){
         VAR compositeResults =新CompositeValidationResult(的String.Format(验证{0}失败!,validationContext.DisplayName));
         results.ForEach(compositeResults.AddResult);         返回compositeResults;
      }      返回ValidationResult.Success;
   }
}公共类CompositeValidationResult:为ValidationResult {
   私人只读表&LT;&为ValidationResult GT; _results =新的List&LT;&为ValidationResult GT;();   公共IEnumerable的&LT;&为ValidationResult GT;结果{
      获得{
         返回_results;
      }
   }   公共CompositeValidationResult(字符串的errorMessage):基地(的errorMessage){}
   公共CompositeValidationResult(字符串的errorMessage,IEnumerable的&LT;串GT; memberNames):基地(的errorMessage,memberNames){}
   保护CompositeValidationResult(为ValidationResult为ValidationResult):基地(为ValidationResult){}   公共无效AddResult(为ValidationResult为ValidationResult){
      _results.Add(为ValidationResult);
   }
}公共类Person {
  [需要]
  公共字符串名称{;组; }  [必需的,ValidateObject]
  公共地址地址{搞定;组; }
}公共类地址{
  [需要]
  公共字符串Street1 {搞定;组; }  公共字符串STREET2 {搞定;组; }  [需要]
  公共字符串城{搞定;组; }  [需要]
  公共字符串状态{搞定;组; }  [必需的,ValidateObject]
  公共邮编code邮编{搞定;组; }
}公共类邮编code {
  [需要]
  公共字符串小学code {搞定;组; }  公共字符串子code {搞定;组; }
}

Summary:

I want a data annotation validator to reference another property in the same class (TitleAuthorAndPublishingConfiguration).

However, DB.SaveChanges() is not being called on this class directly. Rather it is being called on the parent of this class (WebsiteConfiguration).

Therefore validationContext.ObjectType is returning WebsiteConfiguration and I am unable to refer to properties of TitleAuthorAndPublishingConfiguration within the data annotation validator.


WebsiteConfiguration.cs

public class WebsiteConfiguration
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }

    public TitleAuthorAndPublishingConfiguration TitleAuthorAndPublishing { get; set; }

    public BookChaptersAndSectionsConfiguration BookChaptersAndSections { get; set; }

    public SocialMediaLoginsConfiguration SocialMediaLogins { get; set; }

    public TagGroupsConfiguration TagGroups { get; set; }
}

public class TitleAuthorAndPublishingConfiguration 
{
    public string BookTitle { get; set; }

    public bool IsPublished { get; set; }

    // how do I access a property of current model when calling DB.SaveChanges() on parent?
    [RequiredIfOtherFieldIsEnabled("IsPublished")]
    public string Publisher { get; set; }
}

// ... and other sub models...

ApplicationDbContext.cs

DbSet<WebsiteConfiguration> WebsiteConfiguration {get;set;}

Example Update Code

    public void SeedWebsiteConfiguration()
    {
        var titleAuthorAndPublishingConfiguration = new TitleAuthorAndPublishingConfiguration()
        {
            // seed values
        };
        var bookChaptersAndSectionsConfiguration = new BookChaptersAndSectionsConfiguration()
        {
            // seed values
        };
        var socialMediaLoginConfiguration = new SocialMediaLoginsConfiguration()
        {
            // seed values
        };
        var tagGroupsConfiguration = new TagGroupsConfiguration()
        {
            // seed values
        };
        var websiteConfiguration = new WebsiteConfiguration()
        {
            TitleAuthorAndPublishing = titleAuthorAndPublishingConfiguration,
            BookChaptersAndSections = bookChaptersAndSectionsConfiguration,
            SocialMediaLogins = socialMediaLoginConfiguration,
            TagGroups = tagGroupsConfiguration
        };
        DB.WebsiteConfiguration.Add(websiteConfiguration);
        DB.SaveChanges();
    }


Validator Code

public class RequiredIfOtherFieldIsEnabledAttribute : ValidationAttribute
{
    private string _ifWhatIsEnabled { get; set; }


    public RequiredIfOtherFieldIsEnabledAttribute(string IfWhatIsEnabled)
    {
        _ifWhatIsEnabled = IfWhatIsEnabled;
    }

    protected override ValidationResult IsValid(object currentPropertyValue, ValidationContext validationContext)
    {
        var isEnabledProperty = validationContext.ObjectType.GetProperty(_ifWhatIsEnabled);
        if (isEnabledProperty == null)
        {
            return new ValidationResult(
                string.Format("Unknown property: {0}", _ifWhatIsEnabled)
            );
        }
        var isEnabledPropertyValue = (bool)isEnabledProperty.GetValue(validationContext.ObjectInstance, null);

        if (isEnabledPropertyValue == true)
        {
            if (String.IsNullOrEmpty(currentPropertyValue.ToString()))
            {
                return new ValidationResult(String.Format("This field is required if {0} is enabled", isEnabledProperty));
            }
        }
        return ValidationResult.Success;
    }
}


Questions

  1. Is there a way for me to access child model properties from validationContext?

  2. Am I misguided in my approach? Is there a better way to store multiple models as part of a larger model in a single DB table?

I was hoping not to have multiple config tables and calls to the DB. (There are 4 child models in this example, but there may be 10+ in the next app.)

The setup above meets my needs in so many ways. But I don't want to give up the functionality of DataAnnotations on the sub models!


Bonus Question

I have come across a few posts like this one: How can I tell the Data Annotations validator to also validate complex child properties?

But that is 4 years old, and I'm wondering if anything has changed since then.

Am I trying to do something that is basically impossible (or at least very difficult)?

解决方案

Am I trying to do something that is basically impossible (or at least very difficult)?

No, there is a very simple solution that integrates perfectly with the framework and technologies using DataAnnotations.

You can create a custom ValidationAttribute that is called by EF Validation and call Validator.TryValidateObject inside. This way, when CustomValidation.IsValid is called by EF you launch child complex object validation by hand and so on for the whole object graph. As a bonus, you can gather all errors thanks to CompositeValidationResult.

i.e.

using System;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;

public class Program
{
    public static void Main() {
   var person = new Person {
      Address = new Address {
         City = "SmallVille",
         State = "TX",
         Zip = new ZipCode()
      },
      Name = "Kent"
   };

   var context = new ValidationContext(person, null, null);
   var results = new List<ValidationResult>();

   Validator.TryValidateObject(person, context, results, true);

   PrintResults(results, 0);

   Console.ReadKey();
}

private static void PrintResults(IEnumerable<ValidationResult> results, Int32 indentationLevel) {
   foreach (var validationResult in results) {
      Console.WriteLine(validationResult.ErrorMessage);
      Console.WriteLine();

      if (validationResult is CompositeValidationResult) {
         PrintResults(((CompositeValidationResult)validationResult).Results, indentationLevel + 1);
      }
   }
}

}

public class ValidateObjectAttribute: ValidationAttribute {
   protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
      var results = new List<ValidationResult>();
      var context = new ValidationContext(value, null, null);

      Validator.TryValidateObject(value, context, results, true);

      if (results.Count != 0) {
         var compositeResults = new CompositeValidationResult(String.Format("Validation for {0} failed!", validationContext.DisplayName));
         results.ForEach(compositeResults.AddResult);

         return compositeResults;
      }

      return ValidationResult.Success;
   }
}

public class CompositeValidationResult: ValidationResult {
   private readonly List<ValidationResult> _results = new List<ValidationResult>();

   public IEnumerable<ValidationResult> Results {
      get {
         return _results;
      }
   }

   public CompositeValidationResult(string errorMessage) : base(errorMessage) {}
   public CompositeValidationResult(string errorMessage, IEnumerable<string> memberNames) : base(errorMessage, memberNames) {}
   protected CompositeValidationResult(ValidationResult validationResult) : base(validationResult) {}

   public void AddResult(ValidationResult validationResult) {
      _results.Add(validationResult);
   }
}

public class Person {
  [Required]
  public String Name { get; set; }

  [Required, ValidateObject]
  public Address Address { get; set; }
}

public class Address {
  [Required]
  public String Street1 { get; set; }

  public String Street2 { get; set; }

  [Required]
  public String City { get; set; }

  [Required]
  public String State { get; set; }

  [Required, ValidateObject]
  public ZipCode Zip { get; set; }
}

public class ZipCode {
  [Required]
  public String PrimaryCode { get; set; }

  public String SubCode { get; set; }
}

这篇关于我如何可以使用自定义的验证在DB实体儿童模特属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆