Populate C# DataTable Asynchronously - c#

I'm adding async implementations to all my SQL base classes used in my WebAPI projects. I'm fairly new to the TAP paradigm so I'm still learning.
I know, thanks to other posts, that spawning a thread via Task.Run() does not have any performance benefits in an ASP.NET context. So I'm being extra careful with my implementations.
I've changed my QueryExecutor method to the async implementation below. But cannot figure out what the best way to load the DataTable is. I'm guessing I would ideally want to use reader.ReadAsync() to populate the DataTable but it seems there isn't anything available in the .NET 4.5 framework for that.
So I wanted to ask would it be worth writing my own extension method such as DataTable.LoadAsync(DbDataReader reader)? I kind of don't want to if it can be helped since it won't be nearly as fool-proof as managed .Net code.
Let me know what you guys think.
private async Task<DataTable> ExecuteQueryInternalAsync(string commandText, CommandType commandType, SqlConnection sqlConnection, SqlTransaction transaction, params SqlParameter[] parameters)
{
using (SqlCommand cmd = new SqlCommand(commandText, sqlConnection) { CommandType = commandType, CommandTimeout = this.config.MainConnectionTimeoutInSeconds })
{
if (transaction != null)
cmd.Transaction = transaction;
if (parameters != null)
{
foreach (var parameter in parameters)
{
if (parameter != null)
{
if (parameter.Value == null)
parameter.Value = DBNull.Value;
cmd.Parameters.Add(parameter);
}
}
}
if (sqlConnection.State == ConnectionState.Closed)
await sqlConnection.OpenAsync();
using (var reader = await cmd.ExecuteReaderAsync())
{
//Want to use: reader.ReadAsync()
var tb = new DataTable();
tb.Load(reader);
return tb;
}
}
}
Thanks

If you want an extension method, you can write directly on the command
public static class extensions
{
public async static Task<DataTable> ExecuteAndCreateDataTableAsync(this SqlCommand cmd)
{
using (var reader = await cmd.ExecuteReaderAsync().ConfigureAwait(false))
{
var dataTable = reader.CreateTableSchema();
while (await reader.ReadAsync().ConfigureAwait(false))
{
var dataRow = dataTable.NewRow();
for (int i = 0; i < dataTable.Columns.Count; i++)
{
dataRow[i] = reader[i];
}
dataTable.Rows.Add(dataRow);
}
return dataTable;
}
}
public static void LoadParams(this SqlCommand cmd, params SqlParameter[] parameters)
{
if (parameters != null)
{
foreach (var parameter in parameters)
{
if (parameter != null)
{
if (parameter.Value == null)
parameter.Value = DBNull.Value;
cmd.Parameters.Add(parameter);
}
}
}
}
private static DataTable CreateTableSchema(this SqlDataReader reader)
{
DataTable schema = reader.GetSchemaTable();
DataTable dataTable = new DataTable();
if (schema != null)
{
foreach (DataRow drow in schema.Rows)
{
string columnName = System.Convert.ToString(drow["ColumnName"]);
DataColumn column = new DataColumn(columnName, (Type)(drow["DataType"]));
dataTable.Columns.Add(column);
}
}
return dataTable;
}
}
and your medhod:
private async Task<DataTable> ExecuteQueryInternalAsync(string commandText, CommandType commandType, SqlConnection sqlConnection, SqlTransaction transaction, params SqlParameter[] parameters)
{
using (SqlCommand cmd = new SqlCommand(commandText, sqlConnection) { CommandType = commandType, CommandTimeout = this.config.MainConnectionTimeoutInSeconds })
{
if (transaction != null)
cmd.Transaction = transaction;
cmd.LoadParams(parameters);
if (sqlConnection.State == ConnectionState.Closed)
await sqlConnection.OpenAsync();
var datatable = await cmd.ExecuteAndCreateDataTableAsync();
return datatable;
}
}

Related

OracleCommand TimeOut not working in C# code

public int ExecuteNonQuery(IDbTransaction dbTransaction, string commandText, CommandType commandType, IDbDataParameter[] parameters)
{
int returnValue = 0;
try
{
var command = database.CreateCommand(commandText, commandType, dbTransaction.Connection);
if (parameters != null)
{
foreach (var parameter in parameters)
{
//check for derived output value with no value assigned
if ((parameter.Direction == ParameterDirection.InputOutput) && (parameter.Value == null))
{
parameter.Value = DBNull.Value;
}
command.Parameters.Add(parameter);
}
}
command.CommandTimeout = 600;
returnValue = command.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
return returnValue;
}
I am trying to execute db executing using above code through c# window service through Oracle.ManagedDataAccess library. Database is Oracle 11g. Problem here is that Command Timeout is not working even when query is taking more than 15 mins to execute.
Please guide how to make it work?

Implementing a query function with undeclared number of parameters?

I'm developing a server/client application in C#. In the earlier phases of development I was writing SQL codes each time and it caused spaghetti code. Now I'm trying to make it clean. My question is: How can I write a general query generator function with dynamic parameters?
private void button3_Click(object sender, EventArgs e)
{
try
{
SqlCommand cmd= new SqlCommand();
cmd.CommandText = "INSERT INTO tbl(line, name, firstvalue, secondvalue)" +
"VALUES(#line, #name, #value, #secondvalue)";
cmd.Parameters.AddWithValue("#line", comboBox4.Text);
cmd.Parameters.AddWithValue("#name", textBox2.Text);
cmd.Parameters.AddWithValue("#value", comboBox5.Text);
cmd.Parameters.AddWithValue("#secondvalue", comboBox6.Text);
cmd.Connection = Db.Db;
cmd.CommandType = CommandType.Text;
SqlDataReader dr = cmd.ExecuteReader();
MessageBox.Show("Saved");
}
catch (Exception ex)
{
MessageBox.Show(ex);
}
finally
{
Db.Close();
}
}
But I want to convert it into:
public void query(string commandText, params string[] parameters)
{
SqlCommand command = new SqlCommand();
command.CommandText = commandText;
foreach (var parameter in parameters)
//In this part, there can be lots of different queries and how can assign each parameter to relevant values
//Is there any change to assign them in order.
}
Well, if you insist on implementing such a routine (usually we use ORM) you have to parse the commandText; the simplest (but not the best) implementation is regular expressions (we Match parameter name within commandText, then Zip it with its value from parameters):
using System.Linq;
using System.Text.RegularExpressions;
...
public void query(string commandText, params string[] parameters) {
using (SqlCommand command = new SqlCommand()) {
command.Connection = myConnection; //TODO: put the right connection here
command.CommandText = commandText;
var prms = Regex
.Matches(commandText, #"\b#[A-Za-z_][A-Za-z_0-9]*\b")
.Cast<Match>()
.Zip(parameters, (match, value) => new {
name = match.Value,
value
});
foreach(var prm in prms)
command.Parameters.AddWithValue(prm.name, prm.value);
// Just execute; we have nothing to read (ExecuteReader)
command.ExecuteNonQuery();
}
}
Edit: If you want / ready to specify parameters' names, not only values you can try Tuples: for c# 7.0+
public void query(string commandText, params (string, object)[] parameters) {
...
foreach (var prm in parameters)
command.Parameters.AddWithValue(prm.Item1, prm.Item2);
...
}
usage
query(sql,
("#line", comboBox4.Text),
("#name", textBox2.Text),
("#id", 123), // please, note integer value
("#money", 456.78d),
...
);
for C# 6.0-:
public void query(string commandText, params Tuple<string, object>[] parameters) {
...
foreach (var prm in parameters)
command.Parameters.AddWithValue(prm.Item1, prm.Item2);
...
}
...
query(sql,
Tuple.Create("#line", comboBox4.Text),
Tuple.Create("#name", textBox2.Text),
...
);
Create an object with database parameters and set all values in it. Use Entity framework to do the rest or read from object when assigning.
I solved it and here is the solution. We will store them in the same array. So the parameters array will be:
parameters[0] = "#line"
parameters[1] = line
parameters[2] = "#name"
parameters[3] = name
and while posting them into AddWithValue() function
for (int i = 0; i < parameters.Length;)
{
command.Parameters.AddWithValue(parameters[i++] as string,parameters[i++]);
}
and we should call the query function when needed like this,
string commandText = "INSERT INTO tbl(line, name, firstvalue, secondvalue)" +
"VALUES(#line, #name, #value, #secondvalue)";
query("#line", line, "#name", name, "#value", firstvalue, "secondvalue", secondvalue);
Function Body
public class Database
{
public string executeScaler(string commandText, bool isStoredProcedure, Dictionary<string, object> dictParams = null)
{
string result = "";
DataTable dt = new DataTable();
SqlConnection con = ConnectionStrings.GetConnection();
SqlCommand cmd = new SqlCommand(commandText, con);
if (isStoredProcedure)
cmd.CommandType = CommandType.StoredProcedure;
if (dictParams != null)
foreach (KeyValuePair<string, object> rec in dictParams)
cmd.Parameters.AddWithValue("#" + rec.Key, rec.Value);
try
{
result = Convert.ToString(cmd.ExecuteScalar());
}
catch (Exception ex)
{
throw ex;
}
finally
{
con.Close();
con.Dispose();
}
return result;
}
}
Calling Function
Dictionary<string, object> dict = new Dictionary<string, object>();
dict.Add("Email", obj.Email);
dict.Add("Password", obj.Password);
Database objDB = new Database();
objDB.executeScaler("RegisterAccount", true, dict);

Get Data from stored procedure as Datatable

I have a method to retrieve data from a database using a stored procedure as a DataTable like:
public DataTable GetTableBySQL(string sql)
{
SqlCommand cmd = new SqlCommand(sql.ToString(), this.dbconn)
{
CommandTimeout = 0,
CommandType = CommandType.Text
};
DataTable tbl = new DataTable("Table1")
{
Locale = System.Globalization.CultureInfo.InvariantCulture
};
SqlDataAdapter da = new SqlDataAdapter(cmd);
try
{
da.SelectCommand.CommandTimeout = 0;
da.Fill(tbl);
}
catch (SqlException e)
{
this.HandleSQLError(e, "GetTableBySQL", sql.ToString());
}
finally
{
cmd.Dispose();
da.Dispose();
}
return tbl;
}
Now I call the stored procedure like this:
var empList = db.GetTableBySQL("$exec getMySP");
But when I execute, it just don't return any columns.
What am I doing wrong? Regards
There are three main problems here (other smaller ones, but three that are important):
The $exec part of the SQL doesn't mean anything. Maybe you just want exec.
When the bad SQL fails, the error is hidden from the program, so you don't really know what happened.
The method signature doesn't support query parameters, and therefore will force you to write horribly insecure code that will result in someone hacking your application. Probably sooner rather than later. This is really bad, and you should not ignore it.
Try something more like this:
public DataTable GetTableBySQL(string sql, params SqlParameter[] parameters)
{
var result = new DataTable();
//ADO.Net really does work better when you create a **NEW** connection
// object for most queries. Just share the connection string.
//Also: "using" blocks are a better way to make sure the connection is closed.
using (var dbconn = new SqlConnection(this.dbConnectionString))
using (var cmd = new SqlCommand(sql, dbconn))
using (var da = new SqlDataAdapter(cmd))
{
cmd.CommandTimeout = 0;
// A number of the properties set on the cmd and tbl variables just set the same value that was already there, didn't accomplish anything
//It's hard to understate how important it is to use parameterized queries.
if (parameters != null && parameters.Length > 0)
{
cmd.Parameters.AddRange(parameters);
}
try
{
da.Fill(result);
}
catch (SqlException e)
{
this.HandleSQLError(e, "GetTableBySQL", sql.ToString());
//you may want to re-throw here,
// or even just remove the try/catch and let the error bubble up to calling code
}
}
return result;
}
Here it is again without all the extra explanatory comments, so you can see that doing it right is less code, rather than more:
public DataTable GetTableBySQL(string sql, params SqlParameter[] parameters)
{
var result = new DataTable();
using (var dbconn = new SqlConnection(this.dbConnectionString))
using (var cmd = new SqlCommand(sql, dbconn))
using (var da = new SqlDataAdapter(cmd))
{
cmd.CommandTimeout = 0;
if (parameters != null && parameters.Length > 0)
{
cmd.Parameters.AddRange(parameters);
}
da.Fill(result);
}
return result;
}
Then call it like this:
var empList = db.GetTableBySQL("exec getMySP");

Connection open and Close issue in Sql Connection

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
}

System.ArgumentException Value does not fall within the expected range, SQL issue

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);

Categories

Resources