Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/272.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# EF核心和大流量导致达到最大池大小错误_C#_Asp.net_Asp.net Web Api_Entity Framework Core_.net Core - Fatal编程技术网

C# EF核心和大流量导致达到最大池大小错误

C# EF核心和大流量导致达到最大池大小错误,c#,asp.net,asp.net-web-api,entity-framework-core,.net-core,C#,Asp.net,Asp.net Web Api,Entity Framework Core,.net Core,我们使用ASP.NET实体框架核心在Web API应用程序中查询MSSQL数据库。有时,当我们有大流量时,查询数据库时会出现以下错误: 超时已过期。从池中获取连接之前经过的超时时间。发生这种情况的原因可能是所有池连接都在使用中,并且已达到最大池大小 我想知道我们使用DbContext和查询的模式是否正确,或者我是否缺少一些use/dispose模式,错误是由一些内存泄漏引起的(经过一些研究,我读了之后,我不应该使用use,因为生命周期是由框架管理的)。我在跟踪 我的连接字符串: "myConne

我们使用ASP.NET实体框架核心在Web API应用程序中查询MSSQL数据库。有时,当我们有大流量时,查询数据库时会出现以下错误:

超时已过期。从池中获取连接之前经过的超时时间。发生这种情况的原因可能是所有池连接都在使用中,并且已达到最大池大小

我想知道我们使用
DbContext
和查询的模式是否正确,或者我是否缺少一些use/dispose模式,错误是由一些内存泄漏引起的(经过一些研究,我读了之后,我不应该使用use,因为生命周期是由框架管理的)。我在跟踪

我的连接字符串:

"myConnection": "Server=xxx;Database=xxx;user id=xxx;password=xxx;Max Pool Size=200;Timeout=200;"
我的Startup.cs

public void ConfigureServices(IServiceCollection services)
    {
    .....
    // scoped context            
    services.AddDbContext<MyDbContext>(
            options => options.UseSqlServer(this.Configuration.GetConnectionString("myConnection")));
    }
我应该使用类似于:

using (var context = this.Context)
        {
            return this.Context.tRealty.Where(x => x.id == id).FirstOrDefault();
        }

但我认为,当我使用
DbContext

的依赖项注入时,这是一种糟糕的模式。您可以在startup.cs中设置DbContext的生存期,看看这是否有帮助:

    services.AddDbContext<MyDbContext>(options => options
                                       .UseSqlServer(connection), ServiceLifetime.Scoped);
编辑:作为上述问题的作者,上述代码在EF Core中是不可能的

可以使用显式事务找到解决方法:

    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();

        using (var transaction = connection.BeginTransaction())
        {
           // transaction.Commit();
           // transaction.Rollback();
        }
    }
我还没有测试过这个

编辑2:另一个未经测试的代码段,您可以在其中执行命令以设置隔离级别:

                using (var c1= new SqlConnection(connectionString))
                {
                    c1.Open();
                    // set isolation level
                    Exec(c1, "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
                    Exec(c1, "BEGIN TRANSACTION;");
                    // do your magic here
                }
与执行官:

        private static void Exec(SqlConnection c, string s)
        {
            using (var m = c.CreateCommand())
            {
                m.CommandText = s;
                m.ExecuteNonQuery();
            }
        }
编辑3:根据线程,从.NET Core 1.2版起将支持事务

@mukundabrt这是由dotnet/corefx#2949跟踪的。注意 TransactionScope已被移植到.NET Core,但将仅为 可在.NET Core 1.2中获得


我认为这个问题是由将对象从数据库上下文查询存储到内存缓存引起的。我有一个对数据库上下文的大型LINQ查询,其中包含一些其他子查询。我在主查询结束时调用了
FirstOrDefault()
,但在子查询内部调用了。控制器对它很好,默认情况下它实现了查询

 return this.Context.tRealty.AsNoTracking().Where(
                x => x.Id == id && x.RealtyProcess == RealtyProcess.Visible).Select(
                s => new
                { .....

// subquery
videos = s.TVideo.Where(video => video.RealtyId == id && video.IsPublicOnYouTube).
                        Select(video => video.YouTubeId).ToList()), // missing ToList()
.....
 }).FirstOrDefault();
还有一个问题——子查询在存储到内存缓存中时保持了与数据库上下文的连接。当我实现Redis分布式缓存时,它首先出现了一些奇怪的错误。当我将
ToList()
FirstOrDefault()
写入所有子查询时,它会有所帮助,因为分布式缓存需要物化对象


现在,我已经显式地实现了我的所有查询,并且我没有得到“达到最大池大小”错误。因此,在将对象从数据库上下文查询存储到内存缓存时,必须小心。需要具体化所有查询,以避免将连接保留在内存中的某个位置。

我正在添加一个替代答案,以防任何人以稍微不同的根本原因到达这里,就像我的.NET Core MVC应用程序一样

在我的场景中,由于在同一控制器中混合使用
async
/
wait
Task.Result
,应用程序产生这些“超时过期…达到最大池大小”错误

我这样做是为了通过调用构造函数中的某个异步方法来设置属性,从而尝试重用代码。由于构造函数不允许异步调用,我被迫使用
Task.Result
。然而,我使用
async Task
方法在同一控制器内等待
数据库调用。我们聘请了Microsoft支持人员,一位工程师帮助解释了发生这种情况的原因:

看起来我们正在对内部的异步方法进行阻塞调用 […]构造函数

所以,基本上,在上面的调用中出现了一些问题 突出显示了async方法,因此列出了所有线程 上面的代码被阻塞了

查看正在执行相同操作并被阻止的线程:

85.71%的螺纹堵塞(174个螺纹)

我们应该避免混合使用异步代码和阻塞代码。混合异步和 阻塞代码会导致死锁、更复杂的错误处理和 上下文线程的意外阻塞

行动计划 请让您的应用程序团队重新访问上述方法的应用程序代码,以了解发生了什么 错

另外,如果您能将应用程序逻辑更新为 不要混合使用异步代码和阻塞代码。您可以使用等待任务而不是 Task.Wait或Task.Result

因此,在我们的例子中,我将
Task.Result
从构造函数中取出,并将其移动到一个私有
async
方法中,在那里我们可以
等待它。然后,由于每次使用控制器时我只希望它运行一次任务,因此我将结果存储到该本地属性,并且仅当属性值为
null
时,才从该方法中运行任务

在我的辩护中,我认为如果混合异步代码和阻塞代码的问题如此严重,编译器至少会发出警告。然而,事后看来,这对我来说已经足够明显了


希望这对某人有所帮助…

您似乎已经回答了自己的问题。因此,当我查询通过依赖项注入添加的dbcontext时,我应该使用use…DI容器应该负责处理上下文,因此没有。默认情况下,dbcontext的生存期是限定范围的(根据AddDbContext方法的定义)。。。使用
.AsNoTracking()
是个好主意,因为我的api是只读的,谢谢。但我认为问题出在连接关闭不好的地方,因为notracking应该进行更快的查询。在EF Core中,我想我不能使用
TransactionScope
,因为它目前不受支持。您的超时是发生在连接级别还是查询级别?您还可以在特定情况下尝试设置Database.CommandTimeout属性。在使用某些LINQ查询时出错。我在我的连接字符串中设置了
TimeOut=200
,我认为这对于easy get query来说是一个很大的值(我们的查询很快,直到所有连接都被使用,然后出错)
        private static void Exec(SqlConnection c, string s)
        {
            using (var m = c.CreateCommand())
            {
                m.CommandText = s;
                m.ExecuteNonQuery();
            }
        }
 return this.Context.tRealty.AsNoTracking().Where(
                x => x.Id == id && x.RealtyProcess == RealtyProcess.Visible).Select(
                s => new
                { .....

// subquery
videos = s.TVideo.Where(video => video.RealtyId == id && video.IsPublicOnYouTube).
                        Select(video => video.YouTubeId).ToList()), // missing ToList()
.....
 }).FirstOrDefault();