I would like to build a SQL function that has large amounts of reuse with ExecuteNonQuery but the biggest issue I have are the parameters.
I am not sure what others would do to make this simple and resilient so that I can simply pass the SQL script.
For example SELECT * FROM table WHERE userid = #userid AND isactive = #isactive, and then perhaps the peramiters can be an array.
public void ExecuteSQLCeNonQuery(string _Sql, ?parameter array of some type?)
{
SqlCeConnection sCon = null;
SqlCeCommand sCmd = null;
int countOf = 0;
try
{
sCon = new SqlCeConnection( sqlConnectionStringLocal);
sCmd = new SqlCeCommand(_Sql, sCon);
sCmd.Parameters.AddWithValue("#recBatchDateTarget", sDateOnly); <----- I know this will have to parse the collection some how.
sCon.Open();
countOf = (int)sCmd.ExecuteNonQuery();
sCon.Close();
}
catch (SqlCeException ex)
{
Console.WriteLine("** DEBUG: ExecuteSQLCeNonQuery: {0} - {1}", ex.Message, sSql);
}
finally
{
if (sCmd != null)
sCmd.Dispose();
if (sCon != null)
sCon.Dispose();
}
}
Any suggestions on how to handle the array or collection of parameters?
Just declare it as an IEnumerable<SqlParameter> so the caller can provide any collection he wants (array, List<SqlParameter>, ...)
public void ExecuteSQLCeNonQuery(string _Sql, IEnumerable<SqlParameter> parameters)
{
...
if (parameters != null) // Null check so caller can pass null if there are no parameters
{
foreach(SqlParameter parameter in parameters)
{
cmd.Parameters.Add(parameter);
}
...
}
}
Following the rabbit hole let me to this post. The .AddRange method for the parameters should work.
How to use SqlCeParameterCollection?
You could try to make a separate class for accessing data,
and place your methods there(like delete method,update,insert,etc..),
your connectionstring,if always accessing same database,could be outside the methods.
In your methods parameters you can pass a string with your formatted
sql statements,setting your sqlcommand object to that parameter.
You can make that class static or public(you would then need to
instatiate it in your clientform).
ex:
inside clientform....
string query = "SELECT * from tablename";
sqlaccessclass sqlclass = new sqlaccessclass();
sqlclass.GetAll(query);
Your return type for fetching data methods would be DataSet while in ExecuteNonQuery
methods would be void.
Related
I'm creating an error log for a webpage.
I need a way to get the parameters from a SQL Command to output to string so that the parameters passed through keep the query as dynamic as possible for use throughout the site.
Heres the SQL Command Type I'm talking about:
SqlCommand cmdErrReport = new SqlCommand("ERROR_LOG", conn.sbConn);
cmdErrReport.CommandType = CommandType.StoredProcedure;
cmdMarineResort.Parameters.Add("#Statement", SqlDbType.VarChar).Value = "FindSeason";
I was trying something like this:
cmdMarineResort.Parameters.ToString()
but I can't find within the .Parameters query how to find parameter names or values.
How would I be able to get the values to string? Would i have to list the parameters and the loop through that list writing the parameter and its value to string? If so, how?
cmdMarineResort.Parameters is a SqlParameterCollection.
You can iterate through it using a foreach statement:
foreach(SqlParameter Para in cmdMarineResort.Parameters)
{
Console.WriteLine((string)Para.Value); //value of the parameter as an object
}
This worked for me Using MSSQL Server:
private static string getQueryFromCommand(SqlCommand cmd)
{
string CommandTxt = cmd.CommandText;
foreach (SqlParameter parms in cmd.Parameters)
{
string val = String.Empty;
CommandTxt+=parms.ParameterName + "=";
if ((parms.Value == null)) // string.IsNullOrEmpty(parms.Value))
{
CommandTxt+="NULL, ";
}
else
{
CommandTxt+=(string)parms.Value.ToString() + ", ";
}
}
return (CommandTxt);
}
How would I take info stored in a Select method and transfer it to a string? I'm trying to get the max value from the match_id column and get its value from command.CommandText into the matchCode string. Where would I go from here?
string connectString = "Server=myServer;Database=myDB;Uid=myUser;Pwd=myPass;";
string matchCode = "";
MySqlConnection connect = new MySqlConnection(connectString);
MySqlCommand command = connect.CreateCommand();
command.CommandText = "SELECT MAX(VAL(match_id)) FROM `data`";
try
{
connect.Open();
command.ExecuteNonQuery();
matchCode = "??";
connect.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
I'm new to C#, as it's like my fourth day trying it out. Thanks for the help!
The ExecuteNonQuery() method is for INSERT/UPDATE/DELETE queries. If you're just getting a single value back, use ExecuteScalar(). If you're getting a whole result set back, use ExecuteReader() or Fill() a DataSet object.
Also, there are some things that are idiomatic to C# that you should be doing:
public int GetMatchCode()
{
//this could be loaded from config file or other source
string connectString = "Server=myServer;Database=myDB;Uid=myUser;Pwd=myPass;";
string sql = "SELECT MAX(VAL(match_id)) FROM `data`";
using (var connect = new MySqlConnection(connectString))
using (var command = new MySqlCommand(sql, connect))
{
connect.Open();
var result = command.ExecuteScalar();
if (result == DBNull.Value)
{
//what you do here depends on your application
// if it's impossible for the query to return NULL, you can even skip this
}
return (int)result;
}
}
Some of the changes need explanation:
I don't ever call .Close(). The using block takes care of that for me, even if an exception was thrown. The old code would have left the connection hanging if an exception occured.
.Net developers tend to believe in very small methods. More than that, this method ought to be part of a class that has nothing but other simple public data access methods and maybe a few private helper methods or properties for abstracting common code in the class.
There is no exception handling code here. If you have small methods that are part of a generic database access class, exception handling should be at higher level, where you are better positioned to make decisions about how to proceed.
I have following code for specifying parameters for SQL query. I am getting following exception when I use Code 1; but works fine when I use Code 2. In Code 2 we have a check for null and hence a if..else block.
Exception:
The parameterized query '(#application_ex_id nvarchar(4000))SELECT E.application_ex_id A' expects the parameter '#application_ex_id', which was not supplied.
Code 1:
command.Parameters.AddWithValue("#application_ex_id", logSearch.LogID);
Code 2:
if (logSearch.LogID != null)
{
command.Parameters.AddWithValue("#application_ex_id", logSearch.LogID);
}
else
{
command.Parameters.AddWithValue("#application_ex_id", DBNull.Value );
}
QUESTION
Can you please explain why it is unable to take NULL from logSearch.LogID value in Code 1 (but able to accept DBNull)?
Is there a better code to handle this?
Reference:
Assign null to a SqlParameter
Datatype returned varies based on data in table
Conversion error from database smallint into C# nullable int
What is the point of DBNull?
CODE
public Collection<Log> GetLogs(LogSearch logSearch)
{
Collection<Log> logs = new Collection<Log>();
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
string commandText = #"SELECT *
FROM Application_Ex E
WHERE (E.application_ex_id = #application_ex_id OR #application_ex_id IS NULL)";
using (SqlCommand command = new SqlCommand(commandText, connection))
{
command.CommandType = System.Data.CommandType.Text;
//Parameter value setting
//command.Parameters.AddWithValue("#application_ex_id", logSearch.LogID);
if (logSearch.LogID != null)
{
command.Parameters.AddWithValue("#application_ex_id", logSearch.LogID);
}
else
{
command.Parameters.AddWithValue("#application_ex_id", DBNull.Value );
}
using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.HasRows)
{
Collection<Object> entityList = new Collection<Object>();
entityList.Add(new Log());
ArrayList records = EntityDataMappingHelper.SelectRecords(entityList, reader);
for (int i = 0; i < records.Count; i++)
{
Log log = new Log();
Dictionary<string, object> currentRecord = (Dictionary<string, object>)records[i];
EntityDataMappingHelper.FillEntityFromRecord(log, currentRecord);
logs.Add(log);
}
}
//reader.Close();
}
}
}
return logs;
}
Annoying, isn't it.
You can use:
command.Parameters.AddWithValue("#application_ex_id",
((object)logSearch.LogID) ?? DBNull.Value);
Or alternatively, use a tool like "dapper", which will do all that messing for you.
For example:
var data = conn.Query<SomeType>(commandText,
new { application_ex_id = logSearch.LogID }).ToList();
I'm tempted to add a method to dapper to get the IDataReader... not really sure yet whether it is a good idea.
I find it easier to just write an extension method for the SqlParameterCollection that handles null values:
public static SqlParameter AddWithNullableValue(
this SqlParameterCollection collection,
string parameterName,
object value)
{
if(value == null)
return collection.AddWithValue(parameterName, DBNull.Value);
else
return collection.AddWithValue(parameterName, value);
}
Then you just call it like:
sqlCommand.Parameters.AddWithNullableValue(key, value);
Just in case you're doing this while calling a stored procedure: I think it's easier to read if you declare a default value on the parameter and add it only when necessary.
SQL:
DECLARE PROCEDURE myprocedure
#myparameter [int] = NULL
AS BEGIN
C#:
int? myvalue = initMyValue();
if (myvalue.hasValue) cmd.Parameters.AddWithValue("myparamater", myvalue);
some problem, allowed with Necessarily set SQLDbType
command.Parameters.Add("#Name", SqlDbType.NVarChar);
command.Parameters.Value=DBNull.Value
where SqlDbType.NVarChar you type. Necessarily set SQL type.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
SQL Server & .net support calling a stored procedure with param's values wihout providing param's names?
I want to call a command.ExecuteReader() of type Stored Procedure, however I do not want the parameter names that I pass to be identical to the ones in the SP. Below is a sample of what I'm trying to do
SP:
ALTER PROCEDURE SPName
#Id nvarchar(50)
AS
BEGIN
SET NOCOUNT ON;
SELECT * FROM TableName
WHERE ColumnName = #Id
END
GO
Code:
using (SqlCommand command = new SqlCommand(spName, connection) { CommandType = CommandType.StoredProcedure })
{
command.parameters.Add(new SqlParameter(*paramaeter name*, sqlDbType.nvarchar){ Value = "SomeValue"};
}
If you want a generic style function, without needing an extra round-trip, and you're happy to use reflection you could use something like this.
// Return an array of SqlParameter's by using reflection on ParamObject
private static SqlParameter[] GetParametersFromObject( object ParamObject )
{
var Params = new List<SqlParameter>();
foreach( var PropInfo in ParamObject.GetType().GetProperties() )
{
Params.Add( new SqlParameter( PropInfo.Name, PropInfo.GetValue( ParamObject, null ) ) );
}
return Params.ToArray();
}
public static void ExecuteSP( SqlConnection Connection, string SPName, object ParamObject )
{
using( var Command = new SqlCommand() )
{
Command.Connection = Connection;
Command.CommandType = CommandType.StoredProcedure;
Command.CommandText = SPName;
Command.Parameters.AddRange( GetParametersFromObject( ParamObject ) );
// Command.ExecuteReader()...
}
}
This uses reflection to get the property names and values out of the anonymous object to populate the SqlCommand. This can be used as such;
ExecuteSP( Conn, "GetStuff", new { Id = 7, Name = "Test" } );
This way ExecuteSP is 'generic' and you pick the parameter names and values when you call ExecuteSP.
Simple fact - you ultimately have to use the correct parameter name when calling a stored procedure because SQL server binds parameters by name (even when you use EXEC to call an SP without using named parameters, the parser binds them by name from left to right).
So if you want to use a different name you will need to introduce an intermediate layer between your SqlCommand and the target SP.
But if you just want to not care about the name and have it automatically discovered - then you can use the technique mentioned by Conrad Frix in his accepted answer on SQL Server & .net support calling a stored procedure with param's values wihout providing param's names? - which is why I've marked as a duplicate, because it is ultimately what you want to do, even if the reasons are different.
For SqlServer there is a DeriveParameters method that takes a command object and queries the database for the parameters (names and types) of the requested stored procedure.
You can then iterate over them and supply values.
Note: this means an extra trip to the database, so if you need this often, you might want to cache the results.
The method below allows you to write generic code for calling stored procedures, but also gives you the flexibility to perform specific actions for each different stored procedure
public delegate void SqlCOmmandDelegate(SqlCommand command);
public class Dal
{
public void ExecuteStoredProcedure(string procedureName,
SqlCommandDelgate commandDelegate)
{
using (SqlConnection connection = new SqlConnection())
{
connection.ConnectionString = GetConnectionString();
using (SqlCommand command = connection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = procedureName;
connection.Open();
commandDelegate(command);
}
}
}
}
class UsesDal
{
public CallFirstProcedure(int value)
{
string userName;
ExecuteStoredProcedure("FIRST_PROCEDURE",
delegate(SqlCommand command)
{
command.Parameters.Add("UserID", value);
command.ExecuteReader();
//Do stuff with results e.g.
username = command.Parameters.Parameters["UserName"].ToString();
}
}
public CallOtherProcedure(string value)
{
int id;
ExecuteStoredProcedure("OTHER_PROCEDURE",
delegate(SqlCommand command)
{
command.Parameters.Add("ParameterName", value);
id = command.ExecuteScalar();
}
}
}
I have a problem with the folowwing piece of code. I am passing a parameter (List<SqlParameter>) to a method executing the following code.
When it executes SQL Server throws an error saying that the proc expects a parameter that was not provided. I know this error and understand it, and when stepping through the code I can see that the cmdExecuteReader object has a collection of parameters with the correct name and value. What could be the problem?
public SqlDataReader ExecuteReader(string storedProcedure, List<SqlParameter> parameters = null)
{
SqlCommand cmdExecuteReader = new SqlCommand()
{
CommandType = System.Data.CommandType.Text,
Connection = conn,
CommandText = storedProcedure
};
if (parameters != null)
{
foreach (SqlParameter param in parameters)
{
cmdExecuteReader.Parameters.AddWithValue(param.ParameterName, param.Value);
}
}
if (conn.State == System.Data.ConnectionState.Closed)
conn.Open();
return cmdExecuteReader.ExecuteReader();
}
Is the .Value set to null for any of the parameters? If so, they aren't sent. Try:
cmdExecuteReader.Parameters.AddWithValue(param.ParameterName,
param.Value ?? DBNull.Value);
(note the null-coalescing with DBNull.Value)
Also, note that AddWithValue may impact your query-plan re-use, as (for strings etc) it uses the length of the value. If you need maximum performance it is better to setup the parameter manually with the defined sizes.
Also note that potentially some of the parameters in the incoming list could be input-output, output or result. I would be very tempted to substitute for something more like:
SqlParameter newParam = cmdExecuteReader.Parameters.Add(
param.ParameterName, param.SqlDbType, param.Size);
newParam.Value = param.Value ?? DBNull.Value;
newParam.Direction = param.Direction;
I did the stuff that you are trying to do, here some examples:
public int ChangeState(int id, int stateId)
{
return DbUtil.ExecuteNonQuerySp("changeDossierState", Cs, new { id, stateId });
}
public IEnumerable<Dossier> GetBy(int measuresetId, int measureId, DateTime month)
{
return DbUtil.ExecuteReaderSp<Dossier>("getDossiers", Cs, new { measuresetId, measureId, month });
}
I recommend you to look here
and to download the samples solution (where there is a DAL Sample project included)
http://valueinjecter.codeplex.com/