i have a class for my sql duety, and have problem for my
how could i do something like this
SqlParameter storedparam = new SqlParameter();
SqlParameter param1 = new SqlParameter("#userid", SqlDbType.BigInt);
param1.Value = "87";
SqlParameter param2 = new SqlParameter("#ip",SqlDbType.VarChar,40);
param2.Value = "192.168.1.1";
storedparam = param1 + param2; //this parth have problem
Db myobject = new Db(myconection);
myobject.writestoredpro("nameofsotred",storedparam )
In the sql duety, take in params SqlParameter[] like so:
public void WriteStoredProcedure( string Query, params SqlParameter[] SqlParameters ) {
// do it
}
For cases where you're defining a parameter and want to run it in one go, you can also define it like so:
SqlParameter storedParam = new SqlParameter("#ip",SqlDbType.Varchar,40) {
Value = "192.168.1.1"
};
the 'writetostoredpro' method would need to take a collection of SqlParameter objects, and then inside the method, you would need to iterate over the collection adding them to the SqlCommand.Parameters property. Take a look at this link : http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.parameters.aspx of how to use the propertied, and SqlCommand class for an idea of what to do: http://msdn.microsoft.com/en-us/library/z4956bkc.aspx
Related
I know how to pass one parameter to an sql query but i want to create a function to pass multiple params that will have differents type and here im stuck.
public List<T> RawSql<T>(string query, params object[] parameters)
{
var command = context.Database.GetDbConnection().CreateCommand();
command.CommandText = query;
command.CommandType = CommandType.Text;
SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "#bookId";
parameter.SqlDbType = SqlDbType.Int;
parameter.Value = parameters[0];
command.Parameters.Add(parameter);
var result = command.ExecuteReader())
return result;
}
Usage :
var rows = helper.RawSql("myStoreProc #bookId", x=> new Book { Id = (bool)x[0] }, bookId);
But how i can change the RawSql function to pass multiple parameters like this :
var rows = helper.RawSql("myStoreProc #bookId, #authorName", x=> new Book { Id = (bool)x[0] }, bookId, authorName);
I would also suggest using Dapper instead of reinventing the wheel - but if you can't for some reason, I would change the method signature to accept params SqlParameter[] parameters instead of params object[] parameters - and then all you need to do in the method is command.Parameters.AddRange(parameters);.
As Marc Gravel wrote in his comment - naming the parameters is going to be the biggest problem if you are simply using object[].
Here is a method I wrote to compare values from two different days:
public DataTable sqlToDTCompare(string conStr, string stpName, DateTime startDate, DateTime endDate, int percent)
{
//receives connection string and stored procedure name
//then returns populated data table
DataTable dt = new DataTable();
using (var con = new SqlConnection(conStr))
using (var cmd = new SqlCommand(stpName, con))
using (var da = new SqlDataAdapter(cmd))
{
cmd.Parameters.Add("#StartDate", SqlDbType.Date).Value = startDate;
cmd.Parameters.Add("#EndDate", SqlDbType.Date).Value = endDate;
cmd.Parameters.Add("#Percent", SqlDbType.Int).Value = percent;
cmd.CommandType = CommandType.StoredProcedure;
da.Fill(dt);
}
return dt;
}
This method then returns that data to a DataTable (was what I needed at time of writing). You would be able to use this , with modifying to be of better fit for your needs.
What you're looking to use is something along:
SqlCommand.Parameters.Add("#Param1", SqlDbType.Type).Value = param1;
SqlCommand.Parameters.Add("#Param2", SqlDbType.Type).Value = param2;
SqlCommand.Parameters.Add("#Param3", SqlDbType.Type).Value = param3;
.....
Where .Type in SqlDbType.Type can be changed to matche whatever SQL datatype you're needing (ex. SqlDbType.Date).
I have previously done implementations along these lines.
public IEnumerable<SampleModel> RetrieveSampleByFilter(string query, params SqlParameter[] parameters)
{
using(var connection = new SqlConnection(dbConnection))
using(var command = new SqlCommand(query, connection))
{
connection.Open();
if(parameters.Length > 0)
foreach(var parameter in parameters)
command.Parameters.Add(parameter);
// Could also do, instead of loop:
// command.Parameters.AddRange(parameters);
using(var reader = command.ExecuteReader())
while(reader != null)
yield return new Sample()
{
Id = reader["Id"],
...
}
}
}
I actually wrote an extension method to read the values returned back into my object, but this allows you to pass a query and a series of parameters to simply return your object.
I would look into Dapper, saves a lot of time. But I find the problem with trying to reuse with the above type of solution creates a bit of tightly coupling often.
By doing this approach you push specific information about your query elsewhere, which separates logic directly out of the repository and tightly couples to another dependency and knowledge.
I have been doing the following to pass parameters with my sql query:
var retval = context.MyObject.SqlQuery(sql.ToString(),
new SqlParameter("#ProductId", productId),
new SqlParameter("#CustomerId", customerId)).ToList();
I ran into a scenario where #CustomerId will not always be used in my sql string. Meaning I used an if statement to either add (Where p.CustomerId = #CustomerId) or keep it out.
I realized that I can't pass my parameters in directly to the SqlQuery function now. Instead I have to create an object list of parameters and then pass it in:
SqlParameter param1 = new SqlParameter("#ProductId", productId);
SqlParameter param2 = new SqlParameter("#CustomerId", customerId);
object[] parameters = new object[] { param1, param2 };
How can I make it so I can use an if statement to either add the CustomerId parameter to my parameters array or not?
You can edit your query to this
SELECT * FROM Product p
WHERE
p.ProductId = #ProductId
AND (#CustomerId IS NULL OR p.CustomerId = #CustomerId)
Then you pass DBNull.Value to #CustomerId if it is not used
You can try using a sql command instead as it allows using collections of parameters. But this might require you to change your code structure a bit.
int a = 1;
SqlCommand sql = new SqlCommand();
sql.CommandText = "SELECT * FROm somwhere";
List<SqlParameter> lstParams = new List<SqlParameter>();
if (a == 1)
{`enter code here`
SqlParameter sqlParam1 = new SqlParameter();
lstParams.Add(sqlParam1);
}
else if (a == 2)
{
SqlParameter sqlParam2 = new SqlParameter();
lstParams.Add(sqlParam2);
}
sql.Parameters.AddRange(lstParams.ToArray());
sql.BeginExecuteReader();
I have created a linq2sql project in which I have an extension method for calling SPs.
This extension method has few features-
It can call SP with Table Valued Parameters.
It can accepts both input and output parameters
It will handle multiple result set
Extension Method -
public partial class TestLinq2SqlDataContext : DataContext
{
public IMultipleResults ExceuteProcedure(string spName, IEnumerable<SqlParameter> parameters, SqlConnection sqlConnection, out SqlDataReader reader)
{
reader = null;
try
{
sqlConnection.Open();
var cmd = new SqlCommand
{
Connection = sqlConnection,
CommandText = spName,
CommandType = CommandType.StoredProcedure
};
cmd.Parameters.AddRange(parameters.ToArray());
reader = cmd.ExecuteReader();
return Translate(reader);
}
catch (Exception)
{
}
return null;
}
}
I am calling below SP -
CREATE PROCEDURE USP_OutPutParameterCheck(
#Id int OUTPUT,
#Name nvarchar(50) OUTPUT)
AS
BEGIN
SET #Id = 12 SET #Name = N'NameSet for OutputParameter'
END
My C# code is
public static void Main(){
context = new TestLinq2SqlDataContext();
#region USP_OutPutParameterCheck
var connection1 = context.Connection as SqlConnection;
SqlDataReader dataReader1;
var outParam1 = new SqlParameter
{
Direction = ParameterDirection.Output,
Value = "Abc",
DbType = DbType.String,
ParameterName = "#Name"
};
var outParam2 = new SqlParameter
{
Direction = ParameterDirection.Output,
Value = 1,
DbType = DbType.Int32,
ParameterName = "#Id"
};
var parameters11 = new[]
{
outParam1,
outParam2
};
var data21 = context.ExceuteProcedure("USP_OutPutParameterCheck", parameters11, connection1, out dataReader1);
}
Now When I check the values of output parameters in debug mode I am getting the #Id's value perfect
but for #Name parameter I'm only getting 'N' value instead of 'NameSet for OutputParameter'
Can anyone help me out where I am going wrong in this?
Thanks
UPDATE :
Adding Screenshot when seeing the values of parameters in debug mode -
I think You must specifcy the Size of the outParam1.
See: https://msdn.microsoft.com/en-us/library/system.data.common.dbparameter.size(v=vs.110).aspx
For bidirectional and output parameters, and return values, you must
set the value of Size. This is not required for input parameters, and
if not explicitly set, the value is inferred from the actual size of
the specified parameter when a parameterized statement is executed.
I am creating a small helper function to return a DataTable. I would like to work across all providers that ADO.Net supports, so I thought about making everything use IDbCommand or DbCommand where possible.
I have reached a stumbling block with the following code:
private static DataTable QueryImpl(ref IDbConnection conn, String SqlToExecute, CommandType CommandType, Array Parameters)
{
SetupConnection(ref conn);
// set the capacity to 20 so the first 20 allocations are quicker...
DataTable dt = new DataTable();
using (IDbCommand cmd = conn.CreateCommand())
{
cmd.CommandText = SqlToExecute;
cmd.CommandType = CommandType;
if (Parameters != null && Parameters.Length > 0)
{
for (Int32 i = 0; i < Parameters.Length; i++)
{
cmd.Parameters.Add(Parameters.GetValue(i));
}
}
dt.Load(cmd.ExecuteReader(), LoadOption.OverwriteChanges);
}
return dt;
}
When this code is executed, I receive an InvalidCastException which states the following:
The SqlParameterCollection only accepts non-null SqlParameter type objects, not String objects.
The code falls over on the line:
cmd.Parameters.Add(Parameters.GetValue(i));
Any ideas?
Any improvements to the above code is appreciated.
Actual solution:
private static readonly Regex regParameters = new Regex(#"#\w+", RegexOptions.Compiled);
private static DataTable QueryImpl(ref DbConnection conn, String SqlToExecute, CommandType CommandType, Object[] Parameters)
{
SetupConnection(ref conn);
DataTable dt = new DataTable();
using (DbCommand cmd = conn.CreateCommand())
{
cmd.CommandText = SqlToExecute;
cmd.CommandType = CommandType;
if (Parameters != null && Parameters.Length > 0)
{
MatchCollection cmdParams = regParameters.Matches(cmd.CommandText);
List<String> param = new List<String>();
foreach (var el in cmdParams)
{
if (!param.Contains(el.ToString()))
{
param.Add(el.ToString());
}
}
Int32 i = 0;
IDbDataParameter dp;
foreach (String el in param)
{
dp = cmd.CreateParameter();
dp.ParameterName = el;
dp.Value = Parameters[i++];
cmd.Parameters.Add(dp);
}
}
dt.Load(cmd.ExecuteReader(), LoadOption.OverwriteChanges);
}
return dt;
}
Thanks for ideas/links etc. :)
I believe IDbCommand has a CreateParameter() method:
var parameter = command.CreateParameter();
parameter.ParameterName = "#SomeName";
parameter.Value = 1;
command.Parameters.Add(parameter);
You could add the code of the accepted answer to an extension method:
public static class DbCommandExtensionMethods
{
public static void AddParameter (this IDbCommand command, string name, object value)
{
var parameter = command.CreateParameter();
parameter.ParameterName = name;
parameter.Value = value;
command.Parameters.Add(parameter);
}
}
I know it's not what you're asking, but I have a much simpler and more robust solution to offer.
The Microsoft Patterns and Practices library includes a Data Access Application block that is incredibly powerful and easy to use. A sample for executing a stored procedure and returning a dataset is shown below from our actual code:
object[] ParameterValues = new object[] {"1",DateTime.Now, 12, "Completed", txtNotes.Text};
Database db = DatabaseFactory.CreateDatabase("ConnectionStringName");
DataSet ds = = db.ExecuteDataSet("StoredProcName", ParameterValues);
It doesn't matter if the Connection is OleDb, ODBC, etc. The ConnectionStringName in the first line of code is just the name of the Consternating as defined in the .config file. You pass in a Connection String name, stored proc name, and an array of objects, which make up the parameters.
This is just one of the many sweet functions available.
You'll get everything you're trying to build and then some.
The official site is here: http://msdn.microsoft.com/en-us/library/ff648951.aspx
To save you some searching, the Data classes documentation are found here: http://msdn.microsoft.com/en-us/library/microsoft.practices.enterpriselibrary.data(PandP.50).aspx
(and it's free from Microsoft, and updated regularly.)
This answer is intended for slightly more specific purpose than what you're doing, but building on #Dismissile's answer, I used a Dictionary to supply the parameter name and value to a foreach loop in my personal project.
using( IDbCommand dbCommand = dbConnection.CreateCommand() )
{
dbCommand.CommandText = Properties.Settings.Default.UpdateCommand;
Dictionary<string,object> values = new Dictionary<string,object>()
{
{"#param1",this.Property1},
{"#param2",this.Property2},
// ...
};
foreach( var item in values )
{
var p = dbCommand.CreateParameter();
p.ParameterName = item.Key;
p.Value = item.Value;
dbCommand.Parameters.Add(p);
}
}
Your Parameters parameter needs to be of type IDataParameter[] and, given the error text, the concrete implementation needs be a SqlParameter[] type.
If you wish to keep your signature, you'll need a factory to derive the necessary concrete implementation.
Add using System.Data.SqlClient; and
cmd.Parameters.Add(new SqlParameter("#parameterName", value));
While using the using() {} (sic) blocks as shown below, and assuming that cmd1 does not live beyond the scope of the first using() {} block, why should the second block throw an exception with the message
The SqlParameter is already contained by another SqlParameterCollection
Does it mean that resources and/or handles - including the parameters (SqlParameterCollection) - attached to cmd1 are not released when its destroyed at the end of the block?
using (var conn = new SqlConnection("Data Source=.;Initial Catalog=Test;Integrated Security=True"))
{
var parameters = new SqlParameter[] { new SqlParameter("#ProductId", SqlDbType.Int ) };
using(var cmd1 = new SqlCommand("SELECT ProductName FROM Products WHERE ProductId = #ProductId"))
{
foreach (var parameter in parameters)
{
cmd1.Parameters.Add(parameter);
}
// cmd1.Parameters.Clear(); // uncomment to save your skin!
}
using (var cmd2 = new SqlCommand("SELECT Review FROM ProductReviews WHERE ProductId = #ProductId"))
{
foreach (var parameter in parameters)
{
cmd2.Parameters.Add(parameter);
}
}
}
NOTE: Doing cmd1.Parameters.Clear() just before the last brace of the first using() {} block will save you from the exception (and possible embarrassment).
If you need to reproduce you can use the following scripts to create the objects:
CREATE TABLE Products
(
ProductId int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
ProductName nvarchar(32) NOT NULL
)
GO
CREATE TABLE ProductReviews
(
ReviewId int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
ProductId int NOT NULL,
Review nvarchar(128) NOT NULL
)
GO
I suspect that SqlParameter "knows" which command it's part of, and that that information isn't cleared when the command is disposed, but is cleared when you call command.Parameters.Clear().
Personally I think I'd avoid reusing the objects in the first place, but it's up to you :)
Adding cmd.Parameters.Clear(); after execution should be fine.
Using blocks do not ensure that an object is "destroyed", simply that the Dispose() method is called. What that actually does is up to the specific implementation and in this case it clearly does not empty the collection. The idea is to ensure that unmanaged resources that would not be cleaned up by the garbage collector are correctly disposed. As the Parameters collection is not an unmanaged resource it is not entirely suprising it is not cleared by the dispose method.
I faced this particular error because I was using the same SqlParameter objects as part of a SqlParameter collection for calling a procedure multiple times. The reason for this error IMHO is that the SqlParameter objects are associated to a particular SqlParameter Collection and you can't use the same SqlParameter objects to create a new SqlParameter collection.
So, instead of this:
var param1 = new SqlParameter{ DbType = DbType.String, ParameterName = param1,Direction = ParameterDirection.Input , Value = "" };
var param2 = new SqlParameter{ DbType = DbType.Int64, ParameterName = param2, Direction = ParameterDirection.Input , Value = 100};
SqlParameter[] sqlParameter1 = new[] { param1, param2 };
ExecuteProc(sp_name, sqlParameter1);
/*ERROR :
SqlParameter[] sqlParameter2 = new[] { param1, param2 };
ExecuteProc(sp_name, sqlParameter2);
*/
Do this:
var param3 = new SqlParameter{ DbType = DbType.String, ParameterName = param1, Direction = ParameterDirection.Input , Value = param1.Value };
var param4 = new SqlParameter{ DbType = DbType.Int64, ParameterName = param2, Direction = ParameterDirection.Input , Value = param2.Value};
SqlParameter[] sqlParameter3 = new[] { param3, param4 };
ExecuteProc(sp_name, sqlParameter3);
using defines a scope, and does the automatic call of Dispose() for which we love it.
A reference falling out of scope will not make the object itself "disappear" if another object has a reference to it, which in this case will be the case for parameters having a reference to cmd1.
I have Also got the same issue Thanks #Jon, based on that I gave example.
When I called the below function in which 2 times same sqlparameter passed. In the first database call, it was called properly, but in the second time, it was give the above error.
public Claim GetClaim(long ClaimId)
{
string command = "SELECT * FROM tblClaim "
+ " WHERE RecordStatus = 1 and ClaimId = #ClaimId and ClientId =#ClientId";
List<SqlParameter> objLSP_Proc = new List<SqlParameter>(){
new SqlParameter("#ClientId", SessionModel.ClientId),
new SqlParameter("#ClaimId", ClaimId)
};
DataTable dt = GetDataTable(command, objLSP_Proc);
if (dt.Rows.Count == 0)
{
return null;
}
List<Claim> list = TableToList(dt);
command = "SELECT * FROM tblClaimAttachment WHERE RecordStatus = 1 and ClaimId = #ClaimId and ClientId =#ClientId";
DataTable dt = GetDataTable(command, objLSP_Proc); //gives error here, after add `sqlComm.Parameters.Clear();` in GetDataTable (below) function, the error resolved.
retClaim.Attachments = new ClaimAttachs().SelectMany(command, objLSP_Proc);
return retClaim;
}
This is the common DAL function
public DataTable GetDataTable(string strSql, List<SqlParameter> parameters)
{
DataTable dt = new DataTable();
try
{
using (SqlConnection connection = this.GetConnection())
{
SqlCommand sqlComm = new SqlCommand(strSql, connection);
if (parameters != null && parameters.Count > 0)
{
sqlComm.Parameters.AddRange(parameters.ToArray());
}
using (SqlDataAdapter da = new SqlDataAdapter())
{
da.SelectCommand = sqlComm;
da.Fill(dt);
}
sqlComm.Parameters.Clear(); //this added and error resolved
}
}
catch (Exception ex)
{
throw;
}
return dt;
}
I encountered this exception because I had failed to instantiate a parameter object. I thought it was complaining about two procedures having parameters with the same name. It was complaining about the same parameter being added twice.
Dim aParm As New SqlParameter()
aParm.ParameterName = "NAR_ID" : aParm.Value = hfCurrentNAR_ID.Value
m_daNetworkAccess.UpdateCommand.Parameters.Add(aParm)
aParm = New SqlParameter
Dim tbxDriveFile As TextBox = gvNetworkFileAccess.Rows(index).FindControl("tbxDriveFolderFile")
aParm.ParameterName = "DriveFolderFile" : aParm.Value = tbxDriveFile.Text
m_daNetworkAccess.UpdateCommand.Parameters.Add(aParm)
**aParm = New SqlParameter()** <--This line was missing.
Dim aDDL As DropDownList = gvNetworkFileAccess.Rows(index).FindControl("ddlFileAccess")
aParm.ParameterName = "AccessGranted" : aParm.Value = aDDL.Text
**m_daNetworkAccess.UpdateCommand.Parameters.Add(aParm)** <-- The error occurred here.
Issue
I was executing a SQL Server stored procedure from C# when I encountered this issue:
Exception message [The SqlParameter is already contained by another SqlParameterCollection.]
Cause
I was passing 3 parameters to my stored procedure. I added the
param = command.CreateParameter();
only once altogether. I should have added this line for each parameter, it means 3 times altogether.
DbCommand command = CreateCommand(ct.SourceServer, ct.SourceInstance, ct.SourceDatabase);
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "[ETL].[pGenerateScriptToCreateIndex]";
DbParameter param = command.CreateParameter();
param.ParameterName = "#IndexTypeID";
param.DbType = DbType.Int16;
param.Value = 1;
command.Parameters.Add(param);
param = command.CreateParameter(); --This is the line I was missing
param.ParameterName = "#SchemaName";
param.DbType = DbType.String;
param.Value = ct.SourceSchema;
command.Parameters.Add(param);
param = command.CreateParameter(); --This is the line I was missing
param.ParameterName = "#TableName";
param.DbType = DbType.String;
param.Value = ct.SourceDataObjectName;
command.Parameters.Add(param);
dt = ExecuteSelectCommand(command);
Solution
Adding the following line of code for each parameter
param = command.CreateParameter();
This is how I have done it!
ILease lease = (ILease)_SqlParameterCollection.InitializeLifetimeService();
if (lease.CurrentState == LeaseState.Initial)
{
lease.InitialLeaseTime = TimeSpan.FromMinutes(5);
lease.SponsorshipTimeout = TimeSpan.FromMinutes(2);
lease.RenewOnCallTime = TimeSpan.FromMinutes(2);
lease.Renew(new TimeSpan(0, 5, 0));
}
If you're using EntityFramework
I also had this same exception. In my case, I was calling SQL via a EntityFramework DBContext. The following is my code, and how I fixed it.
Broken Code
string sql = "UserReport #userID, #startDate, #endDate";
var sqlParams = new Object[]
{
new SqlParameter { ParameterName= "#userID", Value = p.UserID, SqlDbType = SqlDbType.Int, IsNullable = true }
,new SqlParameter { ParameterName= "#startDate", Value = p.StartDate, SqlDbType = SqlDbType.DateTime, IsNullable = true }
,new SqlParameter { ParameterName= "#endDate", Value = p.EndDate, SqlDbType = SqlDbType.DateTime, IsNullable = true }
};
IEnumerable<T> rows = ctx.Database.SqlQuery<T>(sql,parameters);
foreach(var row in rows) {
// do something
}
// the following call to .Count() is what triggers the exception
if (rows.Count() == 0) {
// tell user there are no rows
}
Note: the above call to SqlQuery<T>() actually returns a DbRawSqlQuery<T>, which implements IEnumerable
Why does calling .Count() throw the exception?
I haven't fired up SQL Profiler to confirm, but I suspect that .Count() is triggering another call to SQL Server, and internally it is reusing the same SQLCommand object and trying to re-add the duplicate parameters.
Solution / Working Code
I added a counter inside my foreach, so that I could keep a row count without having to call .Count()
int rowCount = 0;
foreach(var row in rows) {
rowCount++
// do something
}
if (rowCount == 0) {
// tell user there are no rows
}
Afterthough
My project is probably using an old version of EF. The newer version may have fixed this internal bug by clearing the parameters, or disposing of the SqlCommand object.
Or maybe, there are explicit instructions that tell developers not to call .Count() after iterating a DbRawSqlQuery, and I'm coding it wrong.