validationVAB配置文件中的程序集和名称空间

我们正在使用validation应用程序块的4.1版。 我相对较新,所以我想知道它是否有能力抽象出配置的命名空间和程序集,或者提供其存在的正确validation?

我们最近遇到了一个问题,即有人移动了一个类,并且没有使用新的命名空间更新validation配置文件。 结果,validation不再应用于对象。 应用程序块似乎只是忽略了差异。 不幸的是,在正常的QA周期中没有发现这种情况。 是否有任何内置的方法可以保护自己免受未来这种变化的影响? 我在过渡期间所做的是加载config xml,提取出所有程序集和定义的名称空间并validation它们是否都存在。

编写一组unit testing,以查看对象是否正确validation。

另一个选择是以流畅的方式动态构建vab配置。 这将为您提供编译时支持和重构安全性。

这只有一个问题: vab不支持开箱即用 。 你必须自己写。 多年来,我已经在我的待办事项清单上创建了具有此类function的VAB contrib项目,但我从未有时间这样做。 虽然不是那么难。 这是我几年前写的代码(但从来没有时间完成它):

public interface IValidationConfigurationMemberCreator { ValidationConfigurationBuilderRuleset BuilderRuleset { get; } } public static class ValidationConfigurationBuilderExtensions { public static ValidationConfigurationBuilderProperty ForField( this IValidationConfigurationMemberCreator memberCreator, Expression> fieldSelector) { string fieldName = ExtractFieldName(fieldSelector); return new ValidationConfigurationBuilderProperty(memberCreator.BuilderRuleset, fieldName); } public static ValidationConfigurationBuilderProperty ForProperty( this IValidationConfigurationMemberCreator memberCreator, Expression> propertySelector) { string propertyName = ExtractPropertyName(propertySelector); return new ValidationConfigurationBuilderProperty(memberCreator.BuilderRuleset, propertyName); } private static string ExtractPropertyName(LambdaExpression propertySelector) { if (propertySelector == null) { throw new ArgumentNullException("propertySelector"); } var body = propertySelector.Body as MemberExpression; if (body == null || body.Member.MemberType != MemberTypes.Property) { throw new ArgumentException("The given expression should return a property.", "propertySelector"); } return body.Member.Name; } private static string ExtractFieldName(LambdaExpression fieldSelector) { if (fieldSelector == null) { throw new ArgumentNullException("fieldSelector"); } var body = fieldSelector.Body as MemberExpression; if (body == null || body.Member.MemberType != MemberTypes.Field) { throw new ArgumentException("The given expression should return a field.", "fieldSelector"); } return body.Member.Name; } public static ValidationConfigurationBuilderMember AddRangeValidator( this ValidationConfigurationBuilderMember memberBuilder, RangeData rangeData) where TMember : IComparable { memberBuilder.AddValidator(rangeData.CreateValidator()); return memberBuilder; } } public class ValidationConfigurationBuilder : IConfigurationSource { private readonly ValidationSettings settings; private readonly HashSet alternativeRulesetNames = new HashSet(StringComparer.Ordinal); private string defaultRulesetName; public ValidationConfigurationBuilder() { this.settings = new ValidationSettings(); } public event EventHandler SourceChanged; public void RegisterDefaultRulesetForAllTypes(string rulesetName) { if (string.IsNullOrEmpty(rulesetName)) { throw new ArgumentNullException("rulesetName"); } if (this.settings.Types.Count > 0) { throw new InvalidOperationException("Registeringen rulesets for all types is not possible " + "after types are registered."); } this.defaultRulesetName = rulesetName; } public void RegisterAlternativeRulesetForAllTypes(string rulesetName) { if (string.IsNullOrEmpty(rulesetName)) { throw new ArgumentNullException("rulesetName"); } if (this.settings.Types.Count > 0) { throw new InvalidOperationException("Registeringen rulesets for all types is not possible " + "after types are registered."); } if (this.alternativeRulesetNames.Contains(rulesetName)) { throw new InvalidOperationException("There already is a ruleset with this name: " + rulesetName); } this.alternativeRulesetNames.Add(rulesetName); } public ValidationConfigurationBuilderType ForType() { ValidatedTypeReference typeReference; if (this.settings.Types.Contains(typeof(T).FullName)) { typeReference = this.settings.Types.Get(typeof(T).FullName); } else { typeReference = new ValidatedTypeReference(typeof(T)) { AssemblyName = typeof(T).Assembly.GetName().FullName, }; if (this.defaultRulesetName != null) { typeReference.Rulesets.Add(new ValidationRulesetData(this.defaultRulesetName)); typeReference.DefaultRuleset = this.defaultRulesetName; } foreach (var alternativeRulesetName in this.alternativeRulesetNames) { typeReference.Rulesets.Add(new ValidationRulesetData(alternativeRulesetName)); } this.settings.Types.Add(typeReference); } return new ValidationConfigurationBuilderType(this, typeReference); } ConfigurationSection IConfigurationSource.GetSection(string sectionName) { if (sectionName == ValidationSettings.SectionName) { return this.settings; } return null; } #region IConfigurationSource Members void IConfigurationSource.Add(string sectionName, ConfigurationSection configurationSection) { throw new NotImplementedException(); } void IConfigurationSource.AddSectionChangeHandler(string sectionName, ConfigurationChangedEventHandler handler) { throw new NotImplementedException(); } void IConfigurationSource.Remove(string sectionName) { throw new NotImplementedException(); } void IConfigurationSource.RemoveSectionChangeHandler(string sectionName, ConfigurationChangedEventHandler handler) { throw new NotImplementedException(); } void IDisposable.Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } #endregion protected virtual void Dispose(bool disposing) { } } public class ValidationConfigurationBuilderType : IValidationConfigurationMemberCreator { internal ValidationConfigurationBuilderType(ValidationConfigurationBuilder builder, ValidatedTypeReference typeReference) { this.Builder = builder; this.TypeReference = typeReference; } ValidationConfigurationBuilderRuleset IValidationConfigurationMemberCreator.BuilderRuleset { get { return this.ForDefaultRuleset(); } } internal ValidationConfigurationBuilder Builder { get; private set; } internal ValidatedTypeReference TypeReference { get; private set; } public ValidationConfigurationBuilderRuleset ForDefaultRuleset() { if (string.IsNullOrEmpty(this.TypeReference.DefaultRuleset)) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "There hasn't been an default ruleset registered for the type {0}.", typeof(TEntity).FullName)); } var defaultRuleset = this.TypeReference.Rulesets.Get(this.TypeReference.DefaultRuleset); if (defaultRuleset == null) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The default ruleset with name {0} is missing from type {1}.", this.TypeReference.DefaultRuleset, typeof(TEntity).FullName)); } return new ValidationConfigurationBuilderRuleset(this, defaultRuleset); } public ValidationConfigurationBuilderRuleset ForRuleset(string rulesetName) { var ruleset = this.TypeReference.Rulesets.Get(rulesetName); if (ruleset == null) { ruleset = new ValidationRulesetData(rulesetName); this.TypeReference.Rulesets.Add(ruleset); //throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, // "The ruleset with name '{0}' has not been registered yet for type {1}.", // rulesetName, this.TypeReference.Name)); } return new ValidationConfigurationBuilderRuleset(this, ruleset); } internal void CreateDefaultRuleset(string rulesetName) { if (this.TypeReference.Rulesets.Get(rulesetName) != null) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "The ruleset with name '{0}' has already been registered yet for type {1}.", rulesetName, this.TypeReference.Name)); } if (!string.IsNullOrEmpty(this.TypeReference.DefaultRuleset)) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "The type {0} already has a default ruleset.", this.TypeReference.Name)); } var ruleset = new ValidationRulesetData(rulesetName); this.TypeReference.Rulesets.Add(ruleset); this.TypeReference.DefaultRuleset = rulesetName; } internal void CreateAlternativeRuleset(string rulesetName) { if (this.TypeReference.Rulesets.Get(rulesetName) != null) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "The ruleset with name '{0}' has already been registered yet for type {1}.", rulesetName, this.TypeReference.Name)); } var ruleset = new ValidationRulesetData(rulesetName); this.TypeReference.Rulesets.Add(ruleset); } } public class ValidationConfigurationBuilderRuleset : IValidationConfigurationMemberCreator { internal ValidationConfigurationBuilderRuleset( ValidationConfigurationBuilderType builderType, ValidationRulesetData ruleset) { this.BuilderType = builderType; this.Ruleset = ruleset; } internal ValidationConfigurationBuilderType BuilderType { get; private set; } ValidationConfigurationBuilderRuleset IValidationConfigurationMemberCreator.BuilderRuleset { get { return this; } } internal ValidationRulesetData Ruleset { get; private set; } internal ValidationConfigurationBuilderRuleset AddValidator(ValidatorData validator) { if (validator == null) { throw new ArgumentNullException("validator"); } // 'Name' is the default value when the validator has not been given a name. if (validator.Name == "Name") { // In that case we set the name to something more specific. validator.Name = typeof(TEntity).Name + "_" + validator.Type.Name; } var validators = this.Ruleset.Validators; // When that specific name already exist, we add a number to that name to ensure uniqueness. if (validators.Contains(validator.Name)) { validator.Name += "_" + validators.Count.ToString(); } validators.Add(validator); return this; } } public abstract class ValidationConfigurationBuilderMember : IValidationConfigurationMemberCreator { private readonly ValidationConfigurationBuilderRuleset builderRuleset; internal ValidationConfigurationBuilderMember( ValidationConfigurationBuilderRuleset builderRuleset, string memberName) { this.builderRuleset = builderRuleset; this.MemberName = memberName; } ValidationConfigurationBuilderRuleset IValidationConfigurationMemberCreator.BuilderRuleset { get { return this.builderRuleset; } } internal ValidationConfigurationBuilderRuleset BuilderRuleset { get { return this.builderRuleset; } } internal string MemberName { get; private set; } public ValidationConfigurationBuilderMember AddValidator(Validator validator) { if (validator == null) { throw new ArgumentNullException("validator"); } return AddValidator(new SingletonValidatorData(validator)); } public ValidationConfigurationBuilderMember AddValidator(ValidatorData validatorData) { if (validatorData == null) { throw new ArgumentNullException("validatorData"); } var memberReference = this.GetOrCreateMemberReference(); // 'Name' is the default value when the validator has not been given a name. if (validatorData.Name == "Name") { // In that case we set the name to something more specific. validatorData.Name = typeof(TEntity).Name + "_" + this.BuilderRuleset.Ruleset.Name + "_" + validatorData.Type.Name; } // When that specific name already exist, we add a number to that name to ensure uniqueness. if (memberReference.Validators.Contains(validatorData.Name)) { validatorData.Name += "_" + memberReference.Validators.Count.ToString(); } memberReference.Validators.Add(validatorData); return this; } internal abstract ValidatedMemberReference GetOrCreateMemberReference(); } public class ValidationConfigurationBuilderProperty : ValidationConfigurationBuilderMember { internal ValidationConfigurationBuilderProperty( ValidationConfigurationBuilderRuleset builderRuleset, string propertyName) : base(builderRuleset, propertyName) { } internal override ValidatedMemberReference GetOrCreateMemberReference() { var properties = this.BuilderRuleset.Ruleset.Properties; var propertyReference = properties.Get(this.MemberName); if (propertyReference == null) { propertyReference = new ValidatedPropertyReference(this.MemberName); properties.Add(propertyReference); } return propertyReference; } } public class ValidationConfigurationBuilderField : ValidationConfigurationBuilderMember { internal ValidationConfigurationBuilderField( ValidationConfigurationBuilderRuleset builderRuleset, string fieldName) : base(builderRuleset, fieldName) { } internal override ValidatedMemberReference GetOrCreateMemberReference() { var fields = this.BuilderRuleset.Ruleset.Fields; var fieldReference = fields.Get(this.MemberName); if (fieldReference == null) { fieldReference = new ValidatedFieldReference(this.MemberName); fields.Add(fieldReference); } return fieldReference; } } internal class SingletonValidatorData : ValidatorData { private readonly Validator validator; public SingletonValidatorData(Validator validator) : base(typeof(TEntity).Name + "ValidatorData", typeof(TEntity)) { this.validator = validator; } protected override Validator DoCreateValidator(Type targetType) { return this.validator; } } [DebuggerDisplay("Range (LowerBound: {LowerBound}, LowerBoundType: {LowerBoundType}, UpperBound: {UpperBound}, UpperBoundType: {UpperBoundType})")] public class RangeData where T : IComparable { private T lowerBound; private RangeBoundaryType lowerBoundType; private bool lowerBoundTypeSet; private T upperBound; private RangeBoundaryType upperBoundType; private bool upperBoundTypeSet; public T LowerBound { get { return this.lowerBound; } set { this.lowerBound = value; if (!this.lowerBoundTypeSet) { this.lowerBoundType = RangeBoundaryType.Inclusive; } } } public RangeBoundaryType LowerBoundType { get { return this.lowerBoundType; } set { this.lowerBoundType = value; this.lowerBoundTypeSet = true; } } public T UpperBound { get { return this.upperBound; } set { this.upperBound = value; if (!this.upperBoundTypeSet) { this.upperBoundType = RangeBoundaryType.Inclusive; } } } public RangeBoundaryType UpperBoundType { get { return this.upperBoundType; } set { this.upperBoundType = value; this.upperBoundTypeSet = true; } } public bool Negated { get; set; } public virtual string MessageTemplate { get; set; } public virtual string MessageTemplateResourceName { get; set; } public virtual string MessageTemplateResourceTypeName { get; set; } public virtual string Tag { get; set; } internal RangeValidator CreateValidator() { return new RangeValidator(this.LowerBound, this.LowerBoundType, this.UpperBound, this.UpperBoundType, this.GetMessageTemplate(), this.Negated) { Tag = this.Tag, }; } internal string GetMessageTemplate() { if (!string.IsNullOrEmpty(this.MessageTemplate)) { return this.MessageTemplate; } Type messageTemplateResourceType = this.GetMessageTemplateResourceType(); if (messageTemplateResourceType != null) { return ResourceStringLoader.LoadString(messageTemplateResourceType.FullName, this.MessageTemplateResourceName, messageTemplateResourceType.Assembly); } return null; } private Type GetMessageTemplateResourceType() { if (!string.IsNullOrEmpty(this.MessageTemplateResourceTypeName)) { return Type.GetType(this.MessageTemplateResourceTypeName); } return null; } } 

使用此代码,您可以如下流利地定义配置:

 const string DefaultRuleset = "Default"; const string AlternativeRuleset = "Alternative"; var builder = new ValidationConfigurationBuilder(); builder.RegisterDefaultRulesetForAllTypes(DefaultRuleset); builder.ForType() .ForProperty(p => p.Age) .AddRangeValidator(new RangeData() { LowerBound = 18, MessageTemplate = "This is an adult system", }) .ForProperty(p => p.FirstName) .AddValidator(new NotNullValidator()) .AddValidator(new StringLengthValidatorData() { LowerBound = 1, LowerBoundType = RangeBoundaryType.Inclusive, UpperBound = 100, UpperBoundType = RangeBoundaryType.Inclusive }) .ForProperty(p => p.LastName) .AddValidator(new NotNullValidator()) .ForProperty(p => p.Friends) .AddValidator(new ObjectCollectionValidator()); builder.ForType().ForRuleset(AlternativeRuleset) .ForProperty(p => p.FirstName) .AddValidator(new NotNullValidator()) .AddValidator(new StringLengthValidatorData() { LowerBound = 1, LowerBoundType = RangeBoundaryType.Inclusive, UpperBound = 10, UpperBoundType = RangeBoundaryType.Inclusive }); 

ValidationConfigurationBuilder是一个IConfigurationSource ,您可以将它提供给ValidationFactory.CreateValidator实例。 这是一个例子:

 var validator = ValidationFactory.CreateValidator(builder); var instance = new Person(); var results = validator.Validate(instance); 

我遇到的一个可能的解决方案是使用AOP编程概念。 简而言之,例如在你的情况下,你用一些attibute标记“脆弱”代码,并在编译时检查typemember functionproperty ……是否处于你想要的状态。

像参考文献:

CSharpCornerArticle-2009 (旧但仍然很好)

PostSharp (可能是AOP市场上最好的工具)

Rolsyn (由MS提供的编译器服务。您可以编写自己的C#VB.NET代码的小解析器并将其注入CI环境中。

希望这可以帮助。

我遇到了类似的问题,所以我为Enterprise Libraryvalidation编写了一个包装器,它在运行时查找部署中的所有validation器并为我注册它们。 然后我很高兴地删除了我的XML配置。

我在这里写了一篇关于它的博客。