Getting an Error: InvalidOperationException

Getting an Error: InvalidOperationException

我在使用下面提到的代码时收到错误: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;
}

错误提供的详细信息是:

Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.

我尝试过的:

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

    context.Database.Connection.Open();

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

    exec sp_executesqlN'SELECT TOP (2) [Extent1].[GroupId] AS [GroupId], [Extent1].[GroupName] AS [GroupName], [Extent1].[别名] AS [别名], [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] 其中 ((LOWER([Extent1].[GroupName])) = (LOWER(@p__linq__0))) 或 ((LOWER([Extent1].[GroupName]) 为 NULL) AND (LOWER(@p__linq__0) IS NULL))',N'@p__linq__0 nvarchar(4000)',@p__linq__0=N'Primary' 去

当我收到此错误时:

当我尝试使用 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;
    }
}

更新:

这是完整的堆栈跟踪:

at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection) at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource1 retry, DbConnectionOptions userOptions) at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource1 retry, DbConnectionOptions userOptions) at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource1 retry) at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource1 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__DisplayClass42.b__3(T x, TProperty val, PropertyValidatorContext propertyValidatorContext) in c:\Projects\FluentValidation\src\FluentValidation\DefaultValidatorExtensions.cs:line 219 at FluentValidation.DefaultValidatorExtensions.<>c__DisplayClass72.<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__142.MoveNext() at System.Collections.Generic.List1..ctor(IEnumerable1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source) at FluentValidation.AbstractValidator1.Validate(ValidationContext1 context) in c:\Projects\FluentValidation\src\FluentValidation\AbstractValidator.cs:line 113 at FluentValidation.AbstractValidator1.Validate(T instance) in c:\Projects\FluentValidation\src\FluentValidation\AbstractValidator.cs:line 94 at FluentValidation.AbstractValidator1.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](Func2 constructEntityDelegate, EntityKey entityKey, EntitySet entitySet)
at lambda_method(Closure , Shaper ) at System.Data.Entity.Core.Common.Internal.Materialization.Coordinator`1.ReadNextElement(Shaper shaper)

更新 1:

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

Underlying Provider failed to Open.

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

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

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

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

更新 2:

得到一些关于问题但没有解决方案的提示。

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

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 中定义的虚方法的重写版本 class。

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

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 class 的 UniqueName 方法行中

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

由于 EntityBase class,每个组都会创建并验证一个新实例。因此,游标运行在 using(MunimPlusContext context = new MunimPlusContext) 上,但随着它越来越深地创建组的新实例,它永远不会关闭连接,结果达到了数据库中的最大连接数。因此我遇到了连接池问题。

当我将最大池大小增加到 999 时,出现另一个错误 WhosebugException。

重现问题的演示项目:

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

所以你现在遇到的是一个无限递归问题。所以它开始是这样的:

  1. 创建一个调用构造函数的新组(调用 EntityBase 中的基本构造函数)。
  2. EntityBase 构造函数获取一个验证器,然后调用它的 Validate 方法,最终调用 UniqueName.
  3. context.GroupSet.FirstOrDefault 将创建一个新的 Group 对象。这会让您再次回到第一步。

这个创建对象的循环将永远不会停止,直到你耗尽堆栈,这会导致你得到的堆栈溢出异常(之前你曾经在你得到堆栈溢出之前达到连接池限制并且连接池问题掩盖了真正的问题在这里)。您可以尝试几件事:

首先,将 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.
    }
}

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

(注意:我没有在您共享的代码中尝试任何这些,因为我无法使用我在互联网上获得的 运行 代码。)