When does EF Core DBContext dispose Sql Connection? - c#

When using classic ADO.NET we typically close the SQL connection as soon as we are done executing the SQL command, so the connection is closed and returned back to the pool.
Something like:
public Order[] GetActiveOrders()
{
var orders = new List<Orders>();
using (SqlConnection connection = new SqlConnection("connectionString"))
{
using (SqlCommand cmd = connection.CreateCommand())
{
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "Select * FROM ORDERS WHERE Status = 'Active'";
cmd.Connection.Open();
using (var reader = cmd.ExecuteReader())
{
//populate orders
}
}
}
// SQL connection is closed here and returned back to connection pool
return orders;
}
In ASP.NET Core using EF Core, we typically inject the DbContext into constructor using DI framework
public class OrderService:IDisposable
{
private readonly MyDBContext _dbContext;
public OrderService(MyDBContext dbContext)
{
_dbContext = dbContext;
}
public Order[] GetActiveOrders()
{
var orders = _dbContext.Orders.Where(x=>x.Status == 'Active').ToArray()
return orders;
}
#region Dispose
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
if (_dbContext != null)
{
_dbContext.Dispose();
}
// Free any other managed objects here.
}
// Free any unmanaged objects here.
_disposed = true;
}
}
I am assuming under the hood, the DbContext is still using SqlCommand and SqlConnection to connect to the database.
I want to know when does EF Core close the SqlConnection? Does it close the connection as soon as it's done executing the query (like how we do in classic ADO) or when the DbContext is disposed?
So in the example above, will it dispose the connection before returning Orders from the GetActiveOrders() method or when the OrderService is disposed by the DI container?

Side note: you shouldn't dispose something you don't own, which is the case with DI injected DbContext (and any injected object).
But to answer you concrete question. I can't find documentation to link to, but as soon as you
don't provide pre allocated DbConnection object via DbContextOptions
don't manually open connection via .Database.OpenConnection method
don't open explicit transactions via Database.BeginTransaction method
i.e. do not take some connection management by yourself (in which case you are responsible for closing/disposing), EF Core opens connection only when needed and closes it immediately after that.
When needed means opening a transaction during the SaveChanges until it gets commit or rolled back. Or when executing DbReader returning query until the returned reader is consumed or cancelled. In other words, exactly how you would do it with classic ADO.NET. And yes, behind the scenes it uses ADO.NET (at least for relational databases).
Some additional info here Do I need to close the DbConnection manually when using ambient transactions with EF Core 2.1? and Using EF Core to invoke stored procedure and closing connection. Also Connection Resiliency and How queries work documentation topics.

Related

Is snowflake net connector thread safe?

Regarding the snowflake .NET connector: https://github.com/snowflakedb/snowflake-connector-net, I can find nothing in the documentation or source code to suggest that connection pooling is supported, and because the connection itself is backed by HttpClient, and we know that HttpClient should be reused rather than created/disposed constantly, what's the best way to use the snowflake .NET connector when you'll be making many queries across threads?
Note: I'm not planning on changing any properties of the connection once it's created (schema, database, etc.).
For example:
// application startup registers this provider as a singleton
public class SnowflakeConnectionProvider : IDisposable
{
private IDbConnection _conn;
public SnowflakeConnectionProvider()
{
_conn = new SnowflakeDbConnection();
_conn.ConnectionString = "connectionString";
_conn.Open();
}
public IDbConnection conn { get => _conn; }
public Dispose() => _conn.Close();
}
Now, is it safe for multiple threads to share the one SnowflakeDbConnection like so:
public class Worker
{
public Worker(SnowflakeConnectionProvider provider)
{
IDbConnection conn = provider.conn;
IDbCommand cmd = conn.CreateCommand();
cmd.CommandText = "select * from t";
IDataReader reader = cmd.ExecuteReader();
while(reader.Read())
{
Console.WriteLine(reader.GetString(0));
}
}
}
Summary from GitHub: The connector is thread safe.
As long as you do not use any session variables in your snowflake code, you can have multiple threads sharing the same SnowflakeDbConnection. It does not matter if you are reusing threads or if you are creating new threads all the time.
Even if you are constantly creating and disposing connections from multiple different threads, a single HttpClient is created once and then reused and shared by all connections.
https://github.com/snowflakedb/snowflake-connector-net/issues/275

In-memory SQLite and database disappearing

I am using Nancy to create an api to a database, and am wanting to test / develop against an in-memory database.
I am using an instance of my database class in a custom bootstrapper class with the connection string Data Source=:memory:, which in turn is creating the necessary tables - I have stepped through this and I'm confident this is occuring.
I am then obtaining a new connection using the same connection string to load/save data, but even a simple select is coming up with the sql error that the table doesn't exist.
Is there a fault in this logic of creating and using a new connection with the same connection string?
OK, so straight from the docs:
The database ceases to exist as soon as the database connection is closed. "
However, this can be worked around with multiple connections by using cache=shared in your connection string.
However, this isn't a solution to the problem because as soon as the last connection closes, the database ceases to exist.
You need to keep the SQLiteConnection open and ensure that the DbContext does not own the connection. This way, when the DbContext is disposed by the container, the connection doesn't get closed with it. Note: This only works with EF6. You can still pass the flag in EF5, but the context will still close the connection when the context gets disposed.
I created a TestBootstrapper which inherited the working Bootstrapper from the web project.
As part of the ConfigureRequestContainer method, I used a method factory on the DbContext registration which created a new DbContext each time, but used the existing connection. If you don't do this, the DbContext will get disposed after your first request and the second request will fail.
public class TestBootstrapper : Bootstrapper
{
private const string ConnectionString = "data source=:memory:;cache=shared;";
private static SQLiteConnection _connection;
private static TestInitializer _initializer = new TestInitializer();
protected override void ConfigureRequestContainer(TinyIoCContainer, NancyContext context)
{
container.Register<Context>((_,__) =>
{
if (_connection == null)
_connection = new SQLiteConnection(ConnectionString);
// The false flag tells the context it does not own the connection, i.e. it cannot close it. (EF6 behaviour only)
var dbContext = new Context(_connection, _initializer, false);
if (_connection.State == ConnectionState.Closed)
_connection.Open();
// I build the DB and seed it here
_initializer.InitializeDatabase(context);
return dbContext;
});
// Additional type registrations
}
// Call this method on a [TearDown]
public static void Cleanup()
{
if (_connection != null && _connection.State == ConnectionState.Open)
_connection.Close();
_connection = null;
}
}

SQL Server : is there any performance penalty for wrapping a SELECT query in a transaction?

As learning exercise and before trying to use any ORM (like EF) I want to build a personal project using ADO.NET and stored procedures.
Because I don't want my code to become a mess over time, I want to use some patterns like the repository and UoW patterns.
I've got almost everything figured it out, except for the transaction handling.
To somehow 'simulate' a UoW, I used this class provided by #jgauffin, but what's stopping me from using that class is that every time you create a new instance of that class (AdoNetUnitOfWork) you're automatically beginning a transaction and there a lot of cases where you only need to read data.
In this regard this is what I found in one of the SQL books I've been reading:
Executing a SELECT statement within a transaction can create locks on the referenced tables, which can in turn block other users or sessions from performing work or reading data
This is the AdoNetUnitOfWork class:
public class AdoNetUnitOfWork : IUnitOfWork
{
public AdoNetUnitOfWork(IDbConnection connection, bool ownsConnection)
{
_connection = connection;
_ownsConnection=ownsConnection;
_transaction = connection.BeginTransaction();
}
public IDbCommand CreateCommand()
{
var command = _connection.CreateCommand();
command.Transaction = _transaction;
return command;
}
public void SaveChanges()
{
if (_transaction == null)
throw new InvalidOperationException("Transaction have already been commited. Check your transaction handling.");
_transaction.Commit();
_transaction = null;
}
public void Dispose()
{
if (_transaction != null)
{
_transaction.Rollback();
_transaction = null;
}
if (_connection != null && _ownsConnection)
{
_connection.Close();
_connection = null;
}
}
}
And this is how I want to use the UoW in my repositories:
public DomainTable Get(int id)
{
DomainTable table;
using (var commandTable = _unitOfWork.CreateCommand())
{
commandTable.CommandType = CommandType.StoredProcedure;
//This stored procedure contains just a simple SELECT statement
commandTable.CommandText = "up_DomainTable_GetById";
commandTable.Parameters.Add(commandTable.CreateParameter("#pId", id));
table = ToList(commandTable).FirstOrDefault();
}
return table;
}
I know I can tweak this code a bit so that the transaction would be optional, but since I trying to make this code as platform independent as possible and as far as I know in other persistence frameworks like EF you don't have to manage transactions manually, the question is, will I be creating some kind of bottleneck by using this class as it is, that is, with transactions always being created?
It all depends on the transaction isolation level. Using the default isolation level (ie. read committed) then your SELECT should occur no performance penalty if is wrapped in a transaction. SQL Server internally wraps statements in a transaction anyway if one is not already started, so your code should behave almost identical.
However, I must ask you why not use the built-in .Net TransactionScope? This way your code will interact much better with other libraries and frameworks, since TransactionScope is universally used. If you do decide to switch to this I must warn you that, by default, TransactionScope uses SERIALIZABLE isolation level and this does result in performance penalties, see using new TransactionScope() Considered Harmful.

DBTransactions between stateless calls using GUIDs

I'm looking to add transactional support to my DB engine and providing to Abstract Transaction Handling down to passing in Guids with the DB Action Command. The DB engine would run similar to:
private static Database DB;
public static Dictionary<Guid,DBTransaction> Transactions = new ...()
public static void DoDBAction(string cmdstring,List<Parameter> parameters,Guid TransactionGuid)
{
DBCommand cmd = BuildCommand(cmdstring,parameters);
if(Transactions.ContainsKey(TransactionGuid))
cmd.Transaction = Transactions[TransactionGuid];
DB.ExecuteScalar(cmd);
}
public static BuildCommand(string cmd, List<Parameter> parameters)
{
// Create DB command from EntLib Database and assign parameters
}
public static Guid BeginTransaction()
{
// creates new Transaction adding it to "Transactions" and opens a new connection
}
public static Guid Commit(Guid g)
{
// Commits Transaction and removes it from "Transactions" and closes connection
}
public static Guid Rollback(Guid g)
{
// Rolls back Transaction and removes it from "Transactions" and closes connection
}
The Calling system would run similar to:
Guid g
try
{
g = DBEngine.BeginTransaction()
DBEngine.DoDBAction(cmdstring1, parameters,g)
// do some other stuff
DBEngine.DoDBAction(cmdstring2, parameters2,g)
// sit here and wait for a response from other item
DBEngine.DoDBAction(cmdstring3, parameters3,g)
DBEngine.Commit(g)
}
catch(Exception){ DBEngine.Rollback(g);}
Does this interfere with .NET connection pooling (other than a connection be accidently left open)?
Will EntLib keep the connection open until the commit or rollback?
The connection will be kept open until a commit or rollback. It is the transaction that is keeping the connection open.
It will not affect connection pooling, other than a connection held by a transaction will not be returned to the connection pool.
I would recommend that you look at the .net TransactionScope. This may be able to meet your needs, without you writing any of this custom code.

IDbConnection exception

I have a IDbConnection for both Sql or Oracle connections. I have no problem to open it and then read data or save data through the connection. However, when the job is done, I tried to close the connection. Then I got a exception: "Internal .Net Framework Data Provider error 1".
Here are some codes to close the connection:
if (conn != null) // conn is IDbConnectin
{
if (conn.State == System.Data.ConnectionState.Open)
{
conn.Close(); // bung! exception
}
conn.Dispose();
}
conn = null;
Anything else I should check before safely closing the connection?
I know it might not sound like it solves your problem directly, but IDbConnection is IDisposable, so wrap your code that uses it in a using {} block.
Why?
You probably know that at the end of a using {} block, Dispose() is called. And, in every instance of IDbConnection, calling Dispose() will indirectly call Close(). But you get an additional bonus to using using that'll prevent you from running into this issue entirely -- by using using, you're forced to keep the creation, opening, closing, and disposal of the connection within the same context.
Most problems I found where people were running into your issue is when they use a Finalizer, or a separate thread to dispose of their connection objects. To me, there's an even bigger smell going on, where they're keeping their disposable objects alive for just a little bit too long, possibly sharing the connection between multiple members of the same class.
In other words, when you pass around the connection object, you might be tempted to write something like this:
class AccountService {
private IDbConnection conn;
internal AccountService(IDbConnection connection) {
this.conn = connection;
}
public Account GetAccount(String id) {
IDbCommand command = conn.CreateCommand();
conn.Open;
Account a = Account.FromReader(command.Execute(Strings.AccountSelect(id)));
conn.Close; // I remembered to call Close here
return a;
}
// ... other methods where I Open() and Close() conn
// hopefully not necessary since I've been careful to call .Close(), but just in case I forgot or an exception occured
~AccountService() {
if (conn != null)
{
if (conn.State == System.Data.ConnectionState.Open)
{
conn.Close();
}
conn.Dispose();
}
conn = null;
}
}
If you had used using, you wouldn't have even needed to think about using a Finalizer:
// IDbFactory is custom, and used to retrieve a Connection for a given Database
interface IDbFactory {
IDbConnection Connection { get; }
}
class AccountService {
private IDbFactory dbFactory;
internal AccountService(IDbFactory factory) {
this.dbFactory = factory;
}
public Account GetAccount(String id) {
using (IDbConnection connection = dbFactory.Connection) {
using (command = connection.GetCommand()) {
connection.Open();
return Account.FromReader(command.Execute(Strings.AccountSelect(id)));
}
} // via using, Close and Dispose are automatically called
}
// I don't need a finalizer, because there's nothing to close / clean up
}
There are exceptions to the using rule, especially if the construction of the disposable object is expensive, but 99 times out of 100, if you're not using using, there's a smell.
I think you call close method in other thread or in Finilize method. Do not call Close or Dispose on a Connection, a DataReader, or any other managed object in the Finalize method of your class. In a finalizer, you should only release unmanaged resources that your class owns directly. If your class does not own any unmanaged resources, do not include a Finalize method in your class definition. see here

Categories

Resources