【问题标题】:Stack overflow when using Microsoft EnterpriseLibrary validation使用 Microsoft EnterpriseLibrary 验证时的堆栈溢出
【发布时间】:2009-02-23 16:28:35
【问题描述】:

我有两个班级:

[HasSelfValidation]
class Country : DomainObject
{
    [NotNullValidator]
    [StringLengthValidator(2, 10)]
    public string Name { get; set; }

    [ObjectCollectionValidator(typeof(Region))]
    public List<Region> Regions { get; set; }
}

和

[HasSelfValidation]
class Region : DomainObject
{
    [NotNullValidator]
    [ObjectValidator]
    public Country Country { get; set; }

    [NotNullValidator]
    [StringLengthValidator(2, 20)]
    public string Name { get; set; }
}

DomainObject 有方法的地方:

public virtual ValidationResults Validate()
{
    Validator validator = 
        ValidationFactory.CreateValidator(this.GetType());
    ValidationResults results = new ValidationResults();
    validator.Validate(this, results);
    return results;
}

我正在使用 Microsoft Enterprise Library 4.1 - 2008 年 10 月/.NET 3.5 SP1/Vista。

如果我为新创建的 Country 对象调用 Validate 且区域列表为 null,则会收到 StackOverflow 异常。如果我删除 Country.Regions 属性的 [ObjectCollectionValidator(typeof(Region))],一切正常。我猜链接国家 - 地区 - 国家是失败的原因。但是,我不想删除 Regions 集合的验证;从区域中删除 [ObjectValidator] 对我来说都不是一个选项。我可以做些什么来维护所有这些验证属性,没有 StackOverflow 异常?

谢谢,

卢锡安

【问题讨论】:

  • 这两个类的 SelfValidation 方法是什么?

标签: c# .net validation enterprise-library validation-application-bl


【解决方案1】:

前几天我遇到了同样的问题,并且能够通过实现一个自定义的验证器类来解决这个问题,该类的功能与 ObjectValidator 的功能非常相似,只是它延迟了对正在评估的属性的评估,直到实际的 DoValidate 方法,如果属性为 null,则不会继续构建验证器。

using System;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Validation.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Validation.Properties;

namespace Microsoft.Practices.EnterpriseLibrary.Validation.Validators
{
    /// <summary>
    /// Performs validation on objects by applying the validation rules specified for a supplied type at RUNTIME.
    /// This validator can be used to get past StackOverflowExceptions that can be thrown as a result of the design
    /// of the ObjectValidator attribute
    /// </summary>
    /// <seealso cref="ValidationFactory"/>
    public class RuntimeObjectValidator : Validator
    {
        private Type targetType;
        private string targetRuleset;

        /// <summary>
        /// <para>Initializes a new instance of the <see cref="RuntimeObjectValidator"/> for a target type.</para>
        /// </summary>
        /// <param name="targetType">The target type</param>
        /// <remarks>
        /// The default ruleset for <paramref name="targetType"/> will be used.
        /// </remarks>
        /// <exception cref="ArgumentNullException">when <paramref name="targetType"/> is <see langword="null"/>.</exception>
        public RuntimeObjectValidator(Type targetType)
            : this(targetType, string.Empty)
        { }

        /// <summary>
        /// <para>Initializes a new instance of the <see cref="RuntimeObjectValidator"/> for a target type
        /// using the supplied ruleset.</para>
        /// </summary>
        /// <param name="targetType">The target type</param>
        /// <param name="targetRuleset">The ruleset to use.</param>
        /// <exception cref="ArgumentNullException">when <paramref name="targetType"/> is <see langword="null"/>.</exception>
        /// <exception cref="ArgumentNullException">when <paramref name="targetRuleset"/> is <see langword="null"/>.</exception>
        public RuntimeObjectValidator(Type targetType, string targetRuleset)
            : base(null, null)
        {
            if (targetType == null)
            {
                throw new ArgumentNullException("targetType");
            }
            if (targetRuleset == null)
            {
                throw new ArgumentNullException("targetRuleset");
            }

            this.targetType = targetType;
            this.targetRuleset = targetRuleset;
        }

        /// <summary>
        /// Validates by applying the validation rules for the target type specified for the receiver.
        /// </summary>
        /// <param name="objectToValidate">The object to validate.</param>
        /// <param name="currentTarget">The object on the behalf of which the validation is performed.</param>
        /// <param name="key">The key that identifies the source of <paramref name="objectToValidate"/>.</param>
        /// <param name="validationResults">The validation results to which the outcome of the validation should be stored.</param>
        /// <remarks>
        /// If <paramref name="objectToValidate"/> is <see langword="null"/> validation is ignored.
        /// <para/>
        /// A referece to an instance of a type not compatible with the configured target type
        /// causes a validation failure.
        /// </remarks>
        protected internal override void DoValidate(object objectToValidate,
            object currentTarget,
            string key,
            ValidationResults validationResults)
        {
            if (objectToValidate != null)
            {
                if (this.targetType.IsAssignableFrom(objectToValidate.GetType()))
                {
                    validationResults.AddAllResults(
                        ValidationFactory.CreateValidator(objectToValidate.GetType()).Validate(objectToValidate));
                }
                else
                {
                    // unlikely
                    this.LogValidationResult(validationResults, Resources.ObjectValidatorInvalidTargetType, currentTarget, key);
                }
            }
        }

        /// <summary>
        /// Gets the message template to use when logging results no message is supplied.
        /// </summary>
        protected override string DefaultMessageTemplate
        {
            get { return null; }
        }

        #region test only properties

        internal Type TargetType
        {
            get { return this.targetType; }
        }

        internal string TargetRuleset
        {
            get { return this.targetRuleset; }
        }

        #endregion
    }
}

当然,您还需要创建一个 RuntimeObjectValidatorAttribute 类,以便您可以这样做:

public class AClassThatReferencesItself
    {
        private AClassThatReferencesItself _other;

        private string myString;

        [NotNullValidator]
        public string MyString
        {
            get { return myString; }
            set { myString = value; }
        }


        [RuntimeObjectValidator]
        [NotNullValidator]
        public AClassThatReferencesItself Other
        {
            get { return _other; }
            set { _other = value; }
        }

    }

【讨论】:

    【解决方案2】:

    这个 bug 是 codeplex 上 EntLib 的 in issue tracker。而且我不确定它是否可以轻松修复。而且我没有找到任何好的解决方法:(。

    【讨论】:

      【解决方案3】:

      诀窍实际上是不使用任何ObjectValidator 和ObjectCollectionValidator 属性。您可以自行验证所有对象。这并不总是可行的,但在 O/RM 场景的上下文中尤其有效,其中 O/RM 框架知道哪些实体是新的或已更改的。

      看看这个例子,在使用实体框架将更改提交到数据库之前触发验证:

      public partial class NorthwindEntities
      {
          partial void OnContextCreated()
          {
              // Adding validation support when saving.
              this.SavingChanges += (sender, e) =>
              {
                  // Throws an exception when invalid.
                  EntityValidator.Validate(
                      this.GetChangedEntities());
              }
          }
      
          private IEnumerable<object> GetChangedEntities()
          {
              const EntityState AddedAndModified =
                  EntityState.Added | EntityState.Modified;
      
              var entries = this.ObjectStateManager
                  .GetObjectStateEntries(AddedAndModified);
      
              return
                  from entry in entries
                  where entry.Entity != null
                  select entry.Entity;
          }
      }
      

      代码挂钩到ObjectContext 的SavingChanges 事件。

      EntityValidator 是一个自定义类,允许使用验证应用程序块验证一组对象。当验证失败时,它会抛出一个自定义的 ValidationException 包装 ValidationResults 集合。

      static class EntityValidator
      {
          public static void Validate(IEnumerable<object> entities)
          {
              ValidationResults[] invalidResults = (
                  from entity in entities
                  let type = entity.GetType()
                  let validator = ValidationFactory.CreateValidator(type)
                  let results = validator.Validate(entity)
                  where !results.IsValid
                  select results).ToArray();
      
              // Throw an exception when there are invalid results.
              if (invalidResults.Length > 0)
              {
                  throw new ValidationException(invalidResults);
              }
          }
      }
      

      更多信息here。

      我希望这会有所帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-01-17
        • 2017-08-02
        • 2019-05-18
        • 1970-01-01
        • 2015-03-28
        • 2013-10-30
        • 1970-01-01
        相关资源
        最近更新 更多