Optimize usage of SqlCommand - c#

I execute a stored procedure from C# like this:
medianOfProjects = db.ExeSQLParamByDateTime("usp_TaskStatistics_Median_Calculation", parameters, "#TaskTypeTableType", 1, startDate, endDate
unitNumberFrom, unitNumberTo, unitNumberBldgsSegsFrom, unitNumberBldgsSegsTo, unitNumberSqrFrom, unitNumberSqrTo, unitNumberWoodStoriesFrom,
unitNumberWoodStoriesTo, currentRegionKey);
ExeSQLParamByDateTime method is too large:
public DataTable ExeSQLParamByDateTime(string sprocName, DataTable paramArray, string tableTypeName, int LegacyKey, DateTime startingDate, DateTime endingDate,
int unitNumberFrom, int unitNumberTo, int BldgSegsFrom, int BldgSegsFromTo, int SquareFootageFrom, int SquareFootageTo, int WoodStoriesFrom,
int WoodStoriesTo, int StatusKey)
{
SqlCommand cmd = new SqlCommand(sprocName, this.dbconn);
var startDate = startingDate.Date;
var endDate = endingDate.Date;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter(tableTypeName, SqlDbType.Structured));
cmd.Parameters[tableTypeName].Value = paramArray;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#LegacyKey", SqlDbType.Int));
cmd.Parameters["#LegacyKey"].Value = LegacyKey;
cmd.Parameters.Add(new SqlParameter("#StartingDate", SqlDbType.DateTime));
cmd.Parameters["#StartingDate"].Value = startDate;
cmd.Parameters.Add(new SqlParameter("#EndingDate", SqlDbType.DateTime));
cmd.Parameters["#EndingDate"].Value = endDate;
cmd.Parameters.Add(new SqlParameter("#UnitNumberFrom", SqlDbType.Int));
cmd.Parameters["#UnitNumberFrom"].Value = unitNumberFrom;
cmd.Parameters.Add(new SqlParameter("#UnitNumberTo", SqlDbType.Int));
cmd.Parameters["#UnitNumberTo"].Value = unitNumberTo;
//etc
//etc
}
There is no way to optimize this? I just checking for good practices, as you can see I send param DataTable to execute sql TableType and in addition send parameters, but I don't found anything similar like this. Help is appreciated. regards

public DataTable ExeSQLParamByDateTime(string sprocName, DataTable paramArray, string tableTypeName, int LegacyKey, DateTime startingDate, DateTime endingDate,
int unitNumberFrom, int unitNumberTo, int BldgSegsFrom, int BldgSegsFromTo, int SquareFootageFrom, int SquareFootageTo, int WoodStoriesFrom,
int WoodStoriesTo, int StatusKey
)
{
var result = new DataTable();
//Not good to re-use the same connection object.
// ADO.Net is designed to use connection pooling, which means you want a new connection each time.
// Instead, just re-use the connection string
using (var cn = new SqlConnection(this.dbconn.ConnectionString))
using (var cmd = new SqlCommand(sprocName, cn))
{
cmd.CommandType = CommandType.StoredProcedure; //only need to do this once
//Most parameters can get down to a single line
cmd.Parameters.Add(tableTypeName, SqlDbType.Structured).Value = paramArray;
cmd.Parameters.Add("#LegacyKey", SqlDbType.Int).Value = LegacyKey;
cmd.Parameters.Add("#StartingDate", SqlDbType.DateTime).Value = startingDate.Date;
cmd.Parameters.Add("#EndingDate", SqlDbType.DateTime).Value = endingDate.Date;
cmd.Parameters.Add("#UnitNumberFrom", SqlDbType.Int).Value = unitNumberFrom;
cmd.Parameters.Add("#UnitNumberTo", SqlDbType.Int).Value = unitNumberTo;
//etc
//etc
//you can also handle parameters with size scopes this way:
cmd.Parameters.Add("#FakeParam", SqlDbType.Decimal, 5, 2).Value = 123.45;
cmd.Parameters.Add("#AlsoFake", SqlDbType.NVarChar, 30).Value = "Hello World";
cn.Open(); // wait as long as possible to open the connection
using (var rdr = cmd.ExecuteReader())
{
result.Load(rdr);
rdr.Close();
}
} //using block handles closing the connection, even if an exception is thrown
return result;
}

Related

What function do I need to write to remove from table?

It's my function to add to a table:
public int insertHistory(string title, string description, int isDone, int userId)
{
int s = -1;
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
string sql = "INSERT INTO History(title,description,isDone,userId) VALUES(#param1,#param2,#param3,#param4)";
using (SqlCommand cmd = new SqlCommand(sql, connection))
{
cmd.Parameters.Add("#param1", SqlDbType.NVarChar, 10).Value = title;
cmd.Parameters.Add("#param2", SqlDbType.NVarChar, 400).Value = description;
cmd.Parameters.Add("#param3", SqlDbType.Int).Value = isDone;
cmd.Parameters.Add("#param4", SqlDbType.Int).Value = userId;
cmd.CommandType = CommandType.Text;
s = cmd.ExecuteNonQuery();
}
}
return s;
}
What code do I need to write to remove from the table by title or something?
You have asked to delete using Title and here is how to do it
public int deleteHistory(string title)
{
int s = -1;
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
string sql = "DELETE FROM History WHERE Title = #title)";
using (SqlCommand cmd = new SqlCommand(sql, connection))
{
cmd.Parameters.Add("#title", SqlDbType.NVarChar, 10).Value = title;
s = cmd.ExecuteNonQuery();
}
}
return s
}
However in this way you could end to delete more records than you want. If two or more records have the same title you will delete all records with the same title. You could mitigate this problem adding also the UserID to the where condition and the relative parameter to the parameters collection.
"DELETE FROM History WHERE Title = #title AND UserID = #uid"
So you delete only titles of a specific user, but still this is not safe. If your table has an IDENTITY column and you retrieve the values from that column when you read the records then you can pass that unique value to your query and delete specifically only one record.
"DELETE FROM History WHERE HistoryID = #hid"
as you are using SqlConnection and a plain SQL statement. You need to call a Delete statement in your code:
public void DeleteHistory(string title)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
string sql = "delete from History where title= #title";
using (SqlCommand cmd = new SqlCommand(sql, connection))
{
cmd.Parameters.Add("#title", SqlDbType.NVarChar).Value = title;
cmd.CommandType = CommandType.Text;
cmd.ExecuteNonQuery();
}
}
}

Executing stored procedure does not work with adding parameters

In T-SQL I have an exec like this:
EXEC [dbo].[usp_TaskStatus_Time_Calculation_Final]
#EmployeeGuidIdTableType = #EmployeeGuidIdTableType,
#StartingDate = '2018-08-02 00:00:00.000',
#EndingDate = '2018-08-08 00:00:00.000'
It works correctly, parameters do their job correctly, so I want to reproduce this in C# and I do:
public DataTable ExeSQLParamAndType(string SprocName, DataTable paramArray, string tableTypeName, string parameters = null)
{
SqlCommand cmd = new SqlCommand(SprocName, this.dbconn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter(tableTypeName, SqlDbType.Structured));
cmd.Parameters[tableTypeName].Value = paramArray;
cmd.Parameters.Add(new SqlParameter(parameters, SqlDbType.NVarChar));
DataTable tbl = new DataTable("Table1")
{
Locale = System.Globalization.CultureInfo.InvariantCulture
};
SqlDataAdapter da = new SqlDataAdapter(cmd);
try
{
da.Fill(tbl);
}
catch (SqlException e)
{
this.HandleSQLError(e, "GetTableBySQL", SprocName);
}
finally
{
cmd.Dispose();
}
return tbl;
}
Execute method:
db.ExeSQLParamAndType("StoredProcedureCalc", parameters,
"#EmployeeGuidIdTableType",
$"#StartingDate = '{startDate}', #EndingDate = '{endDate}'");
I get an exception when trying to pass StaringDate and EndingDate parameter in line this.HandleSQLError(e, "GetTableBySQL", SprocName);
#StartingDate = ''2018-08-02 00:00:00.000'', #EndingDate = ''2018-08-08 00:00:00.000'' is not a parameter for procedure StoredProcedureCalc
Someone see something wrong there? Regards
Note: If I execute it from c# without this two parameters (just with table type) it works
Update
I change my code as comments below to:
var startDate = $"'{startingDate.Value.ToString("yyyy-MM-dd 00:00:00.000")}'";
var endDate = $"'{endingDate.Value.ToString("yyyy-MM-dd 00:00:00.000")}'";
db.ExeSQLParamAndType("usp_TaskStatus_Time_Calculation_Final", parameters, "#EmployeeGuidIdTableType", startDate, endDate);
and method like:
public DataTable ExeSQLParamAndType(string sprocName, DataTable paramArray, string tableTypeName, string startingDate, string endingDate)
{
SqlCommand cmd = new SqlCommand(sprocName, this.dbconn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter(tableTypeName, SqlDbType.Structured));
cmd.Parameters[tableTypeName].Value = paramArray;
DateTime.TryParse(startingDate, out var startDate);
cmd.Parameters.Add(new SqlParameter("#StartingDate", SqlDbType.DateTime));
cmd.Parameters["#StartingDate"].Value = startDate;
DateTime.TryParse(startingDate, out var endDate);
cmd.Parameters.Add(new SqlParameter("#EndingDate", SqlDbType.DateTime));
cmd.Parameters["#EndingDate"].Value = endDate;
DataTable tbl = new DataTable("Table1")
{
Locale = System.Globalization.CultureInfo.InvariantCulture
};
SqlDataAdapter da = new SqlDataAdapter(cmd);
try
{
da.Fill(tbl);
}
catch (SqlException e)
{
this.HandleSQLError(e, "GetTableBySQL", sprocName);
}
finally
{
cmd.Dispose();
}
return tbl;
}
But I have troubles with DateTime, how can I parse as desire output format: 2018-08-02 00:00:00.000 ?
The problems is caused because
cmd.Parameters.Add(new SqlParameter(parameters, SqlDbType.NVarChar));
is creating a parameter with the name of whatever value is held in the variable parameters which is the string value "#StartingDate = '{startDate}', #EndingDate = '{endDate}'" because that is what you passed into ExeSQLParamAndType with this code:
db.ExeSQLParamAndType("StoredProcedureCalc"
, parameters
, "#EmployeeGuidIdTableType"
,$"#StartingDate = '{startDate}', #EndingDate = '{endDate}'");
you need to create two parameters, one called startDate and one called EndDate
cmd.Parameters.Add(new SqlParameter(startingDate, SqlDbType.NVarChar));
cmd.Parameters.Add(new SqlParameter(EndingDate , SqlDbType.NVarChar));
C# DateTime and SQL Server DateTime are incompatible. You must change the setting in your SQL tables to use Datetime2 not DateTime. You can do this by directly issuing an ALTER statement to every table with a Datetime field in your Database or adding a one line command in your DbContext class telling Entity Framework to specifically use "datetime2" for all classes that use DateTime.

Is it possible to iterate multiple data sets async BeginExecuteReader?

I have a long running stored procedure that returns multiple results.
I'd like to iterate the results asynchronously and grab results as they are ready (AS THEY ARE AVAILABLE).
ExecuteReaderAsync with some WaitOne logic? (never used this so any example is appreciated)
Is this possible?
private IEnumerable<DataTable> validationResultSets(string MOName, DateTime StartDate, DateTime EndDate, string FilePath)
{
DataTable d;
using (SqlConnection conn =
new SqlConnection(connString))
{
conn.Open();
using (cmd = new SqlCommand("dbo.sp_ValidateAcceptanceFile", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter
{
ParameterName = "#MOName",
Value = MOName,
SqlDbType = SqlDbType.VarChar,
Size = 255
});
cmd.Parameters.Add(new SqlParameter
{
ParameterName = "#StartDate",
Value = StartDate,
SqlDbType = SqlDbType.DateTime
});
cmd.Parameters.Add(new SqlParameter
{
ParameterName = "#EndDate",
Value = EndDate,
SqlDbType = SqlDbType.DateTime
});
cmd.Parameters.Add(new SqlParameter
{
ParameterName = "#FilePath",
Value = FilePath,
SqlDbType = SqlDbType.VarChar,
Size = 500
});
//IDataReader rdr = cmd.ExecuteReader();
IDataReader rdr = cmd.BeginExecuteReader(); //??
try
{
do
{
d = new DataTable();
d.Load(rdr);
yield return d;
} while (!rdr.IsClosed);
}
finally
{
rdr.Close();
rdr.Dispose();
}
}
}
}
You are mixing concepts. A procedure returning multiple results is not related in any way to MARS. A procedure returning multiple results can be invoked asynchronously, note that your async call will complete as soon as the first fragment of the result has returned from the server and from there on you read the results just like in the sync code.

SQL Server parameters array bol --> dal

The error is cannot convert object to sql parameter
I'm working with a bol->dal, building an array of parameters to pass to the dal:
bol:
SqlParameter[] sqlParams = new SqlParameter[]
{
new SqlParameter("#p1", SqlDbType.VarChar, 30).Value = "val1",
new SqlParameter("#p2", SqlDbType.VarChar, 30).Value = "val2"
};
dal:
public static int ExecuteNonQuery(string sql, SqlParameter[] #params)
{
SqlConnection cnn = new SqlConnection(connectionstring);
SqlCommand cmd = new SqlCommand(sql, cnn);
for (int i = 0; i <= #params.Length - 1; i++)
{
cmd.Parameters.Add(#params[i]);
}
foreach (IDataParameter param in cmd.Parameters)
{
if (param.Value == null) param.Value = DBNull.Value;
}
cnn.Open();
int retval = cmd.ExecuteNonQuery();
cnn.Close();
return retval;
}
I need help improving this, thanks--
You need to pass in the actual SqlParameter. Notice the object initializer of the Value:
var sqlParams = new SqlParameter[]
{
new SqlParameter("#p1", SqlDbType.VarChar, 30) {Value = "val1"},
new SqlParameter("#p2", SqlDbType.VarChar, 30) {Value = "val2"},
};

Get output parameter value in ADO.NET

My stored procedure has an output parameter:
#ID INT OUT
How can I retrieve this using ado.net?
using (SqlConnection conn = new SqlConnection(...))
{
SqlCommand cmd = new SqlCommand("sproc", conn);
cmd.CommandType = CommandType.StoredProcedure;
// add parameters
conn.Open();
// *** read output parameter here, how?
conn.Close();
}
The other response shows this, but essentially you just need to create a SqlParameter, set the Direction to Output, and add it to the SqlCommand's Parameters collection. Then execute the stored procedure and get the value of the parameter.
Using your code sample:
// SqlConnection and SqlCommand are IDisposable, so stack a couple using()'s
using (SqlConnection conn = new SqlConnection(connectionString))
using (SqlCommand cmd = new SqlCommand("sproc", conn))
{
// Create parameter with Direction as Output (and correct name and type)
SqlParameter outputIdParam = new SqlParameter("#ID", SqlDbType.Int)
{
Direction = ParameterDirection.Output
};
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(outputIdParam);
conn.Open();
cmd.ExecuteNonQuery();
// Some various ways to grab the output depending on how you would like to
// handle a null value returned from the query (shown in comment for each).
// Note: You can use either the SqlParameter variable declared
// above or access it through the Parameters collection by name:
// outputIdParam.Value == cmd.Parameters["#ID"].Value
// Throws FormatException
int idFromString = int.Parse(outputIdParam.Value.ToString());
// Throws InvalidCastException
int idFromCast = (int)outputIdParam.Value;
// idAsNullableInt remains null
int? idAsNullableInt = outputIdParam.Value as int?;
// idOrDefaultValue is 0 (or any other value specified to the ?? operator)
int idOrDefaultValue = outputIdParam.Value as int? ?? default(int);
conn.Close();
}
Be careful when getting the Parameters[].Value, since the type needs to be cast from object to what you're declaring it as. And the SqlDbType used when you create the SqlParameter needs to match the type in the database. If you're going to just output it to the console, you may just be using Parameters["#Param"].Value.ToString() (either explictly or implicitly via a Console.Write() or String.Format() call).
EDIT: Over 3.5 years and almost 20k views and nobody had bothered to mention that it didn't even compile for the reason specified in my "be careful" comment in the original post. Nice. Fixed it based on good comments from #Walter Stabosz and #Stephen Kennedy and to match the update code edit in the question from #abatishchev.
For anyone looking to do something similar using a reader with the stored procedure, note that the reader must be closed to retrieve the output value.
using (SqlConnection conn = new SqlConnection())
{
SqlCommand cmd = new SqlCommand("sproc", conn);
cmd.CommandType = CommandType.StoredProcedure;
// add parameters
SqlParameter outputParam = cmd.Parameters.Add("#ID", SqlDbType.Int);
outputParam.Direction = ParameterDirection.Output;
conn.Open();
using(IDataReader reader = cmd.ExecuteReader())
{
while(reader.Read())
{
//read in data
}
}
// reader is closed/disposed after exiting the using statement
int id = outputParam.Value;
}
Not my code, but a good example i think
source: http://www.eggheadcafe.com/PrintSearchContent.asp?LINKID=624
using System;
using System.Data;
using System.Data.SqlClient;
class OutputParams
{
[STAThread]
static void Main(string[] args)
{
using( SqlConnection cn = new SqlConnection("server=(local);Database=Northwind;user id=sa;password=;"))
{
SqlCommand cmd = new SqlCommand("CustOrderOne", cn);
cmd.CommandType=CommandType.StoredProcedure ;
SqlParameter parm= new SqlParameter("#CustomerID",SqlDbType.NChar) ;
parm.Value="ALFKI";
parm.Direction =ParameterDirection.Input ;
cmd.Parameters.Add(parm);
SqlParameter parm2= new SqlParameter("#ProductName",SqlDbType.VarChar);
parm2.Size=50;
parm2.Direction=ParameterDirection.Output;
cmd.Parameters.Add(parm2);
SqlParameter parm3=new SqlParameter("#Quantity",SqlDbType.Int);
parm3.Direction=ParameterDirection.Output;
cmd.Parameters.Add(parm3);
cn.Open();
cmd.ExecuteNonQuery();
cn.Close();
Console.WriteLine(cmd.Parameters["#ProductName"].Value);
Console.WriteLine(cmd.Parameters["#Quantity"].Value.ToString());
Console.ReadLine();
}
}
public static class SqlParameterExtensions
{
public static T GetValueOrDefault<T>(this SqlParameter sqlParameter)
{
if (sqlParameter.Value == DBNull.Value
|| sqlParameter.Value == null)
{
if (typeof(T).IsValueType)
return (T)Activator.CreateInstance(typeof(T));
return (default(T));
}
return (T)sqlParameter.Value;
}
}
// Usage
using (SqlConnection conn = new SqlConnection(connectionString))
using (SqlCommand cmd = new SqlCommand("storedProcedure", conn))
{
SqlParameter outputIdParam = new SqlParameter("#ID", SqlDbType.Int)
{
Direction = ParameterDirection.Output
};
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(outputIdParam);
conn.Open();
cmd.ExecuteNonQuery();
int result = outputIdParam.GetValueOrDefault<int>();
}
string ConnectionString = ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
using (SqlConnection con = new SqlConnection(ConnectionString))
{
//Create the SqlCommand object
SqlCommand cmd = new SqlCommand(“spAddEmployee”, con);
//Specify that the SqlCommand is a stored procedure
cmd.CommandType = System.Data.CommandType.StoredProcedure;
//Add the input parameters to the command object
cmd.Parameters.AddWithValue(“#Name”, txtEmployeeName.Text);
cmd.Parameters.AddWithValue(“#Gender”, ddlGender.SelectedValue);
cmd.Parameters.AddWithValue(“#Salary”, txtSalary.Text);
//Add the output parameter to the command object
SqlParameter outPutParameter = new SqlParameter();
outPutParameter.ParameterName = “#EmployeeId”;
outPutParameter.SqlDbType = System.Data.SqlDbType.Int;
outPutParameter.Direction = System.Data.ParameterDirection.Output;
cmd.Parameters.Add(outPutParameter);
//Open the connection and execute the query
con.Open();
cmd.ExecuteNonQuery();
//Retrieve the value of the output parameter
string EmployeeId = outPutParameter.Value.ToString();
}
Font http://www.codeproject.com/Articles/748619/ADO-NET-How-to-call-a-stored-procedure-with-output
You can get your result by below code::
using (SqlConnection conn = new SqlConnection(...))
{
SqlCommand cmd = new SqlCommand("sproc", conn);
cmd.CommandType = CommandType.StoredProcedure;
// add other parameters parameters
//Add the output parameter to the command object
SqlParameter outPutParameter = new SqlParameter();
outPutParameter.ParameterName = "#Id";
outPutParameter.SqlDbType = System.Data.SqlDbType.Int;
outPutParameter.Direction = System.Data.ParameterDirection.Output;
cmd.Parameters.Add(outPutParameter);
conn.Open();
cmd.ExecuteNonQuery();
//Retrieve the value of the output parameter
string Id = outPutParameter.Value.ToString();
// *** read output parameter here, how?
conn.Close();
}
Create the SqlParamObject which would give you control to access methods on the parameters
:
SqlParameter param = new SqlParameter();
SET the Name for your paramter (it should b same as you would have declared a variable to hold the value in your DataBase)
: param.ParameterName = "#yourParamterName";
Clear the value holder to hold you output data
: param.Value = 0;
Set the Direction of your Choice (In your case it should be Output)
: param.Direction = System.Data.ParameterDirection.Output;
That looks more explicit for me:
int? id = outputIdParam.Value is DbNull ? default(int?) : outputIdParam.Value;

Categories

Resources