【问题标题】:Getting an Error: InvalidOperationException出现错误:InvalidOperationException
【发布时间】:2015-03-24 15:57:23
【问题描述】:

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

错误提供的详细信息是:

超时。在获得一个之前的超时时间 来自池的连接。这可能是因为所有汇集的 正在使用连接并且已达到最大池大小。

我尝试过的:

  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].[别名] AS [别名], [Extent1].[ParentId] AS [ParentId], [Extent1].[IsSystemGroup] AS [IsSystemGroup], [Extent1].[NatureOfGroupId] AS [NatureOfGroupId], [Extent1].[EffectId] 作为 [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))) 或 ((LOWER([Extent1].[GroupName]) 为 NULL) AND (LOWER(@p__linq__0) 为 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;
    }
}

更新:

这是完整的堆栈跟踪:

在 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 重试,DbConnectionOptions userOptions)在 System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection 外部连接,DbConnectionFactory 连接工厂, 任务完成源1 retry, DbConnectionOptions userOptions) at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource1 重试)在 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) 在 c:\Projects\FluentValidation\src\FluentValidation\DefaultValidatorExtensions.cs:line 219 在 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() 在 System.Collections.Generic.List1..ctor(IEnumerable1 个集合)
在 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 实例) c:\Projects\FluentValidation\src\FluentValidation\AbstractValidator.cs:line 94 在 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 构造EntityDelegate, EntityKey entityKey, EntitySet entitySet)
在 lambda_method(Closure, Shaper) 在 System.Data.Entity.Core.Common.Internal.Materialization.Coordinator`1.ReadNextElement(Shaper 整形器)

更新1:

如果我不使用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

【问题讨论】:

  • GroupSet 有多大?将ToLower() 放在Where 中会导致查询中出现小写,这可能会导致大量性能问题。
  • 您可以完全删除Wherecontext.GroupSet.SingleOrDefault(x =&gt; x.GroupName.ToLower() == groupName.ToLower()) 您也可以将 groupName.ToLower() 放在本地。否则每次检查都会调用.ToLower();这是没有意义的。
  • @DionV。 GroupSet 只包含 28 条记录......
  • @diemaus 我已经尝试了您建议的代码,但得到了相同的结果。另外,我已经用 Stack Trace 更新了我的问题。你能看一下吗?
  • @DionV。我已经用堆栈跟踪更新了我的问题。你能检查一下吗?

标签: c# sql-server wpf fluentvalidation


【解决方案1】:

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

  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.
    }
}

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

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

【讨论】:

  • 谢谢,我的问题解决了。我使用 Any 而不是 FirstOrDefault() 并且效果很好。
  • 正如您所说,在构造函数中调用 Validate 不是一个好主意,我只是在构造函数中注释掉了 Validate 方法的调用。现在,当我调用 Validate() 时,在我的保存方法中,我收到了所有错误消息,但这些错误消息不会像我在构造函数中调用 validate 时那样显示。你能建议我对我的基类进行一些更改吗????
  • @Vishal 我不太清楚您所说的“我收到所有错误消息,但这些错误消息没有像我在构造函数中调用验证时显示的那样显示。”由于无法看到您做了什么以及错误是什么,我所能做的就是猜测。您可能应该提出一个新问题,并包含一个指向该问题的链接。这样,更多的人会看到这个新问题并且也可以提供帮助。此外,一旦您提出新问题,请在此处使用链接或网址发表评论,我会看到并查看。
  • 感谢您的建议。我已经按照你的建议发布了一个新问题,你可以在这里找到它:stackoverflow.com/questions/29349591/…
猜你喜欢
  • 1970-01-01
  • 2023-02-21
  • 2012-01-30
  • 2016-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多