我想要一个数据注释验证器引用同一个类中的另一个属性(TitleAuthorAndPublishingConfiguration).
但是,DB.SaveChanges()没有直接在此类上调用.而是在该类的父级(WebsiteConfiguration)上被调用.
因此,validationContext.ObjectType正在返回WebsiteConfiguration,我无法引用数据注释验证器中TitleAuthorAndPublishingConfiguration的属性.
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;}
示例更新代码
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(); }
验证码
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; } }
问题
>有没有办法让我从validationContext访问子模型属性?
我误导我的做法吗?有没有更好的方法来存储多个模型作为一个更大的模型在单个数据库表的一部分?
我希望不要有多个配置表和调用DB. (在这个例子中有4个孩子模型,但在下一个应用中可能有10个.)
上面的设置以很多方式满足了我的需求.但是我不想放弃DataAnnotations在子模型上的功能!
奖金问题
我遇到过这样一个帖子:
How can I tell the Data Annotations validator to also validate complex child properties?
但是那是4岁,我想知道自那以后有没有改变.
我是否试图做一些基本上不可能的事情(至少是非常困难的)?
解决方法
Am I trying to do something that is basically impossible (or at least
very difficult)?
不,有一个非常简单的解决方案,与DataAnnotations的框架和技术完美结合.
您可以创建一个自定义ValidationAttribute,由EF Validation调用,并在其中调用Validator.TryValidateObject.这样,当通过EF调用CustomValidation.IsValid时,您可以手动启动子对象验证,以此等等对整个对象图进行验证.作为奖金,您可以收集所有错误,谢谢CompositeValidationResult.
即
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); Validator.TryValidateObject(value,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; } }