Find to many solution but not a single solution fit on my scenario.
Problem: I am working on online software which is build under asp.net. From the past few days my application is working slow and some time its crash. When I try to find the issue, then I find that connection pool have connections which are in sleeping mode. I know that some connection are open but not closed properly. In below I will show you My DBManager file. Please review it and give me suggestion which can help me to open and close my connection properly.
Note: exception on connection is thrown when user use application fastly.
My application use many data entry operator which are type with speed. And move between pages again and again.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.SqlClient;
using System.Data;
using System.Configuration;
/// <summary>
/// Summary description for DBManager
/// </summary>
public class DBManager
{
public static SqlConnection _connection;
public static SqlCommand _command;
public static SqlDataReader _reader;
public static SqlDataAdapter _dataAdapter;
public List<SqlParameter> Parameters = new List<SqlParameter>();
public static string _connectionString = "DefaultConnectionString";
SqlTransaction _sqlTransaction;
public DBManager()
{
// TODO: Add constructor logic here
}
public DBManager(SqlTransaction sqlTransaction = null)
{
_sqlTransaction = sqlTransaction;
}
public DBManager(string connectionStringName, SqlTransaction sqlTransaction = null)
{
_connectionString = connectionStringName;
_sqlTransaction = sqlTransaction;
}
public static string CreateConnection()
{
string ConnectionString = ConfigurationManager.ConnectionStrings[_connectionString].ConnectionString;
_connection = new SqlConnection(ConnectionString);
_connection.Open();
return "0";
}
public static void CloseConnection()
{
_connection.Close();
_connection.Dispose();
}
public void AddParameter(string parameterName, object value, SqlDbType sqlDbType, int size)
{
SqlParameter parameter = new SqlParameter(parameterName, sqlDbType, size);
parameter.Value = value;
Parameters.Add(parameter);
}
public void AddParameter(string parameterName, object value, SqlDbType sqlDbType, int size,ParameterDirection parameterDirection)
{
SqlParameter parameter = new SqlParameter(parameterName, sqlDbType, size);
parameter.Value = value;
parameter.Direction = parameterDirection;
Parameters.Add(parameter);
}
public void AddParameter(string parameterName, object value)
{
SqlParameter parameter = new SqlParameter(parameterName,value);
Parameters.Add(parameter);
}
public int ExecuteNonQuery(string procedureName)
{
int result = 0;
try
{
// if (CreateConnection() == "1") { return 0; }
CreateConnection();
_command = new SqlCommand(procedureName, _connection);
if (Parameters.Count != 0)
{
for (int i = 0; i < Parameters.Count; i++)
{
_command.Parameters.Add(Parameters[i]);
}
}
_command.CommandType = CommandType.StoredProcedure;
result = _command.ExecuteNonQuery();
CloseConnection();
_command.Dispose();
}
catch (Exception)
{
CloseConnection();
_command.Dispose();
throw;
}
return result;
}
public SqlDataReader ExecuteReader(string procedureName)
{
SqlDataReader reader;
try
{
CreateConnection();
// if (CreateConnection() == "1") { return reader=0; }
_command = new SqlCommand(procedureName, _connection);
if (Parameters.Count != 0)
{
for (int i = 0; i < Parameters.Count; i++)
{
_command.Parameters.Add(Parameters[i]);
}
}
_command.CommandType = CommandType.StoredProcedure;
reader = _command.ExecuteReader(CommandBehavior.CloseConnection);
CloseConnection();
_command.Dispose();
}
catch (Exception)
{
CloseConnection();
_command.Dispose();
throw;
}
return reader;
}
public DataSet ExecuteDataSet(string procedureName)
{
DataSet dataSet = new DataSet();
try
{
CreateConnection();
_command = new SqlCommand(procedureName, _connection);
if (Parameters.Count != 0)
{
for (int i = 0; i < Parameters.Count; i++)
{
_command.Parameters.Add(Parameters[i]);
}
}
_command.CommandType = CommandType.StoredProcedure;
_dataAdapter = new SqlDataAdapter(_command);
_dataAdapter.Fill(dataSet);
CloseConnection();
_command.Dispose();
_dataAdapter.Dispose();
}
catch (Exception)
{
CloseConnection();
_dataAdapter.Dispose();
_command.Dispose();
throw;
}
return dataSet;
}
public DataTable ExecuteDataTable(string procedureName)
{
DataTable dataTable = new DataTable();
try
{
CreateConnection();
_command = new SqlCommand(procedureName, _connection);
if (Parameters.Count != 0)
{
for (int i = 0; i < Parameters.Count; i++)
{
_command.Parameters.Add(Parameters[i]);
}
}
_command.CommandType = CommandType.StoredProcedure;
_dataAdapter = new SqlDataAdapter(_command);
_dataAdapter.Fill(dataTable);
CloseConnection();
_command.Dispose();
_dataAdapter.Dispose();
}
catch (Exception)
{
CloseConnection();
_dataAdapter.Dispose();
_command.Dispose();
throw;
}
return dataTable;
}
public string ExecuteScalar(string procedureName)
{
string result = "";
try
{
CreateConnection();
_command = new SqlCommand(procedureName, _connection);
if (Parameters.Count != 0)
{
for (int i = 0; i < Parameters.Count; i++)
{
_command.Parameters.Add(Parameters[i]);
}
}
_command.CommandType = CommandType.StoredProcedure;
result = _command.ExecuteScalar().ToString();
CloseConnection();
_command.Dispose();
}
catch (Exception)
{
CloseConnection();
_command.Dispose();
throw;
}
return result;
}
}
Exceptions are:
InnerException System.InvalidOperationException: ExecuteReader requires an open and available Connection. The connection's current state is connecting.
at System.Data.SqlClient.SqlConnection.
InnerException System.InvalidOperationException: Invalid operation. The connection is closed.
at System.Data.ProviderBase.DbConnectionClosed.
InnerException System.NullReferenceException: Object reference not set to an instance of an object.
You are doing it wrong way all together , you should create connection but should not open it , you should open it when needed and close it
I suggest remove this function also and just make use of using
public static string CreateConnection()
{
string ConnectionString = ConfigurationManager.ConnectionStrings[_connectionString].ConnectionString;
_connection = new SqlConnection(ConnectionString);
//remove this line
//_connection.Open();
return "0";
}
you dont need this function also
public static void CloseConnection()
{
_connection.Close();
_connection.Dispose();
}
make use of using as suggested that will help
Best way I suggest is always make use of using and dispose conenction , as below
using(SqlConnection con = new SqlConnection() )
{
}
If you are worrying about it will create too many object, then for information connection with database is pooled means you can sepecify connection pooling information in connection string, so that way you dont have to worry about making connection when you create connection object.
<add name="sqlConnectionString" connectionString="Data
Source=mySQLServer;Initial Catalog=myDatabase;Integrated
Security=True;Connection Timeout=15;Connection Lifetime=0;Min Pool Size=0;Max
Pool Size=100;Pooling=true;" />
above is connection string which take cares of pooling
Sample code , this i how i did in my poroject , if you see the code i disponse connection object every time by making use of using
public class DbHelper
{
#region Private methods
private static OracleConnection GetConnection()
{
string connectionString = DbConnectionString.ConnectionString;
return new OracleConnection(connectionString);
}
private static OracleCommand GetCommand(OracleConnection connection, string commandText, OracleParameter[] param, bool isProcedure)
{
OracleCommand dbCommand = new OracleCommand();
dbCommand.Connection = connection;
dbCommand.CommandText = commandText;
if (param != null)
dbCommand.Parameters.AddRange(param);
if (isProcedure)
dbCommand.CommandType = CommandType.StoredProcedure;
return dbCommand;
}
#endregion
#region public methods
public static DataTable GetDataTable(string commandText, OracleParameter[] odbcPrams, bool isProcedure = false)
{
DataTable dt = new DataTable();
using (OracleConnection ODBCConn = GetConnection())
{
using (OracleCommand dbCommand = GetCommand(ODBCConn, commandText, odbcPrams, isProcedure))
{
ODBCConn.Open();
OracleDataAdapter da = new OracleDataAdapter(dbCommand);
da.Fill(dt);
}
}
return dt;
}
#endregion
}
Related
I have two methods that connect to the database and I try to avoid double code
one of my methods is one that can run alon (open itself SqlConnection and close it)
another method using existing SqlConnection and using SqlTransaction also (I don't want to open another connection and also I don't want to close it)
my first method :
public static List<CSerieses> GetCSerieses(DeliveryReportObject DeliveryReportObject)
{
List<CSerieses> CSerieses = new List<CSerieses>();
try
{
using (SqlConnection openCon = new SqlConnection(connectionString))
{
string query = "SELECT [CSeriesNum],[CCount],[Mark] from [Serieses] " +
"where [TestPrimary]=#deliveryNumber";
SqlCommand command = new SqlCommand(query, openCon);
command.Parameters.AddWithValue("#deliveryNumber", DeliveryReportObject.DeliveryNumber);
openCon.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
CSerieses.Add(new CSerieses(reader.GetString(0), reader.GetInt32(1), reader.GetBoolean(2)));
}
}
openCon.Close();
}
}
catch (Exception ex)
{
LocalPulserDBManagerInstance.WriteLog(ex.StackTrace, ex.Message);
}
return CSerieses;
}
The method that using on the transaction :
public static List<CSerieses> GetCSerieses(DeliveryReportObject DeliveryReportObject,
SqlConnection co,SqlTransaction tran)
{
List<CSerieses> CSerieses = new List<CSerieses>();
try
{
using (co)
{
string query = "SELECT [CSeriesNum],[CCount],[Mark] from [Serieses] " +
"where [TestPrimary]=#deliveryNumber";
SqlCommand command = new SqlCommand(query, co, tran);
command.Parameters.AddWithValue("#deliveryNumber", DeliveryReportObject.DeliveryNumber);
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
CSerieses.Add(new CSerieses(reader.GetString(0), reader.GetInt32(1), reader.GetBoolean(2)));
}
}
}
}
catch (Exception ex)
{
LocalPulserDBManagerInstance.WriteLog(ex.StackTrace, ex.Message);
}
return CSerieses;
}
I try to combine them :
public static List<CSerieses> GetCSerieses(DeliveryReportObject DeliveryReportObject,
SqlConnection co = null,SqlTransaction tran = null)
{
List<CSerieses> CSerieses = new List<CSerieses>();
try
{
using (co ?? new SqlConnection(connectionString))
{
if (co.IsOpened() == false)
{
co.Open();
}
string query = "SELECT [CSeriesNum],[CCount],[Mark] from [Serieses] " +
"where [TestPrimary]=#deliveryNumber";
SqlCommand command = new SqlCommand(query, co, tran);
if(tran != null)
{
command.Transaction = tran;
}
command.Parameters.AddWithValue("#deliveryNumber", DeliveryReportObject.DeliveryNumber);
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
CSerieses.Add(new CSerieses(reader.GetString(0), reader.GetInt32(1), reader.GetBoolean(2)));
}
}
}
}
catch (Exception ex)
{
LocalPulserDBManagerInstance.WriteLog(ex.StackTrace, ex.Message);
}
return CSerieses;
}
It does not work for me. I have no idea how to check if it null in using and if yes to create a new instance of SqlConnection that should close at the end of the using statement
And I do it the right way anyway?
This is a major problem:
using (co ?? new SqlConnection(connectionString))
If co is passed in, then you don't own it - the caller does - so: you shouldn't be disposing it. What I would suggest here is:
bool ownConnection = false;
try
{
if (co is null)
{
ownConnection = true;
co = new SqlConnection(...);
co.Open();
}
// your code here
}
finally
{
if (ownConnection)
{
co?.Dispose();
}
}
or wrap that up in a helper - perhaps a custom disposable that takes a connection and connection string:
public readonly struct ConnectionWrapper : IDisposable
{
private readonly bool owned;
public SqlConnection Connection { get; }
public ConnectionWrapper(SqlConnection connection, string connectionString)
{
if (connection is null)
{
owned = true;
Connection = new SqlConnection(connectionString);
Connection.Open();
}
else
{
owned = false;
Connection = connection;
}
}
public void Dispose()
{
if (owned)
{
Connection?.Dispose();
}
}
}
then you can just use:
using var wrapped = new ConnectionWrapper(co, connectionString);
// your code, using wrapped.Connection
This seems that kind of situation that perfectly fits the overload concept.
The GetCSerieses method should have two versions, the first one builds its own connection and transaction, the second one takes both a non optional connection and a non optional transaction. The first one, after creating the connection and the transaction calls the second one.
Now if a third method requires a call the GetCSerieses could pass its own connection and transaction, while a call without them will be handled by the first overload
public static List<CSerieses> GetCSerieses(DeliveryReportObject DeliveryReportObject)
{
using(SqlConnection con = new SqlConnection(......))
{
try
{
con.Open();
using(SqlTransaction tran = con.BeginTransaction())
{
return GetCSerieses(DeliveryReportObject, con, tran);
}
// Or, if you don't need a transaction you could call the
// overload passing null
// return GetCSerieses(DeliveryReportObject, con, null);
}
catch(Exception ex)
{
LocalPulserDBManagerInstance.WriteLog(ex.StackTrace, ex.Message);
return null; // ?? or return new List<CSerieses>();
}
}
}
public static List<CSerieses> GetCSerieses(DeliveryReportObject DeliveryReportObject, SqlConnection co, SqlTransaction tran)
{
List<CSerieses> CSerieses = new List<CSerieses>();
try
{
// We don't own the connection and the transaction object.
// Whoever passed them to us is responsible of their disposal.
string query = "......";
SqlCommand command = new SqlCommand(query, co, tran);
command.Transaction = tran;
....
}
catch (Exception ex)
{
LocalPulserDBManagerInstance.WriteLog(ex.StackTrace, ex.Message);
}
return CSerieses;
}
I'm using .Net Compact 3.5 Windows 7 CE.
I have an application with about 50 users, I have it setup so that I would get an email every time a database transaction failed, with the query.
Every so often I would get an email with a stack trace that starts like this:
System.ArgumentException: Value does not fall within the expected range.
at System.Data.SqlClient.SqlParameterCollection.Validate(Int32 index, SqlParameter value)
at System.Data.SqlClient.SqlParameterCollection.AddWithoutEvents(SqlParameter value)
at System.Data.SqlClient.SqlParameterCollection.Add(SqlParameter value)
at MedWMS.Database.startSqlConnection(String query, SqlParameter[] parameters, SqlConnection connection, SqlCommand cmd)
at MedWMS.Database.<>c__DisplayClasse.b__8()
at MedWMS.Database.retry(Action action)
at MedWMS.Database.executeNonQuery(String query, SqlParameter[] parameters, String connectionString)...
The SQL query which causes this issue is not always the same. I run the same query seconds after I get the email in SQL Server Management Studio with no issues.
I would like to know why this could be happening. This is my first question on SO so please let me know if I'm doing something wrong. I would be happy to answer any questions to provide more detail.
This is a sample of the code that would cause this error:
SqlParameter[] parameters = new SqlParameter[1];
parameters[0] = new SqlParameter("#salesOrder", this.salesOrderNumber);
string query = #"
Select InvTermsOverride from SorMaster where SalesOrder = Convert(int, #salesOrder) and InvTermsOverride = '07' --07 is for COD";
DataTable dt = Database.executeSelectQuery(query, parameters, Country.getCurrent().getSysproConnectionStrReportServer());
This is the query that actually gets passed:
Select InvTermsOverride from SorMaster where SalesOrder = Convert(int, '000000001138325') and InvTermsOverride = '07' --07 is for COD
Here is the relevant methods from the Database class:
public static DataTable executeSelectQuery(String query, SqlParameter[] parameters, string connectionString)
{
DataTable dt = new DataTable();
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand cmd = null;
try
{
retry(() =>
{
cmd = startSqlConnection(query, parameters, connection, cmd);
using (SqlDataReader reader = cmd.ExecuteReader())
{
dt.Load(reader);
}
});
}
catch (Exception ex)
{
onDbConnectionCatch(cmd, ex);
}
finally
{
cmd.Dispose();
connection.Close();
}
}
return dt;
}
public static void executeNonQuery(String query, SqlParameter[] parameters, string connectionString)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand cmd = null;
try
{
retry(() =>
{
cmd = startSqlConnection(query, parameters, connection, cmd);
cmd.ExecuteNonQuery();
});
}
catch (Exception ex)
{
onDbConnectionCatch(cmd, ex);
}
finally
{
cmd.Dispose();
connection.Close();
}
}
}
private static void retry(Action action)
{
int retryCount = 3;
int retryInterval = 1000;
Exception lastException = null;
for (int retry = 0; retry < retryCount; retry++)
{
try
{
if (retry > 0)
System.Threading.Thread.Sleep(retryInterval);
action();
lastException = null;
return;
}
catch (Exception ex)
{
lastException = ex;
}
}
if (lastException != null)
{
throw lastException;
}
}
private static SqlCommand startSqlConnection(String query, SqlParameter[] parameters, SqlConnection connection, SqlCommand cmd)
{
if (connection.State != ConnectionState.Open)
{
connection.Open();
}
cmd = new SqlCommand(query, connection);
if (parameters != null)
{
foreach (SqlParameter sp in parameters)
{
if (sp != null)
{
cmd.Parameters.Add(sp);
}
}
}
return cmd;
}
private static void onDbConnectionCatch(SqlCommand cmd, Exception ex)
{
try
{
new BigButtonMessageBox("", "Unable connect to database").ShowDialog();
sendEmailWithSqlQuery(cmd, ex);
}
catch
{
}
}
private static void sendEmailWithSqlQuery(SqlCommand cmd, Exception ex)
{
string query2 = "cmd was null";
if (cmd != null)
{
query2 = cmd.CommandText;
foreach (SqlParameter p in cmd.Parameters)
{
query2 = query2.Replace(p.ParameterName, "'" + p.Value.ToString() + "'");
}
}
InternetTools.sendEmail("DB ERROR", ex.ToString() + "\r\n" + query2);
}
I had the same issue as Can't solve "Sqlparameter is already contained by another SqlparameterCollection"
For some reason SQL CE has a different error.
Because of my retry method, I couldn't reuse the SqlParameter object, still not sure why it's not allowed
Anyways I changed
cmd.Parameters.Add(sp);
to
cmd.Parameters.Add(sp.ParameterName, sp.Value);
i have a problem with my windows service program
i try to create a windows service to read data from a database and insert them into an other database but when i read data from first db and copy it into a dataset i have a problem, i can't read those data from data set and insert it into an other dataset to insert it into the other database
here's my code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Timers;
using System.Data.SqlClient;
using System.Collections;
using System.Data.Sql;
namespace WindowSeriveDemo
{
public partial class Service1 : ServiceBase
{
string connection = #"Data Source=DEVELOPER-PC\DEVELOPER;Initial Catalog=NMSys;User ID=sa;Password=2649940931";
string connection2 = #"Data Source=DEVELOPER-PC\DEVELOPER;Initial Catalog=Dpardazesh;User ID=sa;Password=2649940931";
private Timer ServiceTimer = new Timer();
private int inProcess = 0;
public const string SP_dataRead = "usp_Read_Data_From_DpardazeshDB";
public const string SP_insertData = "usp_Write_Data_Into_NMSysDB";
private IEnumerable<DataRow> item;
//Timer timer1 = new Timer();
public Service1()
{
InitializeComponent();
setupTimer();
}
protected override void OnStart(string[] args)
{
//ServiceTimer.Elapsed += new ElapsedEventHandler(timer1_Elapsed);
}
private void setupTimer()
{
ServiceTimer.Elapsed += new ElapsedEventHandler(ServiceTimer_Tick);
ServiceTimer.Interval = 10000;
ServiceTimer.Enabled = true;
ServiceTimer.Start();
}
protected override void OnStop()
{
//timer1.Enabled = false;
}
private void SyncDatabases()
{
inProcess = 1;
SqlConnection conn2 = new SqlConnection(connection2);
try
{
conn2.Open();
SqlCommand cmd = new SqlCommand(SP_dataRead);
winservDS dataSet = new winservDS();
cmd.CommandType = CommandType.StoredProcedure;
//AddParameter(cmd);
SqlDataAdapter adapter = new SqlDataAdapter(SP_dataRead, conn2);
adapter.Fill(dataSet, dataSet.usp_Read_Data_From_DpardazeshDB.TableName);
dataSet.AcceptChanges();
return ;
}
catch (Exception ex)
{
string text = ex.Message;
}
inProcess = 0;
}
private static SqlCommand cmdGetIdentity;
public winservDS InsertUsers(IDictionary ids)
{
inProcess = 1;
SqlConnection conn = new SqlConnection(connection);
SqlConnection conn2 = new SqlConnection(connection2);
conn.Open();
conn2.Open();
winservDS updates = new winservDS();
DataRow dr;
dr = updates.usp_Read_Data_From_DpardazeshDB.NewRow();
foreach (DictionaryEntry i in ids)
{
if (i.Value == null)
dr[i.Key.ToString()] = DBNull.Value;
else
dr[i.Key.ToString()] = i.Value;
}
updates.usp_Read_Data_From_DpardazeshDB.Rows.Add(dr);
try
{
winservDS.usp_Read_Data_From_DpardazeshDBDataTable tbl = updates.usp_Read_Data_From_DpardazeshDB;
//Create the adapter initial
SqlDataAdapter dataAdapter = new SqlDataAdapter();
dataAdapter.InsertCommand = WriteDatabase(conn);
//Roll Back the changes if some one error have
dataAdapter.ContinueUpdateOnError = false;
cmdGetIdentity = new SqlCommand("SELECT ##IDENTITY", conn);
dataAdapter.RowUpdated += new SqlRowUpdatedEventHandler(HandleRowUpdated);
dataAdapter.Update(tbl.Select("", "", DataViewRowState.Added));
return updates;
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
inProcess = 0;
}
private static void HandleRowUpdated(object sender, SqlRowUpdatedEventArgs e)
{
if ((e.Status == UpdateStatus.Continue) && (e.StatementType == StatementType.Insert))
{
e.Row["id"] = Convert.ToInt32 (cmdGetIdentity.ExecuteScalar());
e.Row.AcceptChanges();
}
}
private static SqlCommand WriteDatabase(SqlConnection conn)
{
SqlCommand cmd = new SqlCommand(SP_insertData);
cmd.Parameters.Clear();
cmd.CommandType = CommandType.StoredProcedure;
SqlParameterCollection pc = cmd.Parameters;
pc.Add(CreateParameter("#fHitType", System.Data.SqlDbType.Int));
pc.Add(CreateParameter("#DateOfHit", System.Data.SqlDbType.DateTime));
pc.Add(CreateParameter("#TimeOfHit", System.Data.SqlDbType.Int));
pc.Add(CreateParameter("#fEmpid", System.Data.SqlDbType.Int));
cmd.ExecuteNonQuery();
return cmd;
}
private static SqlParameter CreateParameter(string p, SqlDbType sqlDbType)
{
SqlParameter parameter = new SqlParameter("#" + p, sqlDbType);
parameter.SourceColumn = p;
return parameter;
}
private void ServiceTimer_Tick(object sender, EventArgs e)
{
if (inProcess == 0)
{
ServiceTimer.Stop();
SyncDatabases();
CopyData();
//InsertUsers(ids);
ServiceTimer.Start();
}
}
private void CopyData()
{
DataSet ds1 = new winservDS();
DataSet ds2 = new In_Out_RecordsDS();
foreach (DataRow item in ds1.Tables[0].Rows)
{
ds2.Tables[0].Rows.Add(item);
}
} here
Try to use a DataReader for the source recordsets, read all columns into variables and then insert them into second database with an INSERT Statement. Be avoid of your transaction log when copying thousands of records...
We have this class to use like SingleTon to return the same connection and transaction(isolation level read commited)(we use CRUD):
public class SharedDbMySQL : DatabaseMySQL
{
private static DatabaseMySQL sConn;
private SharedDbMySQL()
{
}
public static DatabaseMySQL GetInstance()
{
return GetInstance(TipoDados.Dados);
}
public static DatabaseMySQL GetInstance(TipoDados OpcoesBD)
{
if (sConn == null)
sConn = new DatabaseMySQL(OpcoesBD);
return sConn;
}
}
With the SQL(microsoft)... the error dont occours... only the Mysql.
We insert first the "NotaFiscalEntrada"...
After we insert the products of this "NotaFiscalEntrada" on this method(and we have the error here):
public static void InsereAtualizaNotaFiscalEntradaProduto(List<nf_entrada_produto> entity, int IDNFEntrada, bool SharedConnection, bool LastOperation)
{
DatabaseMySQL db;
MySqlCommand cmd = new MySqlCommand();
if (SharedConnection)
db = SharedDbMySQL.GetInstance();
else
db = new DatabaseMySQL();
try
{
cmd.Connection = db.Conn;
cmd.Transaction = db.BeginTransaction();
ONF_Entrada_Produto OpNFProduto = new ONF_Entrada_Produto(cmd);
foreach (nf_entrada_produto Item in entity)
{
Item.ValorICMSST = 0;
Item.IDNFEntrada = IDNFEntrada;
Item.IDEmpresa = BusinessLogicLayer.ObjetosGlobais.DadosGlobais.EmpresaGlobal.ID;
if (Item.ID == 0)
{
if (!OpNFProduto.Add(Item))
throw OpNFProduto.LastError;
}
else
{
if (!OpNFProduto.Update(Item))
throw OpNFProduto.LastError;
}
}
if (LastOperation || !SharedConnection)
{
db.CommitTransaction();
db.Disconnect();
}
}
catch (Exception ex)
{
db.RollBackTransaction();
db.Disconnect();
throw ex;
}
}
The error is when we insert the Products (code above)
"Lock wait timeout exceeded; try restarting transaction".
We found something about the deadlock... the lost of the connection can be the error, how to resolve it?I think thats a server error? thanks all.
The Problem was on the METHODS... I created again a new connection and not taking it from the singleton...
And the database deadlock the tables and other connections try to change it too... and there is the problems.
cmd.Connection = new db.Connect();
cmd.Connection = db.Conn;
replaced to
cmd.Connection = db.Conn;
Inside of class db(singleton):
MySqlConnection conn;
public MySqlConnection Conn
{
get
{
if ((conn == null) || (conn.State == System.Data.ConnectionState.Closed))
{
Connect();
}
return conn;
}
set
{
conn = value;
}
}
public override void Connect()
{
RetornaDadosIniParaClasse();
conn = new MySqlConnection(StringConnection);
try
{
conn.Open();
if (conn.State == System.Data.ConnectionState.Closed)
{
throw new AccessDatabaseException("Conexão com o banco de dados firebird fechada");
}
}
catch (Exception ex)
{
throw new AccessDatabaseException(ex.Message);
}
}
It taked a lot of time because its difficult to see the error... we debuged it a lot to find it.
First time user - hoping this is in the right format:
I am wanting to know if I can create SQL DbParameter values, esp on the ParamaterName.
My current code is:
DbCommand dbCommand = SqlDb.GetStoredProcCommand(uspCommand);
DbParameter ProcessedFileName = dbCommand.CreateParameter();
ProcessedFileName.DbType = DbType.String;
ProcessedFileName.ParameterName = "#FileName";
ProcessedFileName.Value = pstrProcessedFileName;
dbCommand.Parameters.Add(ProcessedFileName);
I am wanting to add:
ProcessedFileName.ParameterName = "#FileName1";
ProcessedFileName.ParameterName = "#FileName2";
ProcessedFileName.ParameterName = "#FileName3";
ProcessedFileName.ParameterName = "#FileName4";
with the #FileNames coming from an array.
Something like this should work:
DbCommand dbCommand = SqlDb.GetStoredProcCommand(uspCommand);
foreach(String param in MyParameters)
{
DbParameter ProcessedFileName = dbCommand.CreateParameter();
ProcessedFileName.DbType = DbType.String;
ProcessedFileName.ParameterName = param;
ProcessedFileName.Value = pstrProcessedFileName;
dbCommand.Parameters.Add(ProcessedFileName);
}
best way to do this is put them in Dictionary, because you will need value also
Dictionary<string, string> params = new Dictionary<string,string>();
and just add them many as you want
params.Add("#FileName1", "my_filename")
etc...
and then
foreach(var param in params)
dbCommand.Parameters.AddWithValue(param.Key, param.Value);
Creating dynamic SQL DbParameter values
This is very helpful when you are going to create project where there is dynamic database, or may in future you are going to migrate / switch database .
Here is step by step solution
step 1) Create Parameter structure
public struct Parameter
{
public string ParameterName { get; set; }
public ParameterDirection Direction { get; set; }
public DbType DbType { get; set; }
public object Value { get; set; }
public string SourceColumn { get; set; }
public int Size { get; set; }
}
Step 2) Create database handling class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Configuration;
using System.Data.Common;
using MySql.Data.MySqlClient;
using MySql.Data;
using Oracle.DataAccess;
using Oracle.DataAccess.Client;
public class DBManagement
{
string connectionStr;
DbConnection con;
DbCommand cmd;
DbDataAdapter AD;
DataSet ds;
DbParameter[] sp;
IDBManagement Iobj = null;
public DBManagement()
{
this.Initialize();
}
void Initialize()
{
try
{
switch (ConfigurationManager.AppSettings["ActiveDatabase"].ToUpper())
{
case "MSSQL":
connectionStr = ConfigurationManager.ConnectionStrings["MSSQLConnectionString"].ConnectionString;
con = new SqlConnection();
cmd = new SqlCommand();
AD = new SqlDataAdapter();
break;
case "ORACLE":
connectionStr = ConfigurationManager.ConnectionStrings["OracleConnectionString"].ConnectionString;
con = new OracleConnection();
cmd = new OracleCommand();
AD = new OracleDataAdapter();
break;
case "MYSQL":
connectionStr = ConfigurationManager.ConnectionStrings["MYSQLConnectionString"].ConnectionString;
con = new MySqlConnection();
cmd = new MySqlCommand();
AD = new MySqlDataAdapter();
break;
default:
break;
}
con.ConnectionString = connectionStr;
cmd.Connection = con;
}
catch (Exception ex)
{
}
}
public DataSet ExecuteProcedure(string procName, CommandType cmdType, Parameter[] DBParameters = null)
{
try
{
cmd.CommandText = procName;
cmd.CommandType = cmdType;
cmd.Parameters.Clear();
if (DBParameters != null && DBParameters.Length > 0)
{
sp = DBParameters.ToParamerArray(cmd);
cmd.Parameters.AddRange(sp);
}
ds = new DataSet();
AD.SelectCommand = cmd;
AD.Fill(ds);
return ds;
}
catch (Exception ex)
{
throw ex;
}
}
}
Step 3) Convert parameter as per database
public static partial class GlobalExtensionFunctions
{
public static DbParameter[] ToParamerArray(this Parameter[] parameters,DbCommand cmd)
{
DbParameter[] sp = new DbParameter[parameters.Length];
int i = 0;
foreach (Parameter parameter in parameters)
{
// DbParameter p = cmd.CreateParameter();
sp[i] = cmd.CreateParameter();
sp[i].ParameterName = parameter.ParameterName;
sp[i].Value = parameter.Value;
sp[i].Direction = string.IsNullOrEmpty(Convert.ToString(parameter.Direction)) || parameter.Direction==0 ? ParameterDirection.Input : parameter.Direction;
sp[i].DbType = parameter.DbType;
sp[i].SourceColumn = parameter.SourceColumn;
sp[i].Size = parameter.Size;
i++;
}
return sp;
}
}
Step 4) Get Data
DBManagement c = new DBManagement();
public DataSet GetGetTestList(int testId)
{
Parameter[] p = new Parameter[1];
p[0].ParameterName = "#TestId";
p[0].Value = testId;
p[0].DbType = DbType.Int32;
return c.ExecuteProcedure(Procedures.TestDetails, CommandType.StoredProcedure,p);
}
Now use dataset or datatable and enjoy! :)
Abe - thanks - you got me in the right direction. Here is what I ended up doing:
inside my foreach loop, I'm calling my method:
foreach (DataRow row in GlobalClass.NAVdataTable.Rows)
{
GlobalClass.AddToDbCommand(ref dBCommand, row["FieldName"].ToString(), row["Value"].ToString());
connection.Open();
SqlDb.ExecuteNonQuery(dBCommand);
connection.Close();
dBCommand.Parameters.Clear();
}
and then my AddToDbCommand method contains:
public static void AddToDbCommand(ref DbCommand dbCommand, string FieldName, string FieldValue)
{
string FieldNameParameter = "#" + FieldName;
DbParameter dbParameter = dbCommand.CreateParameter();
dbParameter.ParameterName = FieldNameParameter;
dbParameter.Value = FieldValue;
dbCommand.Parameters.Add(dbParameter);
}
Refactored as an extension to DbCommand, also fieldName is left without #, so you need to pass # or : prefixes and fieldValue is set to object type (not only string).
public static class DbCommandExtensions
{
public static void AddParam(this DbCommand dbCommand, string fieldName, object fieldValue)
{
string fieldNameParameter = fieldName;
DbParameter dbParameter = dbCommand.CreateParameter();
dbParameter.ParameterName = fieldNameParameter;
dbParameter.Value = fieldValue;
dbCommand.Parameters.Add(dbParameter);
}
}