获取错误:InvalidOperationException

时间:2015-03-24 15:57:23

标签: c# sql-server wpf fluentvalidation

我在使用下面提到的代码时收到错误:InvalidOperationException:

using (MunimPlusContext context = new MunimPlusContext())
{
    var dbGroup = context.GroupSet
                         .Where(x => x.GroupName.ToLower() == groupName.ToLower())
                         .SingleOrDefault();

    if (dbGroup == null)
        return true;
    else
        return dbGroup.GroupId == group.GroupId;
}

错误提供的详细信息是:

  

超时已过期。在获得a之前经过了超时时间   从游泳池连接。这可能是因为所有人都集中了   正在使用连接并达到最大池大小。

我尝试过:

  1. 我使用下面提到的代码行来克服这个错误:

    context.Database.Connection.Open();

  2. 我还使用了SQL Profiler,它按如下方式触发查询,但我不理解生成的SQL:

    exec sp_executesql N'SELECT TOP(2)     [Extent1]。[GroupId] AS [GroupId],     [Extent1]。[GroupName] AS [GroupName],     [Extent1]。[Alias] AS [Alias],     [Extent1]。[ParentId] AS [ParentId],     [Extent1]。[IsSystemGroup] AS [IsSystemGroup],     [Extent1]。[NatureOfGroupId] AS [NatureOfGroupId],     [Extent1]。[EffectId] AS [EffectId],     [Extent1]。[BankDetailsVisibility] AS [BankDetailsVisibility],     [Extent1]。[CreditLimitsVisibility] AS [CreditLimitsVisibility],     [Extent1]。[GeneralDetailsVisibility] AS [GeneralDetailsVisibility],     [Extent1]。[ContactDetailsVisibility] AS [ContactDetailsVisibility],     [Extent1]。[TaxInformationVisibility] AS [TaxInformationVisibility]     FROM [dbo]。[Group] AS [Extent1]     WHERE((LOWER([Extent1]。[GroupName]))=(LOWER(@ p__linq__0)))OR((LOWER([Extent1]。[GroupName])IS NULL)AND(LOWER(@ p__linq__0)IS NULL)) ',N'@ p__linq__0 nvarchar(4000)',@ p__linq__0 = N'Primary' 去

  3. 当我收到此错误时:

    当我尝试使用FluentValidation检查数据库中的重复项时,如下所示,我收到此错误:

    RuleFor(obj => obj.GroupName).Must(UniqueName)
                                 .WithMessage("Group with same name already exists. Please choose a different Group name");
    

    以下是UniqueName方法,它是错误的来源:

    private bool UniqueName(Group group, string groupName)
    {
        using (MunimPlusContext context = new MunimPlusContext())
        {
            var dbGroup = context.GroupSet
                                 .Where(x => x.GroupName.ToLower() == groupName.ToLower())
                                 .SingleOrDefault();
    
            if (dbGroup == null)
                return true;
            else
                return dbGroup.GroupId == group.GroupId;
        }
    }
    

    更新

    这是完整的堆栈跟踪:

      

    在   System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(的DbConnection   owningConnection,TaskCompletionSource 1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection) at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource 1次重试,DbConnectionOptions userOptions)at at   System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(的DbConnection   outerConnection,DbConnectionFactory connectionFactory,   TaskCompletionSource 1 retry, DbConnectionOptions userOptions) at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource 1   重试)   System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource 1 retry) at System.Data.SqlClient.SqlConnection.Open() at MunimPlus.Entities.Group.GroupValidator.UniqueName(Group group, String groupName) in H:\Work\Trial\New\MunimPlus\MunimPlusSolution\MunimPlus.Entities\Group.cs:line 274 at FluentValidation.DefaultValidatorExtensions.<>c__DisplayClass4 2.b__3(T   x,TProperty val,PropertyValidatorContext propertyValidatorContext)   在   C:\项目\ FluentValidation \ SRC \ FluentValidation \ DefaultValidatorExtensions.cs:线   219在   FluentValidation.DefaultValidatorExtensions。&lt;&gt; c__DisplayClass7 2.<Must>b__6(Object instance, Object property, PropertyValidatorContext propertyValidatorContext) in c:\Projects\FluentValidation\src\FluentValidation\DefaultValidatorExtensions.cs:line 235 at FluentValidation.Validators.PredicateValidator.IsValid(PropertyValidatorContext context) in c:\Projects\FluentValidation\src\FluentValidation\Validators\PredicateValidator.cs:line 37 at FluentValidation.Validators.PropertyValidator.Validate(PropertyValidatorContext context) in c:\Projects\FluentValidation\src\FluentValidation\Validators\PropertyValidator.cs:line 71 at FluentValidation.Internal.PropertyRule.InvokePropertyValidator(ValidationContext context, IPropertyValidator validator, String propertyName) in c:\Projects\FluentValidation\src\FluentValidation\Internal\PropertyRule.cs:line 346 at FluentValidation.Internal.PropertyRule.<Validate>d__10.MoveNext() in c:\Projects\FluentValidation\src\FluentValidation\Internal\PropertyRule.cs:line 234 at System.Linq.Enumerable.<SelectManyIterator>d__14 2.MoveNext()at   System.Collections.Generic.List 1..ctor(IEnumerable 1集合)
      at System.Linq.Enumerable.ToList [TSource](IEnumerable 1 source) at FluentValidation.AbstractValidator 1.Validate(ValidationContext 1 context) in c:\Projects\FluentValidation\src\FluentValidation\AbstractValidator.cs:line 113 at FluentValidation.AbstractValidator 1.Validate(T instance)in   C:\项目\ FluentValidation \ SRC \ FluentValidation \ AbstractValidator.cs:线   94点   FluentValidation.AbstractValidator 1.FluentValidation.IValidator.Validate(Object instance) in c:\Projects\FluentValidation\src\FluentValidation\AbstractValidator.cs:line 55 at Core.Common.Core.EntityBase.Validate() in H:\Work\Trial\New\Core\Core.Common\Core\EntityBase.cs:line 206 at Core.Common.Core.EntityBase..ctor() in H:\Work\Trial\New\Core\Core.Common\Core\EntityBase.cs:line 25 at MunimPlus.Entities.Group..ctor() at lambda_method(Closure , Shaper ) at System.Data.Entity.Core.Common.Internal.Materialization.Shaper.HandleEntityAppendOnly[TEntity](Func 2   constructEntityDelegate,EntityKey entityKey,EntitySet entitySet)
      在lambda_method(Closure,Shaper)at   System.Data.Entity.Core.Common.Internal.Materialization.Coordinator`1.ReadNextElement(整形   整形器)

    UPDATE1:

    如果我不使用context.Database.Connection.Open(),那么我会收到另一个错误:

      

    基础提供商无法开放。

    但是在调用数据库之前,我还有一些其他调用工作正常。

    如果我删除此验证,那么我的项目也可以正常工作。

    我还想在这里显示我的连接字符串:

    <connectionStrings>
      <add name="MunimPlus" 
           connectionString="data source=.\SQLEXPRESS;Initial Catalog=Max;Integrated Security=SSPI" 
           providerName="System.Data.SqlClient" />
    </connectionStrings>
    

    更新2:

    对问题有一些暗示但没有解决方案。

    我有一个名为EntityBase的基类。我的所有实体都继承自EntityBase。所以我的Group类看起来像:

    public class Group : EntityBase
    {
    
        Fields.....
    
        Properties....
    
        class GroupValidator : AbstractValidator<T>
        {
            public GroupValidator()
            {
                RuleFor(obj => obj.GroupName).NotEmpty().WithMessage("Group name cannot be empty.");
                RuleFor(obj => obj.GroupName).Must(UniqueName).WithMessage("Group with same name already exists. Please choose a different Group name");
                RuleFor(obj => obj.ParentId).NotNull().WithMessage("Please select the group under which this group will appear")
                                            .GreaterThan(0).WithMessage("Please select a valid/existing group name");
            }
    
            private bool UniqueName(Group group, string groupName)
            {
                if (groupName == null)
                    groupName = "";
    
                using (MunimPlusContext context = new MunimPlusContext())
                {
                    Group dbGroup = context.GroupSet.FirstOrDefault(x => x.GroupName.ToLower() == groupName.ToLower());
    
                    if (dbGroup == null)
                        return true;
                    else
                        return dbGroup.GroupId == group.GroupId;
                }
            }
        }
    
        protected override IValidator GetValidator()
        {
            return new GroupValidator();
        }
    
    }
    

    查看最后一个名为GetValidator的方法,它是EntityBase类中定义的虚方法的重写版本。

    现在,EntityBase类的一部分看起来像:

    public abstract class EntityBase
    {
    
        public EntityBase()
        {
            _Validator = GetValidator();
            Validate();
        }
    
        protected IValidator _Validator = null;
    
        protected IEnumerable<ValidationFailure> _ValidationErrors = null;
    
        protected virtual IValidator GetValidator()
        {
            return null;
        }
    
        public IEnumerable<ValidationFailure> ValidationErrors
        {
            get { return _ValidationErrors; }
            set { }
        }
    
        public void Validate()
        {
            if (_Validator != null)
            {
                ValidationResult results = _Validator.Validate(this);
                _ValidationErrors = results.Errors;
            }
        }
    
        public virtual bool IsValid
        {
            get
            {
                if (_ValidationErrors != null && _ValidationErrors.Count() > 0)
                    return false;
                else
                    return true;
            }
        }
    }   
    

    现在在CarValidator类的UniqueName方法的行

    Group dbGroup = context.GroupSet.FirstOrDefault(x => x.GroupName.ToLower() == groupName.ToLower());
    

    对于每个组,由于EntityBase类而创建并验证了新实例。因此,游标在using(MunimPlusContext context = new MunimPlusContext)之间运行但从不关闭连接,因为它越来越深地创建组的新实例,因此达到了数据库中的最大连接。因此我得到连接池问题。

    当我将Max Pool Size增加到999时,我得到另一个错误StackOverFlowException。

    演示项目再现问题:

    https://drive.google.com/file/d/0B5WyqSALui0bM252VXdveVVMMzQ/view?usp=sharing

1 个答案:

答案 0 :(得分:1)

所以你现在拥有的是无限递归问题。所以它从这样开始:

  1. 创建一个调用构造函数的新组(在EntityBase中调用基础构造函数)。
  2. EntityBase构造函数获取验证程序,然后调用其Validate方法,最终调用UniqueName
  3. context.GroupSet.FirstOrDefault行将创建一个新的Group对象。这将使你再次回到第一步。
  4. 这个制作对象的循环永远不会停止,直到你耗尽堆栈,这会导致你得到的堆栈溢出异常(在你遇到堆栈溢出之前达到连接池限制之前,连接池问题掩盖了真正的问题这里)。你可以试试几件事:

    首先,将context.GroupSet.FirstOrDefault更改为使用Any而不是FirstOrDefault。这样,第3步永远不会创建一个对象,也永远不会将您送回第1步。虽然这可以解决问题,但如果您需要在其他地方执行类似的操作,它可以轻松弹回。

    第二个(并且到目前为止,更好)是不要在构造函数中调用Validate。通常,您应该尝试在对象持久保存到数据库之前验证它,而不是在创建时(例如,验证一个全新的,空的对象在我填写它时不会对我有用。之后。我需要在保存之前进行验证,以确保我不会通过将某些数据组合放入对象来违反某些约束。)在我完成的项目中,我们做了类似的事情:

    public class EntityBase
    {
        //...
        public void Save()
        {
            //Validate here and check for errors
            //If errors exist, throw an exception
            //If no errors, persist to database.
        }
    }
    

    显然,您必须弄清楚如何在项目中执行此操作。也许是通过存储库或服务,但在保存而不是创建之前,你最好不要进行验​​证。

    (注意:我没有在您分享的代码中尝试任何此类代码,因为我无法运行我在互联网上传递的代码。)