New DbConnection performance - c#

This is my function for selecting data from database:
public DataTable SelectDataTable(string selectStatement, string connectionString)
{
using (var oracleConnection = new OracleConnection(connectionString))
{
using (var command = new OracleCommand(selectStatement, oracleConnection))
{
return SelectDataTable(command);
}
}
}
public DataTable SelectDataTable(OracleCommand command)
{
var result = new DataTable();
try
{
var adapter = new OracleDataAdapter(command);
adapter.Fill(result);
} catch (OracleException ex)
{
throw NewDatabaseException("Error on fill data table.", command.CommandText, ex);
}
return result;
}
I call this function very often and I am little afraid that there can be performance issue because I am still creating new DbConnection. Is it better to crete only one DbConnection, store it in field and use it multiplt time?

Set MinPoolSize to value greater than 0 in your connection string, so instead of creating new connections, it will be taken from the pool of existing connections. Also set MaxPoolSize at number higher than MinPoolSize.

Related

Transaction with Dapper .NET

I currently have two classes in one layer, which perform the inclusion of data in the database:
using Dapper;
using System;
using System.Data.SqlClient;
using System.Linq;
namespace repositories
{
public class DAOBook
{
private readonly string _connection;
public DAOBook(string databaseConnection)
{
_connection = databaseConnection;
}
public bool IncludeBook(string title)
{
try
{
using (var connection = new SqlConnection(_connection))
{
var sql = $#"
INSERT INTO books
(title)
VALUES
('{title}' ";
var result = connection.Execute(sql);
return result != 0;
}
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
}
}
using Dapper;
using System;
using System.Data.SqlClient;
using System.Linq;
namespace repositories
{
public class DAOBookTag
{
private readonly string _connection;
public DAOBookTag(string databaseConnection)
{
_connection = databaseConnection;
}
public bool IncludeBookTag(string tag, int userid)
{
try
{
using (var connection = new SqlConnection(_connection))
{
var sql = $#"
INSERT INTO bookTag
(tag, userid)
VALUES
('{tag}', {userid} ";
var result = connection.Execute(sql);
return result != 0;
}
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
}
}
In my service layer, I can call these two classes normally, and they insert them into the database.
try
{
var connectionString = "<my_connection_string>";
var daoBook = new DAOBook(connectionString);
var daoBookTag = new DAOBookTag(connectionString);
dao.IncludeBook("Alice");
dao.IncludeBookTag("Romance", 1);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
However, I want to place a transaction control, so that in case of an error in the insertion of the second class, it undoes the transaction in catch, something like this:
try
{
var connectionString = "<my_connection_string>";
var daoBook = new DAOBook(connectionString);
var daoBookTag = new DAOBookTag(connectionString);
// begin transaction
dao.IncludeBook("Alice");
dao.IncludeBookTag("Romance", 1);
// commit
}
catch (Exception ex)
{
// rollback
throw new Exception($"{ex.Message}", ex);
}
I know it must be a beginner's question, but I can't seem to find a way for the two persistence classes to share the same transaction.
I saw an example of implementing Dapper's transaction control, but I don't know how I could implement it in my service layer (instead of the persistence layer).
https://riptutorial.com/dapper/example/22536/using-a-transaction
Thank you
There are two ways of handling transactions in ADO.NET; the usually preferred mechanism is an ADO.NET transaction, i.e. BeginTransaction. This has limitations, but is very efficient and maps natively into most providers. The key restriction of an ADO.NET transaction is that it only spans one connection, and your connection must last at least as long as the transaction.
In terms of Dapper usage, you must also pass the transaction into the call; for example:
using (var conn = new SqlConnection(connectionString))
{
connection.Open();
using (var tran = connection.BeginTransaction())
{
// ... your work
tran.Commit();
}
}
where "your work" here effectively uses the same conn and tran instances, using:
var result = conn.Execute(sql, args, transaction: tran);
The much lazier way is to use TransactionScope. This is simpler to use, but
more more involved. I usually advise against it, but it works.
You should also parameterize:
var sql = #"
INSERT INTO bookTag (tag, userid)
VALUES (#tag, #userId)";
var result = connection.Execute(sql, new { tag, userId });
Use a TransactionScope:
using (var transactionScope = new TransactionScope())
{
var connectionString = "<my_connection_string>";
var daoBook = new DAOBook(connectionString);
var daoBookTag = new DAOBookTag(connectionString);
// begin transaction
dao.IncludeBook("Alice");
dao.IncludeBookTag("Romance", 1);
//commit
transactionScope.Complete();
}
https://dapper-tutorial.net/transaction

Closing MySql datareader connection

So this is a little bit code-ceptionlike.
I have a function that is checking the last ID in a table, this function is called within another function. At the end of that function, I have another function that's opening another datareader.
Error:
There is already an open Datareader associated with this connection which must be closed first.
getLastIdfromDB()
public string getLastIdFromDB()
{
int lastIndex;
string lastID ="";
var dbCon = DB_connect.Instance();
if (dbCon.IsConnect())
{
MySqlCommand cmd2 = new MySqlCommand("SELECT ID FROM `competitor`", dbCon.Connection);
try
{
MySqlDataReader reader = cmd2.ExecuteReader();
while (reader.Read())
{
string item = reader2["ID"].ToString();
lastIndex = int.Parse(item);
lastIndex++;
lastID = lastIndex.ToString();
}
}
catch (Exception ex)
{
MessageBox.Show("Error:" + ex.Message);
}
}
return lastID;
}
This function is later-on used in this function:
private void addPlayerBtn_Click(object sender, EventArgs e)
{
ListViewItem lvi = new ListViewItem(getLastIdFromDB());
.........................................^
... HERE
...
... irrelevant code removed
.........................................
var dbCon = DB_connect.Instance();
if (dbCon.IsConnect())
{
MySqlCommand cmd = new MySqlCommand("INSERT INTO `competitor`(`ID`, `Name`, `Age`) VALUES(#idSql,#NameSql,#AgeSql)", dbCon.Connection);
cmd.Parameters.AddWithValue("#idSql", getLastIdFromDB());
cmd.Parameters.AddWithValue("#NameSql", playerName.Text);
cmd.Parameters.AddWithValue("#AgeSql", playerAge.Text);
try
{
cmd.ExecuteNonQuery();
listView1.Items.Clear();
}
catch (Exception ex)
{
MessageBox.Show("Error:" + ex.Message);
dbCon.Connection.Close();
}
finally
{
updateListView();
}
}
}
What would be the best way for me to solve this problem and in the future be sure to close my connections properly?
UPDATE: (per request, included DB_connect)
class DB_connect
{
private DB_connect()
{
}
private string databaseName = "simhopp";
public string DatabaseName
{
get { return databaseName; }
set { databaseName = value; }
}
public string Password { get; set; }
private MySqlConnection connection = null;
public MySqlConnection Connection
{
get { return connection; }
}
private static DB_connect _instance = null;
public static DB_connect Instance()
{
if (_instance == null)
_instance = new DB_connect();
return _instance;
}
public bool IsConnect()
{
bool result = true;
try
{
if (Connection == null)
{
if (String.IsNullOrEmpty(databaseName))
result = false;
string connstring = string.Format("Server=localhost; database={0}; UID=root;", databaseName);
connection = new MySqlConnection(connstring);
connection.Open();
result = true;
}
}
catch (Exception ex)
{
Console.Write("Error: " + ex.Message);
}
return result;
}
public void Close()
{
connection.Close();
}
}
}
You are trying to have multiple open readers on the same connection. This is commonly called "MARS" (multiple active result sets). MySql seems to have no support for it.
You will have to either limit yourself to one open reader at a time, or use more than one connection, so you can have one connection for each reader.
My suggestion would be to throw away that singleton-like thingy and instead use connection pooling and proper using blocks.
As suggested by Pikoh in the comments, using the using clause indeed solved it for me.
Working code-snippet:
getLastIdFromDB
using (MySqlDataReader reader2 = cmd2.ExecuteReader()) {
while (reader2.Read())
{
string item = reader2["ID"].ToString();
lastIndex = int.Parse(item);
lastIndex++;
lastID = lastIndex.ToString();
}
}
Your connection handling here is not good. You need to ditch the DB_connect. No need to maintain a single connection - just open and close the connection each time you need it. Under the covers, ADO.NET will "pool" the connection for you, so that you don't actually have to wait to reconnect.
For any object that implements IDisposable you need to either call .Dispose() on it in a finally block, or wrap it in a using statement. That ensures your resources are properly disposed of. I recommend the using statement, because it helps keep the scope clear.
Your naming conventions should conform to C# standards. Methods that return a boolean should be like IsConnected, not IsConnect. addPlayerBtn_Click should be AddPlayerButton_Click. getLastIdFromDB should be GetlastIdFromDb or getLastIdFromDatabase.
public string GetLastIdFromDatabase()
{
int lastIndex;
string lastID ="";
using (var connection = new MySqlConnection(Configuration.ConnectionString))
using (var command = new MySqlCommand("query", connection))
{
connection.Open();
MySqlDataReader reader = cmd2.ExecuteReader();
while (reader.Read())
{
string item = reader2["ID"].ToString();
lastIndex = int.Parse(item);
lastIndex++;
lastID = lastIndex.ToString();
}
}
return lastID;
}
Note, your query is bad too. I suspect you're using a string data type instead of a number, even though your ID's are number based. You should switch your column to a number data type, then select the max() number. Or use an autoincrementing column or sequence to get the next ID. Reading every single row to determine the next ID and incrementing a counter not good.

Cannot connect to an external database in SQLCLR

I am trying to create a function that will connect to an external database and run a query. When I run my function I am getting this error:
Data access is not allowed in this context. Either the context is a function or method not marked with DataAccessKind.Read or SystemDataAccessKind.Read, is a callback to obtain data from FillRow method of a Table Valued Function, or is a UDT validation method.
I don't think that I am doing anything strange, but here is my code. Please let me know if you spot something odd. I really don't know what else to try.
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString createFile()
{
string theQuery = "SELECT * FROM A_TABLE;";
string theConnection = "Data Source=serverName;Initial Catalog=DatabaseBane;Persist Security Info=True;User ID=login;Password=thePassword";
SqlConnection DBConnect = new SqlConnection(theConnection);
try
{
//My code is breaking here************************************
DBConnect.Open();
}
catch (Exception e)
{
return "Happening in the connect: " + e.Message;
}
SqlDataAdapter dataAdapter = new SqlDataAdapter(theQuery, DBConnect);
DataTable HRNdata = new DataTable();
dataAdapter.Fill(HRNdata);
FileStream stream = new FileStream(#"C:\TestFiles\demo.xls", FileMode.OpenOrCreate);
ExcelWriter writer = new ExcelWriter(stream);
writer.BeginWrite();
Dictionary<string, int> noteDict = new Dictionary<string, int>();
foreach (DataRow r in HRNdata.Rows)
{
try
{
noteDict.Add(r["Note"].ToString(), 1);
}
catch
{
noteDict[r["Note"].ToString()] += 1;
}
}
int counter = 1;
foreach (KeyValuePair<string, int> pair in noteDict)
{
writer.WriteCell(1, counter, pair.Key);
writer.WriteCell(2, counter, pair.Value);
counter++;
}
writer.EndWrite();
stream.Close();
try
{
DBConnect.Close();
}
catch (Exception e)
{
return e.Message;
}
return "";
}
You will need to add an annotation to your method along the lines DataAccessKind.Read.

C# "There is already an open DataReader associated with this Connection which must be closed first [duplicate]

I have a Parallel.Foreach loop
var options = new ParallelOptions();
options.MaxDegreeOfParallelism = 1;
Parallel.ForEach(urlTable.AsEnumerable(),drow =>
{
using (var WCC = new MasterCrawlerClass())
{
WCC.MasterCrawlBegin(drow);
}
}
This loop calls the class and loops through all my DataRows, however each of those datarows either does a DataTable fill, or does an update command to a MySQL DB. The code I have for both of those is below.
private static DataTable DTTable(string mysqlQuery, string queryName)
{
DataTable DTTableTable = new DataTable();
try
{
MySqlDataAdapter DataDTTables = new MySqlDataAdapter(mysqlQuery, MySQLProcessing.MySQLStatic.Connection);
DataTable DataDTTablesDT = new DataTable();
DataDTTables.SelectCommand.CommandTimeout = 240000;
DataDTTables.Fill(DataDTTablesDT);
DTTableTable = DataDTTablesDT;
}
catch (Exception ex)
{
GenericLogging("Failed MySQLquery: " + ex.Message.ToString(), "MySQLProcessor", "DTTable", "", "MysqlError", "", queryName, mysqlQuery);
}
return DTTableTable;
}
private static void MySQLInsertUpdate(string MySQLCommand, string mysqlcommand_name)
{
try
{
MySqlCommand MySQLCommandFunc = new MySqlCommand(MySQLCommand, MySQLProcessing.MySQLStatic.Connection);
MySQLCommandFunc.CommandTimeout = 240000;
MySQLCommandFunc.ExecuteNonQuery();
}
catch (Exception ex)
{
GenericLogging("Failed MySQLquery: " + ex.Message.ToString(), "MySQLProcessor", "DTTable", "", "MysqlError", "", mysqlcommand_name, MySQLCommand);
}
}
The thing is WCC contains 10 or so voids, each of these voids accesses the MySQL functions at least once. So if locking is the answer, is it possible to create 1 locking function for all voids? If so how? If there is another way, please let me know
Thanks!
Okay as suggested i consolidated the questions
The code has now been updated to reflect locking, please see below.
static readonly object _object = new object();
public static DataTable DTTable(string mysqlQuery, string queryName)
{
lock (_object)
{
DataTable DTTableTable = new DataTable();
try
{
using (MySqlDataAdapter DataDTTables = new MySqlDataAdapter(mysqlQuery, MySQLProcessing.MySQLStatic.Connection))
{
using (DataTable DataDTTablesDT = new DataTable())
{
DataDTTables.SelectCommand.CommandTimeout = 240000;
DataDTTables.Fill(DataDTTablesDT);
DTTableTable = DataDTTablesDT;
DataDTTables.Dispose();
}
}
}
catch (Exception ex)
{
GenericLogging("Failed MySQLquery: " + ex.Message.ToString(), "MySQLProcessor", "DTTable", "", "MysqlError", "", queryName, mysqlQuery);
}
return DTTableTable;
}
}
This is the only code that calls the DataReader, how can there be two open data readers if there is a lock?
The problem is that ADO.NET data providers generally do not allow for more than one open data reader at a time per connection. SQL Server has the concept of multiple active result sets (MARS), but as far as I know MySQL does not yet support it.
You will probably need to specify a different connection other than MySQLProcessing.MySQLStatic.Connection. There is nothing stopping you from using more than one connection. The problem here is that connections are expensive resources so you are supposed to use them sparingly.
You're using the same connection at the same time.
Do you have several thread ? Because it seems that 2 threads use the same connection to concurrently make a call.

MultiThreading error: There is already an open DataReader associated with this Connection which must be closed first

I have a Parallel.Foreach loop
var options = new ParallelOptions();
options.MaxDegreeOfParallelism = 1;
Parallel.ForEach(urlTable.AsEnumerable(),drow =>
{
using (var WCC = new MasterCrawlerClass())
{
WCC.MasterCrawlBegin(drow);
}
}
This loop calls the class and loops through all my DataRows, however each of those datarows either does a DataTable fill, or does an update command to a MySQL DB. The code I have for both of those is below.
private static DataTable DTTable(string mysqlQuery, string queryName)
{
DataTable DTTableTable = new DataTable();
try
{
MySqlDataAdapter DataDTTables = new MySqlDataAdapter(mysqlQuery, MySQLProcessing.MySQLStatic.Connection);
DataTable DataDTTablesDT = new DataTable();
DataDTTables.SelectCommand.CommandTimeout = 240000;
DataDTTables.Fill(DataDTTablesDT);
DTTableTable = DataDTTablesDT;
}
catch (Exception ex)
{
GenericLogging("Failed MySQLquery: " + ex.Message.ToString(), "MySQLProcessor", "DTTable", "", "MysqlError", "", queryName, mysqlQuery);
}
return DTTableTable;
}
private static void MySQLInsertUpdate(string MySQLCommand, string mysqlcommand_name)
{
try
{
MySqlCommand MySQLCommandFunc = new MySqlCommand(MySQLCommand, MySQLProcessing.MySQLStatic.Connection);
MySQLCommandFunc.CommandTimeout = 240000;
MySQLCommandFunc.ExecuteNonQuery();
}
catch (Exception ex)
{
GenericLogging("Failed MySQLquery: " + ex.Message.ToString(), "MySQLProcessor", "DTTable", "", "MysqlError", "", mysqlcommand_name, MySQLCommand);
}
}
The thing is WCC contains 10 or so voids, each of these voids accesses the MySQL functions at least once. So if locking is the answer, is it possible to create 1 locking function for all voids? If so how? If there is another way, please let me know
Thanks!
Okay as suggested i consolidated the questions
The code has now been updated to reflect locking, please see below.
static readonly object _object = new object();
public static DataTable DTTable(string mysqlQuery, string queryName)
{
lock (_object)
{
DataTable DTTableTable = new DataTable();
try
{
using (MySqlDataAdapter DataDTTables = new MySqlDataAdapter(mysqlQuery, MySQLProcessing.MySQLStatic.Connection))
{
using (DataTable DataDTTablesDT = new DataTable())
{
DataDTTables.SelectCommand.CommandTimeout = 240000;
DataDTTables.Fill(DataDTTablesDT);
DTTableTable = DataDTTablesDT;
DataDTTables.Dispose();
}
}
}
catch (Exception ex)
{
GenericLogging("Failed MySQLquery: " + ex.Message.ToString(), "MySQLProcessor", "DTTable", "", "MysqlError", "", queryName, mysqlQuery);
}
return DTTableTable;
}
}
This is the only code that calls the DataReader, how can there be two open data readers if there is a lock?
The problem is that ADO.NET data providers generally do not allow for more than one open data reader at a time per connection. SQL Server has the concept of multiple active result sets (MARS), but as far as I know MySQL does not yet support it.
You will probably need to specify a different connection other than MySQLProcessing.MySQLStatic.Connection. There is nothing stopping you from using more than one connection. The problem here is that connections are expensive resources so you are supposed to use them sparingly.
You're using the same connection at the same time.
Do you have several thread ? Because it seems that 2 threads use the same connection to concurrently make a call.

Categories

Resources