3

EF Core opens and closes a DbConnection for each query by default, unless you pass in an already-open connection.

I have lots of small queries, so instead of opening and closing a connection each time, I'd like to keep the connection open for a period of five seconds at a time, while reusing that connection for each query/command. (The solution for the question linked above leave the connection open for the entire life of the DBContext.)

Putting aside the locking/concurrency issues, where can I inject custom connection resolving/opening logic in a DbContext? Something like

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
Arithmomaniac
  • 3,978
  • 2
  • 31
  • 52
  • I think the underlying db driver usually provides connection pooling which should enable reusing connections. Also check out context pooling in EF core: https://neelbhatt.com/2018/02/27/use-dbcontextpooling-to-improve-the-performance-net-core-2-1-feature/amp/ – weichch Mar 30 '20 at 12:50
  • there's definitely an easy way to control connection pool with EF Core - assuming that's what you are after. (or did you want *different* connections for each query?) – timur Mar 30 '20 at 19:32
  • Sorry, the original question was misdescribed. What I meant to ask was about leaving the connection _open_ for multiple queries based on a timer, but not for the entire lifetime of the context. – Arithmomaniac Mar 31 '20 at 07:52
  • I wonder if I can use [DBConnecitonInterceptor](https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.diagnostics.dbconnectioninterceptor)... – Arithmomaniac Mar 31 '20 at 08:28
  • 1
    Checking your edit, looks to me like you could benefit from a [Semaphore](https://docs.microsoft.com/es-es/dotnet/api/system.threading.semaphore?view=netframework-4.8) with an internal timer. If we think about it, is pretty similar to connection pooling. Have you considered connection pooling? – Cleptus Apr 06 '20 at 06:23
  • I'm looking for keeping a single connection _open_ on a timer; connection pooling would not guarantee that. – Arithmomaniac Apr 06 '20 at 12:23

2 Answers2

2

This is the standard way of doing transaction. You can combine multiple queries.

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 core can use same connection or different or based on connection pooling. Ef core has connected and disconnected mode of transaction. I think this can suit you. aving data in the disconnected scenario is a little bit different than in the connected scenario. In the disconnected scenario, the DbContext is not aware of disconnected entities because entities were added or modified out of the scope of the current DbContext instance. So, you need to attach the disconnected entities to a context with appropriate EntityState in order to perform CUD (Create, Update, Delete) operations to the database.

The following figure illustrates the CUD operations in disconnected scenario:

As per the above figure, disconnected entities (entities which are not being tracked by the DbContext) need to be attached to the DbContext with an appropriate EntityState. For example, Added state for new entities, Modified state for the edited entities and Deleted state for the deleted entities, which will result in an INSERT, UPDATE, or DELETE command in the database when the SaveChanges() method is called.

The following steps must be performed in order to insert, update or delete records into the DB table using Entity Framework Core in disconnected scenario:

Attach an entity to DbContext with an appropriate EntityState e.g. Added, Modified, or Deleted Call SaveChanges() method The following example demonstrates inserting a new record into the database using the above steps:

//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();
}

In the example above, std is a disconnected instance of the Student entity. The context.Add() method attaches a Student entity to a context with an Added state. The SaveChanges() method builds and executes the following INSERT statement:

exec sp_executesql N'SET NOCOUNT ON; https://www.entityframeworktutorial.net/efcore/saving-data-in-disconnected-scenario-in-ef-core.aspx These are important methods.

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

Behavior in EF6 and future versions For EF6 and future versions we have taken the approach that if the calling code chooses to open the connection by calling context.Database.Connection.Open() then it has a good reason for doing so and the framework will assume that it wants control over opening and closing of the connection and will no longer close the connection automatically.

Note

This can potentially lead to connections which are open for a long time so use with care.

We also updated the code so that ObjectContext.Connection.State now keeps track of the state of the underlying connection correctly.

  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

Jin Thakur
  • 2,265
  • 15
  • 12
0

In ADO.NET you can configure connection pooling to fit your requirements

A connection pool is created for each unique connection string. When a pool is created, multiple connection objects are created and added to the pool so that the minimum pool size requirement is satisfied. Connections are added to the pool as needed, up to the maximum pool size specified (100 is the default). Connections are released back into the pool when they are closed or disposed.

The connection pooler satisfies requests for connections by reallocating connections as they are released back into the pool. If the maximum pool size has been reached and no usable connection is available, the request is queued. The pooler then tries to reclaim any connections until the time-out is reached (the default is 15 seconds). If the pooler cannot satisfy the request before the connection times out, an exception is thrown.

The connection pooler removes a connection from the pool after it has been idle for approximately 4-8 minutes, or if the pooler detects that the connection with the server has been severed. Note that a severed connection can be detected only after attempting to communicate with the server. If a connection is found that is no longer connected to the server, it is marked as invalid. Invalid connections are removed from the connection pool only when they are closed or reclaimed.

If the SqlConnection goes out of scope, it won't be closed. Therefore, you must explicitly close the connection by calling Close or Dispose. Close and Dispose are functionally equivalent. If the connection pooling value Pooling is set to true or yes, the underlying connection is returned back to the connection pool. On the other hand, if Pooling is set to false or no, the underlying connection to the server is actually closed.

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

You can read more details in this article. EF.Core and Connection pooling this

Related SO question. Entity Framework and Connection Pooling

OxQ
  • 832
  • 5
  • 17
  • 1
    DBContextPooling is reusing DbContexts across different requests for a DBContexts; I want to reuse _connections_ inside of a single DbContext – Arithmomaniac Apr 06 '20 at 12:20
  • A DbContext can how only on the opened connection https://github.com/dotnet/efcore/issues/2032 – OxQ Apr 06 '20 at 13:58
  • Also, to do own connection pooling can lead you to performance problems such as connections exhausting on SqlServer – OxQ Apr 06 '20 at 13:59