Stored Procedure With Array parameters csharp to mssql - c#

This is my manual query work in SQL:
SELECT * FROM Accounts where Phone in ('05763671278','05763271578','04763125578')
how can I get parameter like this to stored procedure from csharp?
I have a phones array in C#. I get this array from parameters from a view (multi check box select). This is my view:
<td><input type="checkbox" class="CheckboxClass" value="'#item.Phones'"/></td>
This is my controller action:
public ActionResult SendSMSOrMail(string[] values){
// this give "'05763671278','05763271578','04763125578'"
string numbers = string.Join(",", values);
// ...
utility.cmd.Parameters.Add("#numbers", numbers);
// ...
}
But the result is null. What is wrong? I want to get result of all records which contain these phones.

There are several ways to do this. The first is to create your array as a string of text, separated by commas or semicolons or some other separator, then in the SQL you would parse that string. That's pretty simple and straight forward, but does not scale very well and there is a limit to the max character length of a parameter.
A second choice is to use an XML parameter. Example here:
https://stackoverflow.com/a/1069388/61164
A third choice is to use a Table parameter, which be passed as a .NET collection.
I don't have an example of that handy. I've done it before, but that should give you enough info to search for yourself.

I suggest you use a table valued parameter
CREATE TYPE PhonesTableType AS TABLE
(
Phone VARCHAR(12)
)
GO
Then you should declare (at the creation script) that you stored procedure expects a parameter of this type:
CREATE PROCEDURE dbo.your_stored_procedure_name
(
#PhonesTableType PhonesTableType READONLY
)
....
SELECT A.*
FROM Accounts AS A
INNER JOIN #PhonesTableType AS P
ON A.Phone = P.Phone
Then at the C# code you should create a DataTable with one column and pass there the values you have mentioned. Last you should pass this a parameter to your stored procedure.
var phonesDataTable = new DataTable("Phones");
phonesDataTable.Columns.Add("Phone", typeof(string));
foreach(var phone in phones) // phones is the values
{
phonesDataTable.Rows.Add(phone);
}
Then if we suppose that you have named command the command that you would ask to be executed, before executing it you should add the above as a parameter:
var sqlParameter = new SqlParameter
{
ParameterName = "#PhonesTableType",
SqlDbType = SqlDbType.Structured,
Value = phonesDataTable
};
command.Parameters.Add(sqlParameter);

Related

Pass two parameters to stored procedure and grab value

First check my controller code below. Then also check the bottom picture which contains my stored procedure code in SQL Server Management Studio.
Now the issue is: my PagedSearchedUserItems procedure needs two int parameters and it will return output of few columns of data (you can see data sample from screen shot picture below).
In my controller, I am not getting idea how I can pass two parameters and get back that data in variable Items. Can you please fix my controller code to pass two parameters correctly, and grab all values on my variable "Items" ?
Controller code:
using (var ctx = new db_demoEntities())
{
var Items = ctx.Database.SqlQuery<SearchedUserItems>("EXEC PagedSearchedUserItems #TakeFrom",2).ToList<SearchedUserItems>();
}
Stored procedure code:
CREATE PROCEDURE PagedSearchedUserItems
#TakeFrom INT,
#TakePerPage INT
AS
BEGIN
SELECT *
FROM SearchedUserItems
ORDER BY Id
OFFSET #TakeFrom ROWS
FETCH NEXT #TakePerPage ROWS ONLY;
END
PagedSearchedUserItems 2,5
SQL Server Management Studio screenshot
I think you are looking for something like that. This code should work fine with passing two parameters. I am assuming that "SearchedUserItems" object is maching your Data model.
using (var ctx = new db_demoEntities())
{
object[] xparams = {
new SqlParameter("#TakeFrom", 2),
new SqlParameter("#TakePerPage", 5)};
var Items = ctx.Database.SqlQuery<SearchedUserItems>("EXEC PagedSearchedUserItems #TakeFrom, #TakePerPage", xparams).ToList<SearchedUserItems>();
}

Passing a parameter as list in linq (LINQ TO ENTITIES EF6)?

I am calling a stored procedure (PS_StatistiqueEsc) via LINQ, and in the stored procedure i pass 3 parameters start,end and engin (see the code at bottom).
using (var escEnts = new escEntities()) {
var query = escEnts.PS_StatistiqueEsc(Convert.ToDateTime(start),
Convert.ToDateTime(end), engin).ToList();
}
so far so good.
my questions is how can i pass a 4th parameter as a list like (see the code at bottom) or an XML as a parameter:
For Example :
List<int> listToPassInSP = new List<int>();
list.Add(2);
list.Add(3);
list.Add(7);
using (var escEnts = new escEntities()) {
var query = escEnts.PS_StatistiqueEsc(Convert.ToDateTime(start), Convert.ToDateTime(end), engin, listToPassInSP).ToList();
}
Actually the stored procedure accept only 3 parameters ? I am going to changed change my sp which is going to accept a 4th parameter.
The actual SP signature is :
ALTER PROCEDURE [dbo].[PS_StatistiqueEsc]
#dateDebut datetime,
#dateFin datetime,
#EnginName varchar(250)
AS
But will be changed like this :
ALTER PROCEDURE [dbo].[PS_StatistiqueEsc]
#dateDebut datetime,
#dateFin datetime,
#EnginName varchar(250),
#listToPassInSP ???? (i don't know how to declare a variable like a list)
AS
Just want to pass a list to a SP and and the SP must accept a list. How can i do ?

ASP.NET Passing apostrophes in Parameter

So I have my SqlDataSource with a SelectQuery defined as follows:
SELECT * FROM table
WHERE UserName IN (#EmployeesIn);
With #EmployeesIn coming from a session variable Session["EmployeesIn"]. During Page_Load I'm taking an ArrayList members and putting the results into a string and setting the session variable:
string employeesIn = "";
foreach (string s in members)
{
employeesIn = employeesIn + "'" + s + "',";
}
employeesIn = employeesIn.TrimEnd(',');
Session["EmployeesIn"] = employeesIn;
Writing the output to the console I can see the value of the parameter #EmployeesIn
#EmployeesIn = 'bob', 'joe'
However, I'm getting zero results back ... and after monitoring from the database level I see the parameter is coming in as:
'''bob'',''joe'''
Then again if I just pass in one employee, I get results back from the SQL as expected and the parameter is passed correctly as just 'bob'. I suppose this is some safety that .NET provides against SQL injection attacks, however what's the safe way around this?
You should absolutely use parameters for this, instead of including the values within the SQL itself. You can just generate the names for the parameters, so if you had three entries you'd generate SQL of:
SELECT * FROM table WHERE UserName IN (#p0, #p1, #p2)
and then fill in those three parameters from the three values.
// Or create the command earlier, of course
List<SqlParameter> parameters = new List<SqlParameter>();
StringBuilder inClause = new StringBuilder("(");
for (int i = 0; i < members.Count; i++)
{
string parameterName = "#p" + i;
inClause.Append(parameterName);
if (i != members.Count - 1)
{
inClause.Append(", ");
}
// Adjust data type as per schema
SqlParameter parameter = new SqlParameter(parameterName, SqlDbType.NVarChar);
parameter.Value = members[i];
parameters.Add(parameter);
}
inClause.Append(")");
// Now use inClause in the SQL, and parameters in the command parameters
I think you have three options:
Comma separated values - you can pass single parameter value as CSVs and split them out in the stored procedure. I don't like this idea ... too many limitations.
XML - you can pass an XML string into the stored procedure and open it as a table using OPENXML. This will give you a table that you can use to do joins, inserts, etc., onto other tables.
Table-Valued Parameters
The better way would be to user your members array to build the query using a parameter list:
SELECT * FROM table
WHERE UserName IN (#EmployeesIn1, #EmployeesIn2, #EmployeesIn3, ... n);
Then loop through your member list, adding the parameters as necessary.
The first thing I noticed was how you're making you're comma demilited list. Try this out:
string employeesIn = string.Join(",", members.Select(x => string.format("'{0}'", x)).ToArray());
As for the parameter, you need to rethink your approach. Have you looked at table value parameters?
SQL Parameters can only represent a single value, you can however pass in multiple parameters such as:
var emps = members.Select( (e,i) => "#EMPLOYEE" + i).ToArray();
string sqlQuery = "SELECT * FROM table WHERE ";
sqlQuery+=string.Format("UserName IN ({0})", string.Join(",", emps));
//add SQL parameters used in query
for (int i = 0; i < members.Count; ++i)
{
parameters.Parameters.Add(new SqlParameter("#EMPLOYEE" + i, members[i]));
}

Passing a SQL parameter to an IN() clause using typed datasets in .NET

First apologies as there are similar questions on this site, but none of them answer this problem directly.
Im using typed datasets in VS 2010. I create a TableAdapter in a Dataset with a query like:
SELECT * from Table WHERE ID IN(#IDs)
Now if I call: TableAdapter.Fill(MyDataTable,"1,2,3") an error occurs stating that VS cannot convert 1,2,3 to type int. Fair enough.
So then i decide to change the Parameter (i.e. #IDs) type to string in the Parameter collection. Try again - still the same error message.
So is there any way this typed dataset can accept my "1,2,3" parameter? At the moment i only have a few parameters to pass, so i could easily just create 5 or so parameters and pass them separately, but what if there are hundreds? Is there any way I can call the Fill() method with my comma separated parameter?
(i know i can use Dynamic SQL to create the statement and execute it but would prefer if there is another way allowing me to keep my typed dataset for use in e.g. ReportViewer/bindingsources)
You can't use a single parameter for a list of values in this way. But there may be database-specific ways to achieve what you want. For example, with SQL Server 2005 or later you could create a table-valued function to split your string parameter, something like:
CREATE FUNCTION dbo.F_Split
(
#InputString VARCHAR(MAX)
,#Separator VARCHAR(MAX)
)
RETURNS #ValueTable TABLE (Value VARCHAR(MAX))
AS
BEGIN
DECLARE #SeparatorIndex INT, #TotalLength INT, #StartIndex INT, #Value VARCHAR(MAX)
SET #TotalLength=LEN(#InputString)
SET #StartIndex = 1
IF #Separator IS NULL RETURN
WHILE #StartIndex <= #TotalLength
BEGIN
SET #SeparatorIndex = CHARINDEX(#Separator, #InputString, #StartIndex)
IF #SeparatorIndex > 0
BEGIN
SET #Value = SUBSTRING(#InputString, #StartIndex, #SeparatorIndex-#StartIndex)
SET #StartIndex = #SeparatorIndex + 1
END
ELSE
BEGIN
Set #Value = SUBSTRING(#InputString, #StartIndex, #TotalLength-#StartIndex+1)
SET #StartIndex = #TotalLength+1
END
INSERT INTO #ValueTable
(Value)
VALUES
(#Value)
END
RETURN
END
You would then use it as follows:
SELECT * from Table WHERE ID IN (SELECT CAST(Value AS INT) FROM F_Split(#IDs, ','))
I tried a workaround for using string "contains" concept in SQL way:
In your case, change the SQL -
Original:
SELECT * from Table WHERE ID IN(#IDs)
Become:
SELECT * from Table WHERE CharIndex(','+Cast(ID As Varchar(10))+',',#IDs) > 0
With .net code -
Original:
TableAdapter.Fill(MyDataTable,"1,2,3")
Become:
TableAdapter.Fill(MyDataTable,",1,2,3,")
SQL Server 2008 has a feature called Table-Valued Parameters
So you need to
define your query as SELECT * from Table WHERE ID IN (SELECT * FROM (#IDs))
go back in the TableAdapter visual designer in Visual Studio, and update the #IDS parameter to modify the #IDS parameter as DbType=Object and ProviderType=Structured
run this SQL batch in the database your are using: CREATE TYPE MyIntArray AS TABLE ( Value INT );GO.
This will create a MyIntArray "table type" with just one column of INT type.
Now the tricky thing is to pass the "MyIntArray" type to the TableAdapter, on the ADO.NET side.
Unfortunately the Table Adapter designer does not support the SqlParameter.TypeName argument, so we need to fix it by ourselves. The goal is to modify the CommandCollection property of the generated TableAdapter class. Unfortunately, this property is protected, so you have to derive the TableAdapter or for example use Reflection to tweak it. Here is an example with a derived class:
public class MyTableAdapter2 : MyTableAdapter
{
public MyTableAdapter2()
{
SqlCommand[] cmds = base.CommandCollection;
// here, the IDS parameter is index 0 of command 1
// you'll have to be more clever, but you get the idea
cmds[1].Parameters[0].TypeName = "MyIntArray";
}
}
And this is how you can call this method:
MyTableAdapter t = new MyTableAdapter2();
// create the TVP parameter, with one column. the name is irrelevant.
DataTable tvp = new DataTable();
tvp.Columns.Add();
// add one row for each value
DataRow row = tvp.NewRow();
row[0] = 1;
tvp.Rows.Add(row);
row = tvp.NewRow();
row[0] = 2;
tvp.Rows.Add(row);
row = tvp.NewRow();
row[0] = 3;
tvp.Rows.Add(row);
t.Fill(new MyDataTable(), tvp);
I was able to solve this by setting the ClearBeforeFill property to to false and filling the TableAdapter in a foreach loop.
List<string> aList = new List<string>();
aList.Add("1");
aList.Add("2");
aList.Add("3");
yourTableAdapter.ClearBeforeFill = true;
yourTableAdapter.Fill(yourDataSet.yourTableName, ""); //clears table
foreach (string a in aList)
{
yourTableAdapter.ClearBeforeFill = false;
yourTableAdapter.Fill(yourDataSet.yourTableName, a);
}
yourTableAdapter.Dispose();
The only database I know of that can use parameters from .NET in an IN clause is PostgreSQL, because PostgreSQL has a concept of arrays that can be used with IN and Npgsql allows array (or IEnumerable<T>) parameters.
With other databases you have to either construct the SQL, or pass a string to a database procedure that converts it to the 0-or-more parameters and then acts on them.
#Joe is right.
Or you can use foreach loop to do that.
Something like:
int[] arr = new int[3];
arr[0] = "1";
arr[1] = "2";
arr[2] = "3";
foreach(vat data in arr)
{
//Do your Code here
//
var MyDatatable = obj.GetDatabyID(data);
TableAdapter.Fill(MyDataTable);
}
Regards
You also can create a list of IDs parameters
so instead of using #IDs you will use #ID1, #ID2, #ID3, etc
var sql = "SELECT * from Table WHERE ID IN (" + getKeys(values.Count) + ")";
And getKeys(count) do something like this:
var result = string.Empty;
for (int i = 0; i < count; i++)
{
result += ", #ID" + i;
}
return string.IsNullOrEmpty(result) ? string.Empty : result.Substring(1);
and Finally, add the parameters:
foreach (int i = 0; i < values.Count; i++)
{
cmd.Parameters.Add(new SqlParameter("#ID" + i, SqlDbType.VarChar) { Value = values[i]});
}
You can also use XML to pass in a parameter list into a stored procedure:
1) In Visual Studio:
Create a new Tableadapter and create a Typed Dataset to get a single record:
SELECT * FROM myTable WHERE (ID = #ID)
2) In SQL Server Manager:
Create a stored procedure with the same select fields as your typed dataset:
CREATE PROCEDURE [dbo].[usrsp_GetIds]
#paramList xml = NULL
AS
SET NOCOUNT ON;
/*
Create a temp table to hold paramaters list.
Parse XML string and insert each value into table.
Param list contains: List of ID's
*/
DECLARE #tblParams AS TABLE (ID INT)
INSERT INTO #tblParams(ID)
SELECT
XmlValues.ID.value('.', 'INT')
FROM
#paramList.nodes('/params/value') AS XmlValues(ID)
/*
Select records that match ID's in param list:
*/
SELECT * FROM myTable
WHERE
ID IN (
SELECT ID FROM #tblParams
)
3) In Visual Studio:
Add a New Query to your Tableadapter, select the stored procedure created above usrsp_GetIds and name it FillBy_Ids. This creates the command:
TableAdapter.FillBy_Ids(#paramList)
4) In Visual Studio:
In your .net code create a utility function to convert an array of strings to XML:
''' <summary>
''' Converts an array of strings to XML values.
''' </summary>
''' <remarks>Used to pass parameter values to the data store.</remarks>
Public Shared Function ConvertToXML(xmlRootName As String, values() As String) As String
'Note: XML values must be HTML encoded.
Dim sb As New StringBuilder
sb.AppendFormat("<{0}>", HttpUtility.HtmlEncode(xmlRootName))
For Each value As String In values
sb.AppendLine()
sb.Append(vbTab)
sb.AppendFormat("<value>{0}</value>", HttpUtility.HtmlEncode(value))
Next
sb.AppendLine()
sb.AppendFormat("</{0}>", xmlRootName)
Return sb.ToString
End Function
Usage Example:
Fill your data table using the strongly typed functions by passing a list of strings as a parameter:
'Create a list of record IDs to retrieve:
Dim ids as New List(of String)
ids.Add(1)
ids.Add(2)
ids.Add(3)
'Convert the list of IDs to an XML string:
Dim paramsXml As String = ConvertToXML("params", ids.ToArray)
'Get the records using standard DataTable & TableAdapter methods:
Using myDT As New MyDataTable
Using myTA As New MyTableAdapter
myTA.FillBy_Ids(myDT, paramsXml)
For Each row In myDT
'do stuff:
Next
End Using
End Using
YOUHOUHHH it works !!!
As the different elements searched are fixed length order numbers with a predefined header ("2021"), I even did not put a separator.
MySQL :
[...] WHERE
(INSTR(#orders, CAST(orders.numorder AS CHAR (11))) > 0)
[...]
C# :
string allOrders="";
foreach (string orderNum in ordersNum)
{
allOrders += orderNum;
textBoxOrdersNum.AppendText ( orderNum+"\r\n");
}
tousDetailsNomenclatureParOrderTableAdapter.FillTousDetailsNomenclatureParOrder(conduiteDataSet.TousDetailsNomenclatureParOrder, allOrders);

C# Run a procedure without specifying a parameter name

How can I execute a stored procedure that takes in parameters without having to specify the prameters name? The name of the parameter in the stored procedure may change from CustomerID to CustID so I don't want to have to keep changing my code.
Rather than doing what is provided below where you specify the parameter name -
command.Parameters.Add("#dtStart", SqlDbType.DateTime);
command.Parameters["#dtStart"].Value = startDate;
command.Parameters.Add("#CustomerID", SqlDbType.NChar);
command.Parameters["#CustomerID"].Value = customerID;
I am looking to do something like this -
command.Parameters.Add(startDate, customerID);
The name of the parameter in the stored procedure may change from CustomerID to CustID
Slap the person who does that.
Parameter names are your reliable way of identifying a parameter. The other option is sequence, seems a lot more flaky.
I don't think you can create a SqlParameter object without specifying its name. However, you should be able to use the DeriveParameters method (see MSDN) to get a collection of parameters with the names automatically retreived from the SQL server.
You can find an example here. It looks roughly like this:
SqlCommand command = // create a command for calling the stored procedure
SqlCommandBuilder.DeriveParameters(command);
// Now you can set values of parameters in a loop
for(int i = 0; i < command.Parameters.Length; i++) {
var parameter = command.Parameters[i]
// Set value of ith parameter
}
You can create a nameless SQL parameter if you force its name to null or empty after it's been added to the Parameters collection, something like this:
var par = cmd.CreateParameter();
par.Value = myValue;
cmd.Parameters.Add(par); // this will change the name to "ParameterX"
par.ParameterName = null;
Use Parameter Discovery, scroll down on: http://msdn.microsoft.com/en-us/library/ff664692(PandP.50).aspx
Using Unnamed parameters is only possible with OdbcCommand and OleDbCommand object parameters.
You could use SQL's exec, which does not ask for parameter names:
command.CommandText = string.Format(
"exec dbo.YourProcedure {0}, '{1}'",
intParameter,
stringParameter.Replace("'","''")
);
command.ExecuteNonQuery();
If your parameter source is untrustworthy, be sure to escape single quotes in string parameters. It's done for stringParameter in the snippet above.

Categories

Resources