使用Microsoft EnterpriseLibrary验证时堆栈溢出

时间:2009-02-23 16:28:36

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

我有两个班级:

[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异常吗?

谢谢,

卢西恩

3 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分: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 :(得分:1)

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

在此示例中查找实例,其中在使用Entity Framework将更改提交到数据库之前触发验证:

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的{​​{1}}事件。

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

ValidationResults

更多信息here

我希望这会有所帮助。