【问题标题】:Custom connection opening/closing for DbContextDbContext 的自定义连接打开/关闭
【发布时间】:2020-03-23 09:17:38
【问题描述】:

默认情况下,EF Core 会为每个查询打开和关闭一个 DbConnection,除非您传入一个已经打开的连接。

我有lots of small queries,所以不是每次都打开和关闭一个连接,我想一次保持连接打开五秒钟,同时为每个查询/命令重用该连接。 (上面链接的问题的解决方案在 DBContext 的整个生命周期中保持连接打开。)

抛开锁定/并发问题,我可以在DbContext 中的哪里注入自定义连接解析/打开逻辑?类似的东西

before executing query:
   if connection is not open
      open
      set timer to fire close request in five seconds
   take lock on connection (to prevent closing)
      execute query
   release lock

【问题讨论】:

  • 我认为底层数据库驱动程序通常提供连接池,它应该能够重用连接。另请查看 EF 核心中的上下文池:neelbhatt.com/2018/02/27/…
  • 绝对有一种简单的方法可以使用 EF Core 控制连接池 - 假设这就是您所追求的。 (或者您是否希望每个查询有不同的 连接?)
  • 抱歉,原来的问题描述有误。我的意思是让连接 open 用于基于计时器的多个查询,但不是在上下文的整个生命周期内。
  • 不知道能不能用DBConnecitonInterceptor...
  • 检查您的编辑,在我看来,您可以从带有内部计时器的Semaphore 中受益。如果我们考虑一下,它与连接池非常相似。你考虑过连接池吗?

标签: c# entity-framework-core database-connection ef-core-3.1


【解决方案1】:

这是进行交易的标准方式。 您可以组合多个查询。

using (var context = new SchoolContext())
{
    var std = new Student()
    {
        FirstName = "Bill",
        LastName = "Gates"
    };
    context.Students.Add(std);

    // or
    // context.Add<Student>(std);

    context.SaveChanges();

   std = context.Students.First<Student>(); 
    std.FirstName = "Steve";
    context.SaveChanges();
}

ef 核心可以使用相同的连接或不同的连接或基于连接池。 Ef核心有连接和断开的事务模式。我想这可以适合你。 在断开连接的情况下保存数据与在连接的情况下略有不同。在断开连接的情况下,DbContext 不知道断开连接的实体,因为在当前 DbContext 实例的范围之外添加或修改了实体。因此,您需要将断开连接的实体附加到具有适当 EntityState 的上下文中,以便对数据库执行 CUD(创建、更新、删除)操作。

下图说明了断开场景下的CUD操作:

根据上图,断开连接的实体(未被 DbContext 跟踪的实体)需要使用适当的 EntityState 附加到 DbContext。例如,新实体的已添加状态,已编辑实体的已修改状态和已删除实体的已删除状态,这将在调用 SaveChanges() 方法时导致数据库中的 INSERT、UPDATE 或 DELETE 命令。

在断开连接的情况下,必须执行以下步骤才能使用 Entity Framework Core 在数据库表中插入、更新或删除记录:

使用适当的 EntityState 将实体附加到 DbContext,例如添加、修改或删除 调用 SaveChanges() 方法 以下示例演示了使用上述步骤将新记录插入数据库:

//Disconnected entity
var std = new Student(){ Name = "Bill" };

using (var context = new SchoolContext())
{
    //1. Attach an entity to context with Added EntityState
    context.Add<Student>(std);

    //or the followings are also valid
    // context.Students.Add(std);
    // context.Entry<Student>(std).State = EntityState.Added;
    // context.Attach<Student>(std);

    //2. Calling SaveChanges to insert a new record into Students table
    context.SaveChanges();
}

在上面的示例中,std 是 Student 实体的断开连接的实例。 context.Add() 方法将 Student 实体附加到具有已添加状态的上下文。 SaveChanges() 方法构建并执行以下 INSERT 语句:

exec sp_executesql N'SET NOCOUNT ON; https://www.entityframeworktutorial.net/efcore/saving-data-in-disconnected-scenario-in-ef-core.aspx 这些是重要的方法。

public DbContext(DbConnection existingConnection, bool contextOwnsConnection)
public DbContext(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection)

EF6 和未来版本中的行为 对于 EF6 和未来版本,我们采取的方法是,如果调用代码选择通过调用 context.Database.Connection.Open() 打开连接,那么它有充分的理由这样做,并且框架将假定它想要控制打开和关闭连接,并且不会再自动关闭连接。

注意

这可能会导致连接长时间打开,因此请小心使用。

我们还更新了代码,以便 ObjectContext.Connection.State 现在可以正确跟踪底层连接的状态。

  using System;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Core.EntityClient;
using System.Data.Entity.Infrastructure;

namespace ConnectionManagementExamples
{
    internal class DatabaseOpenConnectionBehaviorEF6
    {
        public static void DatabaseOpenConnectionBehavior()
        {
            using (var context = new BloggingContext())
            {
                // At this point the underlying store connection is closed

                context.Database.Connection.Open();

                // Now the underlying store connection is open and the
                // ObjectContext.Connection.State correctly reports open too

                var blog = new Blog { /* Blog’s properties */ };
                context.Blogs.Add(blog);
                context.SaveChanges();

                // The underlying store connection remains open for the next operation  

                blog = new Blog { /* Blog’s properties */ };
                context.Blogs.Add(blog);
                context.SaveChanges();

                // The underlying store connection is still open

           } // The context is disposed – so now the underlying store connection is closed
        }
    }
}

https://docs.microsoft.com/en-us/ef/ef6/fundamentals/connection-management?redirectedfrom=MSDN

【讨论】:

  • 我相信 OP 是在 EF Core 解决方案之后?
  • @timur 我相信我在开始时写道,他可以组合方法。我对其进行了编辑以使其更有意义。
  • This can potentially lead to connections which are open for a long time so use with care.。这就是我实际上试图通过在计时器上打开和关闭来解决的问题。抱歉,我原来的帖子措辞不佳。
  • 这不是我需要的解决方案;请参阅我对问题的更正。但我赞成它
【解决方案2】:

在 ADO.NET 中,您可以配置连接池以满足您的要求

为每个唯一的连接字符串创建一个连接池。创建池时,会创建多个连接对象并将其添加到池中,以满足最小池大小要求。连接会根据需要添加到池中,直至指定的最大池大小(默认值为 100)。当连接被关闭或释放时,连接会被释放回池中。

连接池程序通过在连接释放回池时重新分配连接来满足连接请求。如果已达到最大池大小并且没有可用的连接可用,则将请求排队。然后,池程序尝试回收任何连接,直到达到超时(默认值为 15 秒)。如果 pooler 在连接超时之前无法满足请求,则会抛出异常。

连接池在空闲大约 4 到 8 分钟后,或者如果池检测到与服务器的连接已被切断,则从池中删除连接。请注意,只有在尝试与服务器通信后才能检测到断开的连接。如果发现不再连接到服务器的连接,则将其标记为无效。无效连接只有在关闭或回收时才会从连接池中移除。

如果SqlConnection 超出范围,它不会被关闭。 因此,您必须通过调用Close 显式关闭连接 或DisposeCloseDispose 在功能上是等效的。如果 连接池值Pooling 设置为true 或者是, 底层连接返回到连接池。在 另一方面,如果 Pooling 设置为 false 或 no,则底层 与服务器的连接实际上已关闭。

  using (SqlConnection connection = new SqlConnection("Integrated Security=SSPI;Initial Catalog=Northwind"))  
    {  
        connection.Open();
        // Pool A is created.  
    }  

using (SqlConnection connection = new SqlConnection("Integrated Security=SSPI;Initial Catalog=pubs"))  
    {  
        connection.Open();
        // Pool B is created because the connection strings differ.  
    }  

using (SqlConnection connection = new SqlConnection("Integrated Security=SSPI;Initial Catalog=Northwind"))  
    {  
        connection.Open();
        // The connection string matches pool A.  
    } 

您可以在this 文章中阅读更多详细信息。 EF.Core 和连接池this

相关的 SO 问题。 Entity Framework and Connection Pooling

【讨论】:

  • DBContextPooling 在对 DBContexts 的不同请求中重用 DbContexts;我想在单个 DbContext 中重用 connections
  • 一个 DbContext 可以如何只在打开的连接上github.com/dotnet/efcore/issues/2032
  • 另外,做自己的连接池可能会导致性能问题,例如 SqlServer 上的连接耗尽
  • 这(ADO 连接池)对大多数人来说是正确的解决方案:把它留给提供商,如果您真的需要调整连接池设置。编写自己的池和计时器通常既是浪费时间,也是灾难的根源。使用数据库的监控工具查看连接何时真正打开和关闭,通常您会看到 EF 尽可能多地重用数据库连接。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多