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.
Related
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?
I'm connecting to MySql using mysqlconnector.net (https://github.com/mysql-net/MySqlConnector/) and want to set some session variables for every single session: SET SESSION wait_timeout = 60 and SET SESSION information_schema_stats_expiry = 15. I would like to always set these session variables, but not have the overhead of executing an additional query when a pooled connection is used (since it's a game server and throughput matters). The user does not have permission to set global variables.
I understand session variables are valid for the lifetime of a MySqlConnection object, and if the connection is created with the ConnectionReset = false option, then session variables will persist when the connection is pooled.
The problem: I don't know whether a connection is being truly created or is coming from the pool, since the same constructor can do either. I would like to set the session variables only when the connection is new.
One solution I can see is to modify the MySql connection library to add an API that would tell me whether a connection object has been newly created or was taken from the pool.
The workaround I will be using is to set the global variables when the DB is created using Ansible--the mysql_variables module seems to do what I need (actually since I'm using AWS it'll have to be the rds_param_group module), however this question is still valid, since there are other MySql users that are not in control of the database they're using.
A request similar to yours has already been discussed at https://github.com/mysql-net/MySqlConnector/issues/519, with an answer similar to this one: attach an event handler to MySqlConnection.StateChange to set the session variables.
If your performance needs are such that you cannot incur the overhead of resetting the session (the default behaviour of ConnectionReset=true) and setting the session variables each time, then it might make sense to implement your own connection pool.
By setting Pooling=false; in the connection string, MySqlConnector's pooling will be bypassed and calling MySqlConnection.Open(Async) will immediately open a new connection. You would have to keep the connection open and not Close/Dispose it (which might impose an awkward programming pattern), or wrap it in an IDisposable wrapper that returns it to your custom pool.
So far what I've tried is to verify that my certificates for SSL are correct and through command line (Using psql) and through a console application (Npgsql) I'm able to reach the database with SSL enabled (No password needed). This is working.. My problem starts with the SSL.
The application I'm working on has working Data Access to Postgre using Entity Framework 6 on non-SSL. However when I try to update the string to use SSL, I need to find a place to pass in the client-certificates. I've looked at different places. Right now, I have changed the DbContext() constructor to use base(DbConnection,bool) so that I can pass in a connection that has been provided the callback method. However, I get this error:
The context cannot be used while the model is being created. This
exception may be thrown if the context is used inside the
OnModelCreating method or if the same context instance is accessed by
multiple threads concurrently. Note that instance members of DbContext
and related classes are not guaranteed to be thread safe.
I've tried to apply different fixes from SO to solve this error, but to no avail.
Anything helps!
I need to make a method run every 30 seconds - I was simply going to make a single page that was IP restricted and call it via CURL on a scheduled task every 30 seconds.
I was reading an article and learnt about hangfire - it seems amazing!
So, I have an existing application that is built on Entity Framework Code First - I tried using the default hangfire settings with the standard database, however I keep getting "login failed for user" yellow screen.
So, I was wondering, is there a quick way to make this just work within the standard entity framework tables\single DbContext, or, am I better off just giving it it's own database and login?
To update as per comment:
I am using a brand new MVC app and simply installed hangfire. The connection string is:
<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-myproject-20150404061144;Integrated Security=True" providerName="System.Data.SqlClient" />
and I added:
GlobalConfiguration.Configuration.UseSqlServerStorage("DefaultConnection");
app.UseHangfireDashboard();
app.UseHangfireServer();
However, when launching, I get:
When I delete those three lines from the startup class, the application runs like normal - so, I don't understand what hangfire is doing that can't connect when EF can.
It seems Hangfire needs the database to be already created. In my case, I just ensured that it's already initialized:
using (var context = new MyContext())
{
context.Database.Initialize(false);
}
Hangfire.GlobalConfiguration.Configuration.UseSqlServerStorage("EFConnectionStringName");
What this does is:
Create the DB
Run the designated initializer (if we only call CreateIfNotExists we won't have the proper result in the database)
Configure Hangfire to use the newly created db (MyContext uses the same connection string name)
This is probably due to you are using a localDB for your project.
Obviously, If you need to create Jobs and Hangfire somehow resembles a Message Queu, needs a somehow fast storage solution. LocalDB is anything but fast and probably lacks of many features Hangfire relies upon.
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.