Testing environment:
-- Framework version: .NET 6
-- Microsoft.Data.Sqlite, version: 6.0.6
-- Dapper, version: 2.0.123
Q: When only one query is executed, there is no problem. Problems occur when multiple concurrent queries are executed.
(Reference: When I write code in native ADO.NET instead of Dapper(conn.Query), there is no problem!)
Here is a simple demo that can reproduce the problem very easily.
using System.Data;
using Dapper;
using Microsoft.Data.Sqlite;
// SQLite db file
string connStr = $"Data Source={AppDomain.CurrentDomain.BaseDirectory}test.db";
// SQLite connection (Share the singleton connection)
using SqliteConnection conn = new SqliteConnection(connStr);
conn.Open();
bool existError = false;
while (true)
{
if (existError == true)
{
break;
}
// Perform concurrent reads
Parallel.For(0, 100, index =>
{
try
{
// Test SQL
string sql_test = " select * from T_Account where AccountId='ab001' ";
// Perform query data (May throw a null reference exception )
var t = conn.Query(sql_test);
}
catch (Exception ex)
{
existError = true;
// Test output: Object reference not set to an instance of an object.
Console.WriteLine($"Read error ({index}): {ex.Message}");
}
});
Console.WriteLine($"{DateTime.Now}-------- split line --------");
}
The whole project has been using Dapper, I want to know where the problem is, how should I solve it?
You might think that it's a super good idea to reuse the connection, but it isn't. There already is a connection pool built-in to ADO, and it's far more efficient than anything we can come up with. I tried to run your code, obviously with another sqlite database and query, and on the 6th or 7th iteration I got the null reference exception. Then I changed your code like this:
// Perform concurrent reads
Parallel.For(0, 100, index =>
{
try
{
// These two lines moved inside the loop
using SqliteConnection conn = new SqliteConnection(connStr);
conn.Open();
// Test SQL
string sql_test = " select * from T_Account where AccountId='ab001' ";
// Perform query data (May throw a null reference exception )
var t = conn.Query(sql_test);
}
catch (Exception ex)
{
existError = true;
// Test output: Object reference not set to an instance of an object.
Console.WriteLine($"Read error ({index}): {ex.Message}");
}
});
And the code can go on and on as expected. Best practice is to create the database connection in a using statement and have it disposed as soon as you are done with it.
Since I haven't seen your code with raw ADO.NET, I can't tell why that works.
Related
Issue:
Basically, update statement awaits endlessly ( ExecuteNonQueryAsync() ) for some reason.
Insert works just fine, I tried to hardcode a simple update statement with hardcoded parameters(without merge), same story, even tried to remove where clause and update all rows, again, same problem.
After setting commandTimeout to 1s I managed to identiy TimeoutException, why it happens? I have no idea.
Edit: Plot twist.
Now I have randomly successful updates, which dazes me even more.
I tried basically every possible scenario I could think of to identify any patterns residing in the issue, my only conclusion is that updates success rate is totally random...
Still inserts are working just fine.
This is the simplified code portion.
public class CipCheckRepository : ICipCheckRepository
{
private string connectionString = "Data Source=****;User Id=****;PASSWORD=****;Persist Security Info=True;";
public CipCheckRepository()
{}
public async Task InsertOrUpdate(CipCheck cipCheck)
{
using (var uow = new UnitOfWork(connectionString))
{
using (var cmd = uow.CreateCommand())
{
cmd.CommandText = #"
MERGE INTO test
USING dual
ON (id = :CAD_ID)
WHEN MATCHED THEN
UPDATE SET value = :ISRA_NUMBER
WHEN NOT MATCHED THEN
INSERT
(
id,
value
)
VALUES
(
:CAD_ID,
:ISRA_NUMBER
)";
cmd.Parameters.AddWithValue("CAD_ID", 1);
cmd.Parameters.AddWithValue("ISRA_NUMBER", cipCheck.IsraNumber);
cmd.CommandTimeout = 1;
try
{
await cmd.ExecuteNonQueryAsync();
}
catch (OracleException ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
}
}
UnitOfWork class is created for abstraction matters.
How can I identify the cause of the never-ending query and what steps can be taken to prevent it?
It seems that I've been locking the table somehow, after committing all statements via Oracle Developer the quirks are gone.
I have a SQL Server 2008 database with two tables, a .NET 4.5 application using a TransactionScope with two queries to the database, each using their own separate connection which shares a single connection string.
This code that works fine:
internal static void Main(string[] args)
{
string connStr = string.Format(
"Data Source={0};Initial Catalog={1};" +
"Integrated Security=True", "My DB Server", "My Database");
using (var scope = new TransactionScope())
{
string query1 = "(actual query not relevant)";
var ds = GetSqlServerDataSet(query1, connStr);
string query2 = "(actual query not relevant)";
var ds1 = GetSqlServerDataSet(query2, connStr);
scope.Complete();
}
}
private static System.Data.DataSet GetSqlServerDataSet(string usingQuery,
string usingConnectionString,
params System.Data.SqlClient.SqlParameter[] withParameters)
{
using (var ds = new System.Data.DataSet())
using (var conn = new System.Data.SqlClient.SqlConnection(usingConnectionString))
using (var command = new System.Data.SqlClient.SqlCommand(usingQuery, conn))
{
command.Parameters.AddRange(withParameters);
using (var adapter = new System.Data.SqlClient.SqlDataAdapter(command))
{
adapter.Fill(ds);
}
return ds;
}
}
Now, if I throw an exception inside the scope to test roll-back, that's when stuff gets strange. I'm using a referenced class library I've written to persist info about exceptions to a database. It's the same type of setup -- same SQL Server, .NET version, identical ADO.NET code, etc. It's just writing to a different database.
Here's how it works: I've added this method to my app:
private static void HandleUnhandledException(Object sender, System.UnhandledExceptionEventArgs e)
{
ExceptionHandling.Core.Main.ProcessException((Exception) e.ExceptionObject);
Environment.Exit(0);
}
and I've added this line to the top of my Main method:
AppDomain.CurrentDomain.UnhandledException += HandleUnhandledException;
Now when I throw an exception, e.g. throw new Exception("blah"); at the bottom of Main right before scope.Complete(), it automatically jumps to HandleUnhandledException, and the rest happens as I've described above. This results in System.Transactions.TransactionManagerCommunicationException with message:
Network access for Distributed Transaction Manager (MSDTC) has been
disabled. Please enable DTC for network access in the security
configuration for MSDTC using the Component Services Administrative
tool.
This happens on the call to Connection.Open() in my class library.
So what's happening here? I know what the error is telling me, and I know how to fix it. My question is why is this happening in the first place? I thought the using statement would take care of rolling back the transaction before it hits my HandleUnhandledException method, so there shouldn't be two transactions involved here. Or am I wrong?
It happens because when your HandleUnhandledException method runs - you are still inside using block and it's finally part has not been run yet. It's easy to verify with this code:
class Program {
static void Main(string[] args) {
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
try {
throw new Exception("test");
}
finally {
Console.WriteLine("finally");
}
}
private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) {
Console.WriteLine("handle exception");
}
}
Which outputs:
handle exception
Unhandled exception: System.Exception: test ...
finally
You may want to read this question (and comments, from Eric Lipper for example) for discussion of related behavior. But I think reasons are not very relevant to your question - we can say it just works like this.
Given that information it's clear why you observe your exception - you are still inside uncommited transaction scope and trying to open connection to different database.
One solution that comes to mind in your case is check if we are inside transaction and suppress it if yes (because you anyway don't want your logging code to affect ambient transaction in any way):
private static void HandleException(Exception ex) {
TransactionScope scope = null;
if (Transaction.Current != null)
scope = new TransactionScope(TransactionScopeOption.Suppress);
try {
// do stuff
}
finally {
scope?.Dispose();
}
}
Or maybe just always run inside supressed TransactionScope.
I am testing out ServiceStacks OrmLite. I have previosly used MySql without OrmLite and now I am faced with the problem easiest described in this error message:
There is already an open DataReader associated with this Connection
which must be closed first.
Since I have a multi-threaded application, certain threads will be polling the database, while other will insert, update or select "on demand", when needed. This results in the above mentioned exception.
What I need to do is to be able to detect if a connection (IDbHandler) is "busy"; has an open DataReader or something else that. If it is busy, take the next connection (from the "connection pool" i want to implement). The problem is, there is no method or property I can use in the IDbHandler object to determine if it is busy or not.
I have solved this in the "normal" mysql case by simply having a method where I send in the MySqlCommand or just the query string, like:
dbConnections.ExecuteQuery("SELECT * FROM test");
dbConnections.ExecuteQuery(cmd); // cmd == MySqlCommand
and the ExecuteQuery will handle of finding an open connection and just passing on the cmd/query there.
But, since I am using OrmLite it has a lot of extension methods to IDbConnection and I do not want to create "proxy methods" for each one. In the simple mysql case above, there is really only one method needed, that takes in a MySqlCommand, but not so with the many methods in OrmLite.
The first question:
How can I detect if a connection is busy? I want to avoid a try-catch situation to detect it.
Second question:
Is there some way to pass the entire "method" call, something like:
Example:
dbConnections.Run(iDbHandler.Select<MyObject>(q => q.Id > 10));
// or
dbConnections.Run(iDbHandler.Where<MyObject>(q => q.Id > 10));
// or
dbConnections.Run(iDbHandler.SomeOtherWeirdMetod<MyObject>(q => bla bla bla));
This is by far not the best solution, but it is an approach that I am testing with to see how it handles for my specific case (currently on ver 3.97). Like Ted, I am seeing frequent exceptions of open data readers, or connections being returned as closed.
In my usage all services inherit my parent service (which in turn inherits Service) that handles some common meta-data handling. I have opted to have my base service override the Service's Db property and do a quick check on the Db connection's state and attempt a recovery. One case this fixed immediately was the following:
My MsSql server is running in a failover cluster. When the SQL server flips from node A to node B, there is no built-in mechanism that I found in ServiceStack to detect that its in memory connections are "dirty" and need to reconnect.
Comments and improvements are most welcome.
public override System.Data.IDbConnection Db
{
get
{
try
{
var d = base.Db;
if (d == null || d.State != System.Data.ConnectionState.Open)
return ForceNewDbConn();
else
return d;
}
catch (Exception ex)
{
return ForceNewDbConn();
//throw;
}
}
}
private IDbConnection ForceNewDbConn()
{
try
{
var f = TryResolve<IDbConnectionFactory>();
if (f as OrmLiteConnectionFactory != null)
{
var fac = f as OrmLiteConnectionFactory;
fac.AutoDisposeConnection = true;
var newDBconn = fac.Open();
return newDBconn;
}
return base.Db;
}
catch (Exception ex)
{
throw;
}
}
My problem is that the transaction is not working properly it should not save the data for one table if an exception occurs during the trascation
When all the table is correct then only save data.
Consider the following:
databaseEntites objEntites = null;
using (objEntites = new databaseEntites())
{
objEntites.Connection.Open();
using (System.Data.Common.DbTransaction transaction =
objEntites.Connection.BeginTransaction())
{
try
{
customer objcust=new customer();
objcust.id=id;
objcust.name="test1";
objcust.email="test#gmail.com";
objEntites.customer.AddObject(objcust);
order objorder=new order();
objorder.custid=objcust.id;
objorder.amount=500;
objEntites.order.AddObject(objorder);
objEntites.SaveChanges();
transaction.Commit();
}
catch()
{
transaction.Rollback();
}
}
}
In this my second table column name is not correct and on SaveChanges() giving the exception.
When i see the database and found that it saving the data for customer table which is wrong i want data will go in the customer table when all table is correct and this savechanges either save for all table or not save for any.
For this i have also try the TransactionScope
using (TransactionScope tscope =
new TransactionScope(TransactionScopeOption.RequiresNew))
{
......all the code here....
objEntites.SaveChanges(false);
tscope.Complete();
objEntites.AcceptAllChanges();
}
But its giving the same issue as described above.
Thanks in advance.
You can use database transaction or EF TransactionScope. For using database transaction it is enough to do as below:
using (var dbContextTransaction = context.Database.BeginTransaction())
{
try
{
//Some stuff
dbContextTransaction.Commit();
}
catch (Exception)
{
dbContextTransaction.Rollback();
}
}
And for using second way that EF TransactionScope just use easily as below:
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
try
{
//Some stuff
scope.Complete();
}
catch (Exception)
{
//Don't need call any rollback like method
}
}
The point is no Rollback() method exist in the TransactionScope (against the normal ADO.NET Transaction) and Unless you call the Complete() method, the transaction do not complete and all the changes are rolled back automatically. You can see MSDN for better understand: http://msdn.microsoft.com/en-us/data/dn456843.aspx
Hope this help
If you have already a try ... catch block, you don't need using - just add finally. The following example should have everything you need:
databaseEntites objEntites = null;
var System.Data.Common.DbTransaction transaction = null;
try
{
objEntites = new databaseEntites();
objEntites.Connection.Open();
transaction = objEntites.Connection.BeginTransaction();
customer objcust=new customer();
objcust.id=id;
objcust.name="test1";
objcust.email="test#gmail.com";
objEntites.customer.AddObject(objcust);
order objorder=new order();
objorder.custid=objcust.id;
objorder.amount=500;
objEntites.order.AddObject(objorder);
objEntites.SaveChanges();
transaction.Commit();
}
catch()
{
if (transaction != null) transaction.Rollback();
}
finally
{
if (objEntites != null && objEntites.Connection.State != System.Data.ConnectionState.Closed
&& objEntites.Connection.State != System.Data.ConnectionState.Broken)
objEntites.Connection.Close();
}
Hints:
The finally block is executed even after an exception has occured, hence in case of an exception the exception handling code is executed first, then the code in the finally block. Only if severe exceptions (system errors) - such as a StackOverflowException - occur, it is not executed but you can't handle such kinds of exceptions anyway easily. For more information about this topic please look here.
For SaveChanges you can also use the option System.Data.Objects.SaveOptions.AcceptAllChangesAfterSave, which I prefer because it guarantees that every entity object has its changes accepted after successful save.
BeginTransaction allows also to specify the kind of transaction, but I would not use the parameter options because if you omit it then it creates a transaction as specified by the database's default - so the administrator is still able to change this easily if required. But if you need to specify it, ensure that you don't hardcode it, but allow to configure it in the App.Config or Web.Config file.
Please let me know if that works for you.
I have the following code
try
{
using (var connection = new SqlConnection(Utils.ConnectionString))
{
connection.Open();
using (var cmd = new SqlCommand("StoredProcedure", connection))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
var sqlParam = new SqlParameter("id_document", idDocument);
cmd.Parameters.Add(sqlParam);
int result = cmd.ExecuteNonQuery();
if (result != -1)
return "something";
//do something here
return "something else";
}
}
//do something
}
catch (SqlException ex)
{
return "something AKA didn't work";
}
The question is: Does var connection still get closed if an unexpected error happens between the using brackets ({ })?
The problem is that most of my calls to stored procedures are made this way, and recently I have been getting this error:
System.InvalidOperationException: Timeout expired. The timeout
period elapsed prior to obtaining a connection from the pool. This
may have occurred because all pooled connections were in use and max
pool size was reached.
The other way I access the DB is through nHibernate.
using Statement (C# Reference)
The using statement ensures that Dispose is called even if an
exception occurs while you are calling methods on the object. You can
achieve the same result by putting the object inside a try block and
then calling Dispose in a finally block; in fact, this is how the
using statement is translated by the compiler. The code example
earlier expands to the following code at compile time (note the extra
curly braces to create the limited scope for the object):
Yes, if it gets into the body of the using statement, it will be disposed at the end... whether you reached the end of the block normally, exited via a return statement, or an exception was thrown. Basically the using statement is equivalent to a try/finally block.
Is that the only place you acquire a connection? Has your stored procedure deadlocked somewhere, perhaps, leaving lots of connections genuinely "busy" as far as the client code is concerned?
In terms of your connection pool running out of available connections, if you are in a distributed environment and using many applications to access SQL Server but they all use the same connection string, then they will all be using the same pool on the server. To get around this you can change the connection string for each application by setting the connection WorkstationID to the Environment.MachineName. This will make the server see each connection as different and provide a pool to each machine instead of sharing the pool.
In the below example we even pass in a token to allow an application on the same machine to have multiple pools.
Example:
private string GetConnectionStringWithWorkStationId(string connectionString, string connectionPoolToken)
{
if (string.IsNullOrEmpty(machineName)) machineName = Environment.MachineName;
SqlConnectionStringBuilder cnbdlr;
try
{
cnbdlr = new SqlConnectionStringBuilder(connectionString);
}
catch
{
throw new ArgumentException("connection string was an invalid format");
}
cnbdlr.WorkstationID = machineName + connectionPoolToken;
return cnbdlr.ConnectionString;
}
Replace your above code.. by this.. and check again..
try
{
using (var connection = new SqlConnection(Utils.ConnectionString))
{
connection.Open();
using (var cmd = new SqlCommand("StoredProcedure", connection))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
var sqlParam = new SqlParameter("id_document", idDocument);
cmd.Parameters.Add(sqlParam);
int result = cmd.ExecuteNonQuery();
if (result != -1)
return "something";
//do something here
return "something else";
}
connection.Close();
connection.Dispose();
}
//do something
}
catch (SqlException ex)
{
return "something AKA didn't work";
}
Here's a reference:
http://msdn.microsoft.com/en-us/library/yh598w02(v=vs.80).aspx
What I know is that if you use an object within the using {} clause, that object inherits the IDisposable interface (i.e. SqlConnection inherits DbConnection, and DbConnection inherits IDisposable), which means if you get an exception, any object will be closed and disposed properly.