EF Core DbContext connection management for native queries - c#

I'm using EF core 6 with NpgSql (connection pooling enabled) and I need to execute a native query for a specific use case. I'm trying to figure out the best way to manage the database connection for native queries inside an ASP.Net Core app. The query is executed inside a scoped service. I am doing the following and it's "working":
_dbContext.Database.OpenConnectionAsync(cancellationToken);
var npgsqlConnection = _dbContext.Database.GetDbConnection() as NpgsqlConnection;
await using var cmd = new NpgsqlCommand(Sql, npgsqlConnection);
// execute cmd, connection isn't closed by me
There are a couple of questions like this already but I wasn't satisfied with the answers. For example some say "if you open the connection you should close it". But this breaks the next time I use the DbContext.
It works if I don't close the connection and it doesn't exhaust the connection pool even if I execute it many times. I assume there is some magic going on and it's released back to the pool when the scope is disposed?
Other way would be to create & close the NpgsqlConnection directly. Is there a better way?

Related

Teradata Client 3.1, EF Core 3.1, Connection / Session Lives On

I'm having trouble finding specific documentation around manually ending the session via .exit or .quit or if its even possible with FromSqlRaw.
Problem Statement:
On connect and execution of a stored procedure a Teradata session is created. After destruction of the db context the session appears to live on for some unknown amount of time. Restarting the service clears it immediately.
Attempts to manually end the session via .exit fail.
Examples:
With dependency injection:
services.AddDbContext<TeradataContext>(options =>
options.UseTeradata(Configuration.GetConnectionString("EDW"), opts => {
opts.CommandTimeout(120);
})
);
It's my understanding connection pooling would not happen as it is lifecycle and not added as a pool. But since garbage collection could take some time I decided to try moving it as a test:
using (var dbContext = new TeradataContext())
{
int result = dbContext.Database.ExecuteSqlRaw("CALL .... ({0});", bar);
dbContext.Database.CloseConnection();
}
The session on Teradata still persists and the next call fails as the stored procedure fails to create the already existing volatile table.
I tried adding:
dbContext.Database.ExecuteSqlRaw(".exit");
I get back:
[Teradata Database] [3706] Syntax error: expected something between ';' and '.'.
Does anyone know the correct way to call .exit here? Or any other way to force the close and end of session using Teradata Client 3.1, EF Core 3.1
dbContext.Database.CloseConnection();
But the session still persists so I don't believe their provider is performing a quit or exit.
I am not every experienced with TeraData...
I was really hoping to use EF but I guess I could fall back to other options if we can't figure this out.
Thanks for any insights.

Too many Oracle connections open

I have a .NET Core web api on IIS running and I'm using oracle as database and I do connect to it like this.
using(OracleConnection con = new OracleConnection(connectionString))
{
OracleCommand cmd = new OracleCommand();
//some other code here
con.Open();
}
I'm not using EF or so. I (de)serialze the data from the reader directly into a json or xml string.
I have a small batch file to test the api. The batch sends a request using curl every second and when I run the batch file 5 times, to simulate a little bit of traffic, (I know there are some tools for that, but thats not the problem) the api has open 7 connections to the database. Why are 7 db connections open, how to handle it, so that a maximum of 2 or 3 simultaneously are open, even if a request has to wait?
I do not want to "just make it work", I want it to work the right way. Because of that, I want to now, is "connection pooling" the keyword here? Especially the max pool size, could I just set it to 3, or did I forget something to set up, or something?

Postgresql NpgSql connection handling extra query and multiple close connection

I am basically running a sql query through dapper but when I do some profiling on this on every query that i perform to npg sql I see an extra ExecuteScalar query that is sent on that connection. And there are multiple NpgsqlConnection.Close events. I run the query in a using statement that terminates the NpgsqlConnection as follow.
using (var connection = new NpgsqlConnection(connectionString))
{
return connection.QueryAsync<T>(sql, param);
}
The but it also runs this extra command one every sql that i send through this code -
SET extra_float_digits = 3
SET ssl_renegotiation_limit = 0
SET lc_monetary = 'C'
SELECT 'Npgsql73113'
Here is the profiler screenshot of the relevant section. Any one know why there is this extra query and multiple Connection Close events.
You are using Npgsql 2.2, which is very old by now and which sent these commands on startup. Please upgrade to the latest stable version (3.1.3) and these should be gone.
I'm less sure about the connection close events, if you see this behavior in 3.1.3 please report an issue.

EF with SQL server application role

I need to use application rule security on sql server. And i want to use Enity Framework Code First.
After a successful login my connection is set to application role. I then create my DbContext using this connection.
But: EF expects a closed connection object. And closing a connection drops the application role.
How can i solve this dilemma?
I managed to get this work with two steps:
Switch connection pooling off, which is mentioned all the time for connections using application roles. As i have a desktop application, this is no problem for me.
Add a handler to DbConnection.StateChanged and activate the application role on every opening of the connection. Without connection pooling, it is not necessary to sp_unsetapprole on closing. So this works for me:
context.Database.Connection.StateChanged += (sender, args) =>
if (args.CurrentState == ConnectionState.Open) {
activateAppRole((DbConnection)sender, ...);
}
}
I guess, if Pooling is vital for someone, she may call sp_unsetapprole on closing the connection in this same handler.
Since this question is high on the search result list, I just wanted to throw in a word of caution. I have an app that needed to use an application role and okrumnow's solution seemed at first to work.
However, in unit testing I discovered that sometimes handling the StateChanged event will cause the event to be raised twice and you'll get the error:
"Impersonate Session Security Context" cannot be called in this batch because a simultaneous batch has called it.
It seems to help to change the conditional to:
args.CurrentState == ConnectionState.Open &&
args.CurrentState == ConnectionState.Closed
But it still doesn't eliminate the error. I confirmed this in EF4.3 and EF5. Ladislav is correct that the ideal way is creating a connection for the DbContext and telling the context that it doesn't own it.
Also, connection pooling is never possible with this setup since there is no ConnectionState.Closing event where you can call sp_unsetapprole before your connection is closed.
Since I had the flexibility, my solution was to eliminate the usage of an app role and using a dedicated SQL login instead. Either way you're hard-coding a password...
EF doesn't have any native support for this. I guess the workaround can be:
Creating your own connection and passing it (closed) to EF context / EntityConnection. This should enforce that you will have connection lifetime under your control and EF will not close it (but I already saw complains that it doesn't work with DbContext).
Once you have instance of the context created set application role. Context itself should not generate any queries to the database (except DbContext with code first checking version of the database) so setting the role after context creation should not cause any problems.

SQL Server: Could not find prepared statement with handle x

Recently our QA team reported a very interesting bug in one of our applications. Our application is a C# .Net 3.5 SP1 based application interacting with a SQL Server 2005 Express Edition database.
By design the application is developed to detect database offline scenarios and if so to wait until the database is online (by retrying to connect in a timely manner) and once online, reconnect and resume functionality.
What our QA team did was, while the application is retrieving a bulk of data from the database, stop the database server, wait for a while and restart the database. Once the database restarts the application reconnects to the database without any issues but it started to continuously report the exception "Could not find prepared statement with handle x" (x is some number).
Our application is using prepared statements and it is already designed to call the Prepare() method again on all the SqlCommand objects when the application reconnects to the database. For example,
At application startup,
SqlCommand _commandA = connection.CreateCommand();
_commandA.CommandText = #"SELECT COMPANYNAME FROM TBCOMPANY WHERE ID = #ID";
_commandA.CommandType = CommandType.Text;
SqlParameter _paramA = _commandA.CreateParameter();
_paramA.ParameterName = "#ID";
_paramA.SqlDbType = SqlDbType.Int;
_paramA.Direction = ParameterDirection.Input;
_paramA.Size = 0;
_commandA.Parameters.Add(_paramA);
_commandA.Prepare();
After that we use ExceuteReader() on this _commandA with different #ID parameter values in each cycle of the application.
Once the application detects the database going offline and coming back online, upon reconnect to the database the application only executes,
_commandA.Prepare();
Two more strange things we noticed.
1. The above situation on happens with CommandType.Text type commands in the code. Our application also uses the same exact logic to invoke stored procedures but we never get this issue with stored procedures.
2. Up to now we were unable to reproduce this issue no matter how many different ways we try it in the Debug mode in Visual Studio.
Thanks in advance..
I think with almost 3 days of asking the question and close to 20 views of the question and 1 answer, I have to conclude that this is not a scenario that we can handle in the way we have tried with SQL server.
The best way to mitigate this issue in your application is to re-create the SqlCommand object instance again once the application detects that the database is online.
We did the change in our application and our QA team is happy about this modification since it provided the best (or maybe the only) fix for the issue they reported.
A final thanks to everyone who viewed and answered the question.
The server caches the query plan when you call 'command.Prepare'. The error indicates that it cannot find this cached query plan when you invoke 'Prepare' again. Try creating a new 'SqlCommand' instance and invoking the query on it. I've experienced this exception before and it fixes itself when the server refreshes the cache. I doubt there is anything that can be done programmatically on the client side, to fix this.
This is not necessarily related exactly to your problem but I'm posting this as I have spent a couple of days trying to fix the same error message in my application. We have a Java application using a C3P0 connection pool, JTDS driver, connecting to a SQL Server database.
We had disabled statement caching in our the C3P0 connection pool, but had not done this on the driver level. Adding maxStatements=0 to our connection URL stopped the driver caching statements, and fixed the error.

Categories