Random errors with my connection in c# with sql server - c#

I have created this class for work with my DB connection.
Applications works fine but one or two times in a day i get this errors.
Invalid operation, the connection is completed. when running query: (different queries)
or other times I received this error.
Value Timeout expired. The timeout period expired before completion of the operation or the server is not responding. This error has occurred while attempting to connect to the server Principal.
and rare errors:
Error at the transport level when receiving results from the server. (provider: Session Provider, error:? 19 - You can not use the connection nf sica) when running query
Thread aborted. when executing query:
ExecuteNonQuery requires an open and available Connection. The current state of the connection is closed. when running query
My db Class(resumed):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Net.Mail;
using System.Web;
using System.IO;
namespace BBDD
{
public class clsBBDD
{
private string strConexion;
private int intTransaciones;
private SqlTransaction Transaccion;
private SqlConnection Conexion;
public clsBBDD(string ParamConexion)
{
strConexion = ParamConexion;
intTransaciones = 0;
}
public int Execute(string Sql)
{
return ExecutePrivado(Sql);
}
public int Execute(string Sql, bool Force)
{
return ExecutePrivado(Sql, Force);
}
private int ExecutePrivado(string Sql,bool Force = false)
{
int result = 0;
SqlCommand sentencia;
try
{
if (!Force && !Sql.ToUpper().Contains("WHERE") && !Sql.ToUpper().Contains("INSERT"))
{
throw new Exception("Sentencia Update o Delete sin WHERE");
}
if (intTransaciones > 0)
{
sentencia = new SqlCommand(Sql, Conexion, Transaccion);
}
else
{
abrirConexion();
sentencia = new SqlCommand(Sql, Conexion);
}
sentencia.CommandTimeout = 0;
result = sentencia.ExecuteNonQuery();
cerrarConexion();
}
catch (Exception e)
{
SendMailError(Sql, e);
result = 0;
}
return result;
}
public bool AbrirTrans()
{
try
{
intTransaciones += 1;
if (intTransaciones == 1)
{
abrirConexion();
Transaccion = Conexion.BeginTransaction();
}
}
catch (Exception e)
{
SendMailError("Error al abrir transacción", e);
return false;
}
return true;
}
public bool CerrarTrans(bool CommitTrans)
{
try
{
intTransaciones -= 1;
if (intTransaciones == 0)
{
if (CommitTrans)
{
Transaccion.Commit();
}
else
{
Transaccion.Rollback();
}
cerrarConexion();
}
}
catch (Exception e)
{
SendMailError("Error al cerrar transacción", e);
return false;
}
return true;
}
public object GetValue(string Sql)
{
object resultado = null;
try
{
SqlCommand sentencia = getCommand(Sql);
SqlDataReader reader = sentencia.ExecuteReader();
while (reader.Read())
{
resultado = reader[0];
}
reader.Close();
cerrarConexion();
}
catch (Exception e)
{
SendMailError(Sql, e);
resultado = null;
}
return resultado;
}
public DataRow GetValuesInRow(string sql)
{
DataRowCollection dsr;
DataRow result;
try
{
dsr = GetDSr(sql);
if (dsr.Count > 0)
{
result = dsr[0];
}
else
{
result = null;
}
}
catch (Exception e)
{
SendMailError(sql,e);
result = null;
}
return result;
}
public DataSet GetDS(string Sql)
{
DataSet result = new DataSet();
SqlDataAdapter adapter;
try
{
SqlCommand command = getCommand(Sql);
adapter = new SqlDataAdapter(command);
adapter.Fill(result);
cerrarConexion();
}
catch (Exception e)
{
SendMailError(Sql, e);
result = null;
}
return result;
}
public DataRowCollection GetDSr(string sql)
{
DataRowCollection result;
try
{
DataSet ds = GetDS(sql);
result = ds.Tables[0].Rows;
}
catch (Exception e)
{
SendMailError(sql, e);
result = null;
}
return result;
}
private void abrirConexion()
{
Conexion = new SqlConnection(strConexion);
if (Conexion.State == ConnectionState.Closed)
{
Conexion.Open();
}
}
private void cerrarConexion(bool Force = false)
{
try
{
if (intTransaciones == 0 && Conexion != null)
{
Conexion.Close();
}
}
catch
{
}
}
private SqlCommand getCommand(string Sql)
{
SqlCommand result;
if (intTransaciones == 0)
{
abrirConexion();
}
result = Conexion.CreateCommand();
if (intTransaciones > 0)
{
result.Transaction = Transaccion;
}
result.CommandText = Sql;
result.CommandTimeout = 0;
return result;
}
}
}
How I Initialize class:
public partial class frmBase : System.Web.UI.Page
{
protected clsBBDD BD;
protected void Page_Init(object sender, EventArgs e)
{
BD = new clsBBDD(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
clsBase.BD = BD;
}
}
All my webforms heritage from frmBase and all clases heritatge from clsBase
For use BD I call like this:
string sql;
DataRowCollection DSTrabajos;
sql = "UPDATE tabletest SET validacion = '" + ValidacionText + "', ModificadoPor = '" + Username + "' WHERE referencia = " + ReferenciaID;
BD.Execute(sql);
sql = "SELECT orderID FROM TrabajosINBOX where referencia = " + ReferenciaID;
DSTrabajos = BD.GetDSr(sql);
foreach (DataRow r in DSTrabajos)
{
//more code inside
}
My connection string params
Data Source=ServerIP;Initial Catalog=BBDDNAME;User ID=USER;Password=***********;Max Pool Size=500;MultipleActiveResultSets=true

Related

System.TypeInitializationException when referencing dll

I'm trying to create a Data Framework in the form of a .dll so that I can reference it when creating new projects, as opposed to reinventing the wheel with each project I create.
I have an app.config in which I store my SQL connections, a class that uses the app.config to build my SQL ConnectionString (ConnectionStrings.cs) and a Logic class (Logic.cs) that'll build whatever objects I require from the SQL Server.
Here's the classes in the .dll:
ConnectionStrings.cs:
using System.Configuration;
using System.Data.SqlClient;
namespace DataFramework
{
public static class ConnectionStrings
{
static string _liveConnectionString = ConfigurationManager.ConnectionStrings["LiveDataSource"].ConnectionString;
static string _liveMISConnectionString = ConfigurationManager.ConnectionStrings["LiveDataSource_MIS"].ConnectionString;
static string _devConnectionString = ConfigurationManager.ConnectionStrings["DevDataSource"].ConnectionString;
static string _devMISConnectionString = ConfigurationManager.ConnectionStrings["DevDataSource_MIS"].ConnectionString;
public static SqlConnection CreateLiveConnection
{
get { return new SqlConnection(_liveConnectionString); }
}
public static SqlConnection CreateLiveMISConnection
{
get { return new SqlConnection(_liveMISConnectionString); }
}
public static SqlConnection CreateDevConnection
{
get { return new SqlConnection(_devConnectionString); }
}
public static SqlConnection CreateDevMISConnection
{
get { return new SqlConnection(_devMISConnectionString); }
}
}
}
Logic.cs:
using System;
using System.Threading.Tasks;
using System.Data;
using System.Data.SqlClient;
namespace DataFramework
{
public class Logic
{
SqlConnection liveConnection = ConnectionStrings.CreateLiveMISConnection;
SqlConnection devMISConnection = ConnectionStrings.CreateDevMISConnection;
public bool IsConnecting { get; set; }
public string ConnectionMessage { get; set; }
public async Task<DataTable> ResultDataTable(bool connectToLive, string commandText, CommandType commandType)
{
DataTable dt = new DataTable();
using (SqlCommand command = new SqlCommand())
{
try
{
command.CommandType = commandType;
command.CommandTimeout = 360000000;
switch (connectToLive)
{
case true:
command.CommandText = commandText;
command.Connection = liveConnection;
if (liveConnection.State == ConnectionState.Connecting)
{
IsConnecting = true;
ConnectionMessage = "Connecting to Data Source...";
}
if (liveConnection.State != ConnectionState.Closed)
liveConnection.Close();
if (liveConnection.State != ConnectionState.Open)
{
liveConnection.Open();
IsConnecting = false;
ConnectionMessage = "";
}
break;
case false:
command.CommandType = commandType;
command.CommandText = "";
command.Connection = devMISConnection;
if (devMISConnection.State == ConnectionState.Connecting)
{
IsConnecting = true;
ConnectionMessage = commandText;
}
if (devMISConnection.State != ConnectionState.Closed)
devMISConnection.Close();
if (devMISConnection.State != ConnectionState.Open)
{
devMISConnection.Open();
IsConnecting = false;
ConnectionMessage = "";
}
break;
}
using (SqlDataReader reader = await command.ExecuteReaderAsync())
{
dt.Load(reader);
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message, "An Error Has Occured", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
}
finally
{
if (devMISConnection.State != ConnectionState.Closed)
devMISConnection.Close();
if (liveConnection.State != ConnectionState.Closed)
liveConnection.Close();
}
}
return dt;
}
}
}
I include this dll as a reference in the app that I'm writing:
using System.Data;
using System.Threading.Tasks;
using System.Windows.Forms;
using DataFramework;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
DataTable dt = new DataTable();
DataFramework.Logic logic = new Logic();
public Form1()
{
InitializeComponent();
}
private async void Form1_Load(object sender, EventArgs e)
{
dt = await Task.Run(() => logic.ResultDataTable(true, "SELECT * FROM MIS.dbo.ETL_Table", CommandType.StoredProcedure));
}
}
}
The code throws the exception here:
SqlConnection liveConnection = ConnectionStrings.CreateLiveMISConnection;
So why, when I'm initializing the class, do I get this issue?
When you reference a DLL (or project) from another project, the app.config from the top most project is used. So, if you have your DataFramework being called from your WinformsApp, then your WinformsApp needs to have the right config settings in it. By default, it will ignore any app.config in the DataFramework. A bit frustrating at times! Copy your settings from your DataFramework app.config in to the WinformsApp app.config and it will work.
Another unrelated observation is that you have the following:
"SELECT * FROM MIS.dbo.ETL_Table", CommandType.StoredProcedure
The command type should be text and not a stored procedure.

Reduce Multiple Calls To Database

I have a code a base that iterates through a list of records and does the follwing
'select * from Table to see if fields exist
then later in the interation
'select * from Table to retreive some data
then farther down in the interation
select field1, field2 from Table to get certain pieces of information
I need to do all of these functions for each record. Would the process speed up if I called the query once for each record and hold the data in a datatable and retreive what I need from there? Or is there another more efficient way to not have to make 3 db calls to the same table which will speed up the process?
You can cache query data to System.Data.DataTable. To simplify things I has written CMyDynaset class, which populates DataTable with data from DB. Here how to use it for example with MySQL:
using System;
using System.Data.Common;
using MySql.Data.MySqlClient;
namesapce MyProg
{
class Program
{
private const string strMyConnection = "Host=localhost;Database=mydb;User Id=myuser;Password=mypsw";
static void Main(string[] args)
{
using (MySqlConnection myConnection = new MySqlConnection(strMyConnection))
{
using (MyDb.CMyDynaset dyn = new MyDb.CMyDynaset(myConnection, MySqlClientFactory.Instance))
{
// populate dyn.Table (System.Data.DataTable)
dyn.Init("select * from Table");
dyn.Load();
// access fields
foreach (DataColumn column in dyn.Table.Columns)
{
// ...
}
// get data
long nCountAll = dyn.Table.Rows.Count; // rows count
foreach (DataRow row in dyn.Table.Rows)
{
Object val1 = row[1]; // acess by index
Object val2 = row["id"]; // acess by name
// ...
}
// update data
dyn.Table.Rows[0]["name"] = "ABC";
dyn.Update();
}
}
}
}
}
CMyDynaset class (CMyDynaset.cs):
// CMyDynaset.cs
using System;
using System.Data.Common;
namespace MyDb
{
/// <summary>
/// Summary description for CMyDynaset.
/// </summary>
public class CMyDynaset : IDisposable
{
public System.Data.DataTable Table = null;
// private
private DbConnection myConnection = null;
private DbProviderFactory myFactory = null;
private DbDataAdapter dataAdap = null;
private DbCommandBuilder cmdBld = null;
private bool bIsSchema = false;
public CMyDynaset(DbConnection conn, DbProviderFactory factory)
{
this.myConnection = conn;
this.myFactory = factory;
}
#region IDisposable Members
public void Dispose()
{
if (this.Table != null)
{
this.Table.Dispose();
this.Table = null;
}
if (this.cmdBld != null)
{
this.cmdBld.Dispose();
this.cmdBld = null;
}
if (this.dataAdap != null)
{
this.dataAdap.Dispose();
this.dataAdap = null;
}
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
#endregion
// Init
public void Init(string strSelect)
{
DbCommand cmdSel = this.myConnection.CreateCommand();
cmdSel.CommandText = strSelect;
this.dataAdap = this.myFactory.CreateDataAdapter();
this.dataAdap.SelectCommand = cmdSel;
this.cmdBld = this.myFactory.CreateCommandBuilder();
this.cmdBld.DataAdapter = this.dataAdap;
this.Table = new System.Data.DataTable();
// schema
this.bIsSchema = false;
}
public void AddParameter(string name, object value)
{
DbParameter param = this.dataAdap.SelectCommand.CreateParameter();
param.ParameterName = name;
param.Value = value;
this.dataAdap.SelectCommand.Parameters.Add(param);
}
public void AddParameter(DbParameter param)
{
this.dataAdap.SelectCommand.Parameters.Add(param);
}
// Open, Close
private void Open(ref bool bClose)
{
if (this.myConnection.State == System.Data.ConnectionState.Closed)
{
this.myConnection.Open();
bClose = true;
}
if (!this.bIsSchema)
{ // schema
this.dataAdap.FillSchema(this.Table, System.Data.SchemaType.Mapped);
this.bIsSchema = true;
}
}
private void Close(bool bClose)
{
if (bClose)
this.myConnection.Close();
}
// Load, Update
public void Load()
{
bool bClose = false;
try
{
this.Table.Clear();
this.Open(ref bClose);
this.dataAdap.Fill(this.Table);
}
catch (System.Exception ex)
{
throw ex;
}
finally
{
this.Close(bClose);
}
}
public void Update()
{
bool bClose = false;
try
{
this.Open(ref bClose);
this.dataAdap.Update(this.Table);
}
catch (System.Exception ex)
{
throw ex;
}
finally
{
this.Close(bClose);
}
}
}
}

Eliminate code duplication (Work with DB)

I've coded a dll to execute SQL commands.
And I have some code duplication in my public methods. Is there some way to avoid code duplication?
Method 1:
public Object ExecuteScalar(String command)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand commandScalar = GetSqlCommand(strCommand: command);
object result = new object();
using (commandScalar)
{
try
{
connection.StatisticsEnabled = statisticsEnabled;
connection.Open();
result = commandScalar.ExecuteScalar();
}
catch (Exception exception)
{
throw new Exception(String.Format("Не удалось выполнить команду: {0}", commandScalar.CommandText), exception);
}
finally
{
connection.Close();
}
}
return result;
if (connection.StatisticsEnabled)
AddDictionary(connection.RetrieveStatistics());
return result;
}
}
Method 2:
public void ExecuteNonQuery(String command, List<SqlParameter> lsParams = null)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand commandNonQuery = GetSqlCommand(strCommand: command, lsParams: lsParams, log: logger);
using(commandNonQuery)
{
try
{
connection.StatisticsEnabled = statisticsEnabled;
connection.Open();
commandNonQuery.ExecuteNonQuery();
}
catch (Exception exception)
{
throw new Exception(String.Format("Не удалось выполнить команду: {0}", commandNonQuery.CommandText), exception);
}
finally
{
connection.Close();
}
}
if (connection.StatisticsEnabled)
AddDictionary(connection.RetrieveStatistics());
}
}
Method 3:
public List<IDataRecord> ExecuteReader(String strCommand, List<SqlParameter> lsParams = null)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand sqlCommandRead = GetSqlCommand(strCommand: strCommand, lsParams: lsParams, log: logger);
List<IDataRecord> lsDataRecord = new List<IDataRecord>();
using (sqlCommandRead)
{
try
{
connection.StatisticsEnabled = statisticsEnabled;
connection.Open();
using (SqlDataReader sqlDataReader = sqlCommandRead.ExecuteReader())
{
while (sqlDataReader.Read())
{
lsDataRecord.Add((IDataRecord)sqlDataReader);
}
}
}
catch (Exception exception)
{
throw new Exception(String.Format("Не удалось выполнить команду: {0}", sqlCommandRead.CommandText), exception);
}
finally
{
connection.Close();
}
}
if (connection.StatisticsEnabled)
this.AddDictionary(connection.RetrieveStatistics());
return lsDataRecord;
}
}
Method that generates SQL command to execute:
private static SqlCommand GetSqlCommand(string strCommand, List<SqlParameter> lsParams = null, Logger log = null)
{
lsParams = null ?? new List<SqlParameter>();
log = null ?? LogManager.GetLogger("noname");
if (strCommand == null || strCommand == "")
{
throw new ArgumentException("Передан пустой (или null) текст команды.", "strCommand");
}
int parametersRequired = strCommand.Split('#').Length - 1;
if (parametersRequired != lsParams.Count)
{
String strParameters = null;
foreach (var item in lsParams)
{
strParameters += item.ParameterName + " : " + item.Value + "\n";
}
strParameters = null ?? "No parameters";
throw new ArgumentException(String.Format("При формировании SQL - команды выявлено, что число требуемых параметров: {0} не соответствует числу переданных: {1}\n"
+ "Команда:\n{2}\nПараметры:\n{3}", parametersRequired, lsParams.Count, strCommand, strParameters), "lsParams");
}
SqlCommand sqlCommand = new SqlCommand(strCommand);
foreach (var item in lsParams)
{
sqlCommand.Parameters.Add(item);
}
return sqlCommand;
}
public class SqlHelper
{
private readonly bool statisticsEnabled;
public SqlHelper(bool statisticsEnabled)
{
this.statisticsEnabled = statisticsEnabled;
}
public T ExecuteSqalar<T>(SqlCommand command)
{
return Execute(command, c => (T) c.ExecuteScalar());
}
public void ExecuteNonQuery(SqlCommand command)
{
Execute(command, c => c.ExecuteNonQuery());
}
public List<IDataRecord> ExecuteReader(SqlCommand command)
{
return Execute<List<IDataRecord>>(command, c =>
{
var lsDataRecord = new List<IDataRecord>();
using (SqlDataReader sqlDataReader = command.ExecuteReader())
{
while (sqlDataReader.Read())
{
lsDataRecord.Add(sqlDataReader);
}
}
});
}
public T Execute<T>(SqlCommand command, Func<SqlCommand, T> processFunction)
{
using (var connection = new SqlConnection("CONNECTION_STRING"))
{
object result = new object();
using (command)
{
try
{
connection.StatisticsEnabled = statisticsEnabled;
connection.Open();
result = processFunction(command);
}
catch (Exception exception)
{
throw new Exception(String.Format("Не удалось выполнить команду: {0}", command.CommandText), exception);
}
finally
{
connection.Close();
}
}
if (connection.StatisticsEnabled)
AddDictionary(connection.RetrieveStatistics());
return (T)result;
}
}
private void AddDictionary(IDictionary retrieveStatistics)
{
// TODO:
}
}

How to manage transaction when dealing with N-tier architecture?

Business entity class:
public class TextileApplication
{
private System.Int64 _UserId;
private System.String _ApplicationNo;
private System.Int32 _SchemeId;
}
Code on .cs page:
protected void ibtnSubmitFirstPanel_Click(object sender, EventArgs e)
{
TextileApplication _TextileApplication = new TextileApplication();
_TextileApplication.UserId = 1;//static
_TextileApplication.ApplicationNo = GenerateApplicationNo();
_TextileApplication.SchemeId = Convert.ToInt16(rblScheme.SelectedValue);
string i = blTextileApplication.InsertTextileApplication(_TextileApplication);
if (Convert.ToInt16(i.Split(',').GetValue(0)) > 0)
{
// insert into another table
}
else
{
// rollback
}
}
Business Access class:
public static string InsertTextileApplication(TextileApplication _TextileApplication)
{
string i = "0";
try
{
daTextileApplication _daTextileApplication = new daTextileApplication();
object [] o = _daTextileApplication.InsertTextileApplication(_TextileApplication);
i = o[0].ToString();
}
catch (Exception ex)
{
LogErrorToLogFile logFile = new LogErrorToLogFile();
logFile.LogError(ex);
throw ex;
}
return i;
}
Data access class:
public object[] InsertTextileApplication(TextileApplication _TextileApplication)
{
try
{
pList = new List<SqlParameter>();
pList.Add(new SqlParameter("#UserId", _TextileApplication.UserId));
pList.Add(new SqlParameter("#ApplicationNo", _TextileApplication.ApplicationNo));
pList.Add(new SqlParameter("#SchemeId", _TextileApplication.SchemeId));
SqlParameter _AppNoOut = new SqlParameter("#AppNoOut", SqlDbType.VarChar,50);
_AppNoOut.Direction = ParameterDirection.Output;
pList.Add(_AppNoOut);
object[] o = sa.ExecuteQueryWithOutParameters("SPInsertTextileApplication", pList);
return o;
}
catch (Exception ex)
{
// logFile.LogError(ex);
// throw ex;
}
}
SQL access class:
public class SqlAccess
{
public object[] ExecuteQueryWithOutParameters(String procedureName, List<SqlParameter> param)
{
int count = 0;
SqlCommand cmd = new SqlCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = procedureName;
cmd.Parameters.Clear();
if (param != null)
{
foreach (SqlParameter p in param)
{
cmd.Parameters.Add(p);
if (p.Direction == ParameterDirection.Output)
{
count++;
}
}
}
try
{
connect();
cmd.Connection = con;
cmd.ExecuteNonQuery();
//iRet = Convert.ToInt32(retValReference.Value);
object[] obj = new object[count];
count = 0;
if (param != null)
{
for (int i = 0; i < param.Count; i++)
{
if (param[i].Direction == ParameterDirection.Output)
{
obj[count] = param[i].Value.ToString();
count++;
}
}
}
return obj;
}
catch (Exception ex)
{
throw ex;
}
finally
{
closeconnect();
}
}
}
I know this is complex architecture. I need to apply transaction concept then how to do it??
Read up on the System.Transaction namespace. A transactionscipe if quite powerfully and perfectuy suitable to project transactions even through a not exactly well designed multi tiered architecture such as yours.
Alternatively a unit of work pattern would be suitable.
But man, you really try to write as much code as possible instead of using established patterns. the lower 3 classes in your list should never be written by a human.

Call multiple SQL Server stored procedures in a transaction

For usage in my current project I've created a class that allows me to call SQL Server async.
My code looks like this:
internal class CommandAndCallback<TCallback, TError>
{
public SqlCommand Sql { get; set; }
public TCallback Callback { get; set; }
public TError Error { get; set; }
}
class MyCodes:SingletonBase<MyCodes>
{
private static string _connString = #"Data Source=MyDB;Initial Catalog=ED;Integrated Security=True;Asynchronous Processing=true;Connection Timeout=0;Application Name=TEST";
private MyCodes() { }
public void SetSystem(bool production)
{
_connString =
string.Format(#"Data Source=MyDB;Initial Catalog={0};Integrated Security=True;Asynchronous Processing=true;Connection Timeout=0;Application Name=TEST", production ? "ED" : "TEST_ED");
}
public void Add(string newCode, Action<int> callback, Action<string> error)
{
var conn = new SqlConnection(_connString);
SqlCommand cmd = conn.CreateCommand();
cmd.CommandTimeout = 0;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = #"ADD_CODE";
cmd.Parameters.Add("#NEW", SqlDbType.NVarChar).Value = newCode;
cmd.Parameters.Add("#NewId", SqlDbType.Int).Direction = ParameterDirection.Output;
try
{
cmd.Connection.Open();
}
catch (Exception ex)
{
error(ex.ToString());
return;
}
var ar = new CommandAndCallback<Action<int>, Action<string>> { Callback = callback, Error = error, Sql = cmd };
cmd.BeginExecuteReader(Add_Handler, ar, CommandBehavior.CloseConnection);
}
private static void Add_Handler(IAsyncResult result)
{
var ar = (CommandAndCallback<Action<int>, Action<string>>)result.AsyncState;
if (result.IsCompleted)
{
try
{
ar.Sql.EndExecuteReader(result);
ar.Callback(Convert.ToInt32(ar.Sql.Parameters["#NewId"].Value));
}
catch (Exception ex)
{
ar.Error(ex.Message);
}
}
else
{
ar.Error("Error executing SQL");
}
}
public void Update(int codeId, string newCode, Action callback, Action<string> error)
{
var conn = new SqlConnection(_connString);
SqlCommand cmd = conn.CreateCommand();
cmd.CommandTimeout = 0;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = #"UPDATE_CODE";
cmd.Parameters.Add("#CODE_ID", SqlDbType.Int).Value = codeId;
cmd.Parameters.Add("#NEW", SqlDbType.NVarChar).Value = newCode;
try
{
cmd.Connection.Open();
}
catch (Exception ex)
{
error(ex.ToString());
return;
}
var ar = new CommandAndCallback<Action, Action<string>> { Callback = callback, Error = error, Sql = cmd };
cmd.BeginExecuteReader(Update_Handler, ar, CommandBehavior.CloseConnection);
}
private static void Update_Handler(IAsyncResult result)
{
var ar = (CommandAndCallback<Action, Action<string>>)result.AsyncState;
if (result.IsCompleted)
{
try
{
ar.Sql.EndExecuteReader(result);
ar.Callback();
}
catch (Exception ex)
{
ar.Error(ex.Message);
}
}
else
{
ar.Error("Error executing SQL");
}
}
}
This may look like too much of code, but it lets me call it as so:
private void Add_Click(object sender, EventArgs e)
{
MyCodes.Instance.Add("Test",Success,Error)
}
private void Success(int newId)
{
MessageBox.Show(newId.ToString(), "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void Error(string error)
{
MessageBox.Show(error, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
Above code works just fine for me, I'm able to do every call async.
Problem that I have right now is to do multiple calls as transaction - I would like to update 2 codes and add one new.
Normally I would call update, then in success handler call second update, and in handler to second update I would call add that would return new id.
Something like:
-UPDATE CODE
|-UPDATE CODE
|-ADD CODE (only this one return something)
But I would like to call all of those as transaction, so if add code would break updates would rollback.
Question:
Is it possible to call multiple async queries as a transaction?
Can I call my above methods as transaction or do I must create separate method to call my procedures as one? (I would like to avoid this one because it's just copying the same code from one method to another)
I would like to add that I use .NET 3.5 so await and other nice features aren't an option.
string cnnString =WebConfigurationManager.ConnectionStrings["MyString"].ConnectionString;
SqlConnection cnn = new SqlConnection(cnnString);
SqlTransaction transaction;
cnn.Open();
transaction = cnn.BeginTransaction();
try
{
// Command Objects for the transaction
SqlCommand cmd1 = new SqlCommand("sproc1", cnn);
SqlCommand cmd2 = new SqlCommand("sproc2", cnn);
cmd1.CommandType = CommandType.StoredProcedure;
cmd2.CommandType = CommandType.StoredProcedure;
cmd1.Parameters.Add(new SqlParameter("#Param1", SqlDbType.NVarChar, 50));
cmd1.Parameters["#Param1"].Value = paramValue1;
cmd1.Parameters.Add(new SqlParameter("#Param2", SqlDbType.NVarChar, 50));
cmd1.Parameters["#Param2"].Value = paramValue2;
cmd2.Parameters.Add(new SqlParameter("#Param3", SqlDbType.NVarChar, 50));
cmd2.Parameters["#Param3"].Value = paramValue3;
cmd2.Parameters.Add(new SqlParameter("#Param4", SqlDbType.NVarChar, 50));
cmd2.Parameters["#Param4"].Value = paramValue4;
cmd1.ExecuteNonQuery();
cmd2.ExecuteNonQuery();
transaction.Commit();
}
catch (SqlException sqlEx)
{
transaction.Rollback();
}
finally
{
cnn.Close();
cnn.Dispose();
}
Yes, it is possible. Simply call SqlConnection.BeginTransaction before your first call. Make sure you assign the returned SqlTransaction object to each SqlCommand.Transaction in the chain and call SqlTransaction.Commit() at the end.
public class Command
{
public string sql { get; set; }
public CommandType cmdType { get; set; }
public Dictionary<string, object> parameter { get; set; } = null;
}
private Command insertInvoice(Invoice invoice)
{
try
{
Dictionary<string, object> parameterLocal = new Dictionary<string, object>();
parameterLocal.Add("p_customerId", invoice.customerId);
parameterLocal.Add("p_invoiceNo", invoice.invoiceNo);
parameterLocal.Add("p_invoiceDate", invoice.invoiceDate);
parameterLocal.Add("p_invoiceAmount", invoice.invoiceAmount);
parameterLocal.Add("p_withInvoice", invoice.withInvoice);
return (new Command { sql = "sp_insertInvoice", cmdType = CommandType.StoredProcedure, parameter = parameterLocal });
}
catch (Exception ex)
{
throw ex;
}
}
private Command insertInvoiceModel(InvoiceModel invoiceModel)
{
try
{
Dictionary<string, object> parameterLocal = new Dictionary<string, object>();
parameterLocal.Add("p_invoiceNo", invoiceModel.invoiceNo);
parameterLocal.Add("p_model", invoiceModel.model);
parameterLocal.Add("p_quantity", invoiceModel.quantity);
parameterLocal.Add("p_unitPrice", invoiceModel.unitPrice);
return (new Command { sql = "sp_insertInvoiceModel", cmdType = CommandType.StoredProcedure, parameter = parameterLocal });
}
catch (Exception ex)
{
throw ex;
}
}
List<Command> commandList = new List<Command>();
cmd = insertInvoice(invoicesave);
commandList.Add(cmd);
cmd = insertInvoiceModel(invoiceModelSave);
commandList.Add(cmd);
try
{
erplibmain.erpDac.runOleDbTransaction(commandList);
}
catch (Exception ex)
{
throw ex;
}
public void runOleDbTransaction(List<Command> commandList)
{
OleDbConnection erpConnection = new OleDbConnection(ErpDalMain.connectionstring);
erpConnection.Open();
OleDbCommand erpCommand = erpConnection.CreateCommand();
OleDbTransaction erpTrans;
// Start a local transaction
erpTrans = erpConnection.BeginTransaction();
// Assign transaction object for a pending local transaction
erpCommand.Connection = erpConnection;
erpCommand.Transaction = erpTrans;
try
{
foreach (Command cmd in commandList)
{
erpCommand.CommandText = cmd.sql;
erpCommand.CommandType = cmd.cmdType;
foreach (KeyValuePair<string, object> entry in cmd.parameter)
{
erpCommand.Parameters.AddWithValue(entry.Key, entry.Value);
}
erpCommand.ExecuteNonQuery();
erpCommand.Parameters.Clear();
}
erpTrans.Commit();
}
catch (Exception e)
{
try
{
erpTrans.Rollback();
}
catch (OleDbException ex)
{
if (erpTrans.Connection != null)
{
throw ex;
}
}
throw e;
}
finally
{
erpConnection.Close();
}
}

Categories