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]));
}
Related
I have a list of items with different ids which represent a SQL table's PK values.
Is there any way to build an efficient and safe statement?
Since now I've always prepared a string representing the statement and build it as I traversed the list via a foreach loop.
Here's an example of what I'm doing:
string update = "UPDATE table SET column = 0 WHERE";
foreach (Line l in list)
{
update += " id = " + l.Id + " OR";
}
// To remove last OR
update.Remove(update.Length - 3);
MySqlHelper.ExecuteNonQuery("myConnectionString", update);
Which feels very unsafe and looks very ugly.
Is there a better way for this?
So yeah, in SQL you've got the 'IN' keyword which allows you to specify a set of values.
This should accomplish what you would like (syntax might be iffy, but the idea is there)
var ids = string.Join(',', list.Select(x => x.Id))
string update = $"UPDATE table SET column = 0 WHERE id IN ({ids})";
MySqlHelper.ExecuteNonQuery("myConnectionString", update);
However, the way you're performing your SQL can be considered dangerous (you should be fine as this just looks like ids from a DB, who knows, better to be safe than sorry). Here you're passing parameters straight into your query string, which is a potential risk to SQL injection which is very dangerous. There are ways around this, and using the inbuilt .NET 'SqlCommand' object
https://www.w3schools.com/sql/sql_injection.asp
https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand?view=dotnet-plat-ext-6.0
It would be more efficient to use IN operator:
string update = "UPDATE table SET column = 0 WHERE id IN (";
foreach (Line l in list)
{
update += l.Id + ",";
}
// To remove last comma
update.Remove(update.Length - 1);
// To insert closing bracket
update += ")";
If using .NET Core Framework, see the following library which creates parameters for a WHERE IN. The library is a port from VB.NET which I wrote in Framework 4.7 years ago. Clone the repository, get SqlCoreUtilityLibrary project for creating statements.
Setup.
public void UpdateExample()
{
var identifiers = new List<int>() { 1, 3,20, 2, 45 };
var (actual, exposed) = DataOperations.UpdateExample(
"UPDATE table SET column = 0 WHERE id IN", identifiers);
Console.WriteLine(actual);
Console.WriteLine(exposed);
}
Just enough code to create the parameterizing SQL statement. Note ActualCommandText method is included for development, not for production as it reveals actual values for parameters.
public static (string actual, string exposed) UpdateExample(string commandText, List<int> identifiers)
{
using var cn = new SqlConnection() { ConnectionString = GetSqlConnection() };
using var cmd = new SqlCommand() { Connection = cn };
cmd.CommandText = SqlWhereInParamBuilder.BuildInClause(commandText + " ({0})", "p", identifiers);
cmd.AddParamsToCommand("p", identifiers);
return (cmd.CommandText, cmd.ActualCommandText());
}
For a real app all code would be done in the method above rather than returning the two strings.
Results
UPDATE table SET column = 0 WHERE id IN (#p0,#p1,#p2,#p3,#p4)
UPDATE table SET column = 0 WHERE id IN (1,3,20,2,45)
This question already has answers here:
Pass Array Parameter in SqlCommand
(11 answers)
Closed 6 years ago.
For some reason the Sqlparameter for my IN() clause is not working. The code compiles fine, and the query works if I substitute the parameter with the actual values
StringBuilder sb = new StringBuilder();
foreach (User user in UserList)
{
sb.Append(user.UserId + ",");
}
string userIds = sb.ToString();
userIds = userIds.TrimEnd(new char[] { ',' });
SELECT userId, username
FROM Users
WHERE userId IN (#UserIds)
You have to create one parameter for each value that you want in the IN clause.
The SQL needs to look like this:
SELECT userId, username
FROM Users
WHERE userId IN (#UserId1, #UserId2, #UserId3, ...)
So you need to create the parameters and the IN clause in the foreach loop.
Something like this (out of my head, untested):
StringBuilder sb = new StringBuilder();
int i = 1;
foreach (User user in UserList)
{
// IN clause
sb.Append("#UserId" + i.ToString() + ",");
// parameter
YourCommand.Parameters.AddWithValue("#UserId" + i.ToString(), user.UserId);
i++;
}
Possible "cleaner" version:
StringBuilder B = new StringBuilder();
for (int i = 0; i < UserList.Count; i++)
YourCommand.Parameters.AddWithValue($"#UserId{i}", UserList[i].UserId);
B.Append(String.Join(",", YourCommand.Parameters.Select(x => x.Name)));
If you are using SQL 2008, you can create a stored procedure which accepts a Table Valued Parameter (TVP) and use ADO.net to execute the stored procedure and pass a datatable to it:
First, you need to create the Type in SQL server:
CREATE TYPE [dbo].[udt_UserId] AS TABLE(
[UserId] [int] NULL
)
Then, you need to write a stored procedure which accepts this type as a parameter:
CREATE PROCEDURE [dbo].[usp_DoSomethingWithTableTypedParameter]
(
#UserIdList udt_UserId READONLY
)
AS
BEGIN
SELECT userId, username
FROM Users
WHERE userId IN (SELECT UserId FROM #UserIDList)
END
Now from .net, you cannot use LINQ since it does not support Table Valued Parameters yet; so you have to write a function which does plain old ADO.net, takes a DataTable, and passes it to the stored procedure: I've written a generic function I use which can do this for any stored procedure as long as it takes just the one table-typed parameter, regardless of what it is;
public static int ExecStoredProcWithTVP(DbConnection connection, string storedProcedureName, string tableName, string tableTypeName, DataTable dt)
{
using (SqlConnection conn = new SqlConnection(connection.ConnectionString))
{
SqlCommand cmd = new SqlCommand(storedProcedureName, conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter p = cmd.Parameters.AddWithValue(tableName, dt);
p.SqlDbType = SqlDbType.Structured;
p.TypeName = tableTypeName;
conn.Open();
int rowsAffected = cmd.ExecuteNonQuery(); // or could execute reader and pass a Func<T> to perform action on the datareader;
conn.Close();
return rowsAffected;
}
}
Then you can write DAL functions which use this utility function with actual names of stored procedures; to build on the example in your question, here is what the code would look like:
public int usp_DoSomethingWithTableTypedParameter(List<UserID> userIdList)
{
DataTable dt = new DataTable();
dt.Columns.Add("UserId", typeof(int));
foreach (var userId in updateList)
{
dt.Rows.Add(new object[] { userId });
}
int rowsAffected = ExecStoredProcWithTVP(Connection, "usp_DoSomethingWithTableTypedParameter", "#UserIdList", "udt_UserId", dt);
return rowsAffected;
}
Note the "connection" parameter above - I actually use this type of function in a partial DataContext class to extend LINQ DataContext with my TVP functionality, and still use the (using var context = new MyDataContext()) syntax with these methods.
This will only work if you are using SQL Server 2008 - hopefully you are and if not, this could be a great reason to upgrade! Of course in most cases and large production environments this is not that easy, but FWIW I think this is the best way of doing this if you have the technology available.
SQL Server sees your IN clause as:
IN ('a,b,c')
What it needs to look like is:
IN ('a','b','c')
There is a better way to do what you're trying to do.
If the user id's are in the DB, then the IN clause should be changed to a subquery, like so:
IN (SELECT UserID FROM someTable WHERE someConditions)
This is a hack -- it doesn't work well with indexes, and you have to be careful it works right with your data, but I've used it successfully in the past:
#UserIDs LIKE '%,' + UserID + ',%' -- also requires #UserID to begin and end with a comma
I have an application I need to create which, given some user input in the form of CSV, needs to parse and generate this CSV into multiple formats. One of these formats is a series of SQL INSERT statements (as a string) for each line of CSV.
(At this point you can assume I've already parsed the CSV into a list of values or something, so that is not the point of the question)
Given that this input could contain vulnerabilities, I wish to generate the INSERT statements which have been validated and sanitised.
I am familiar with creating an SqlCommand object and adding values to its list of Parameters, but looking over a similar question it doesn't appear to work in way I had hoped.
So is there a way to generate sanitised SQL statements, as strings, in the way I need to?
EDIT: This is an example what I want to do.
CSV:
id,name,country
1,Alice,France
2,Bob,Austria
3,Carol,Germany
SQL:
...
INSERT INTO Users (id, name, country) VALUES (1, 'Alice', 'France');
INSERT INTO Users (id, name, country) VALUES (2, 'Bob', 'Austria');
INSERT INTO Users (id, name, country) VALUES (3, 'Carol', 'Germany');
...
As there are no data types given in the CSV, the application has to determine that as well.
Not so much an answer, as a cautionary note. If you end up needing to go the 'classic' escaping route to do this, and really need safety (i.e. the data is coming in from untrusted source), don't forget it's not just simple escaping you need to worry about.
Basic character escaping we hear about all the time:
' -> '' apostrophe's and stuff are quite obvious and documented ad-nauseum
; multiple-commands in one statement - not always allowed by the DB, but dangerous
If you're parsing for "nefarious behaviour" though, have you thought about:
SELECT/*not important*/1/*really...*/FROM/*im serious*/users
SELECT%09FIELDS%09FROM%0dTABLE_NAME
WHERE username=CONCAT(CHAR(97),CHAR(100),CHAR(109),CHAR(105),CHAR(110))
SELECT passwd FROM users WHERE username=0x61646d696e
In summary: Here Be Dragons.
http://www.ihteam.net/papers/blind-sqli-regexp-attack.pdf
http://ferruh.mavituna.com/sql-injection-cheatsheet-oku/#HexbasedSamples
Well chances are if you don't wan't to use SQL objects then you would have to sanatize the entries yourself. I'm not aware of any recommended format for SQL however for MySQL the following would work. I've changed it to work with SQL however I cant garantee it has covered all possible injection attacks.
public string sqlEscape(string VAL)
{
if (VAL == null)
{
return null;
}
return "\"" + Regex.Replace(VAL, #"[\r\n\x00\x1a\\'""]", #"\$0") + "\"";
}
to use you would then do (assuming your CSV line is stored in an array called csv):
string query = #"INSERT INTO Users (id, name, country) VALUES (" + sqlEscape(csv[0]) + ", " + sqlEscape(csv[1]) + ", " + sqlEscape(csv[2]) + ");";
if anyone can enhance this let me know!
Because i don't know how you've stored your variables, i'll show you a complete, possible implementation with your sample data using a List<Dictionary<String, Object>>():
Add your sample-data:
var tableName = "Users";
var records = new List<Dictionary<String, Object>>();
var recordFields = new Dictionary<String, Object>();
recordFields.Add("id", 1);
recordFields.Add("name", "Alice");
recordFields.Add("country", "France");
records.Add(recordFields);
recordFields = new Dictionary<String, Object>();
recordFields.Add("id", 2);
recordFields.Add("name", "Bob");
recordFields.Add("country", "Austria");
records.Add(recordFields);
recordFields = new Dictionary<String, Object>();
recordFields.Add("id", 3);
recordFields.Add("name", "Carol");
recordFields.Add("country", "Germany");
records.Add(recordFields);
Generate the parametrized insert statements:
using (var con = new SqlConnection(Settings.Default.ConnectionString))
{
con.Open();
foreach (var record in records)
{
String insertSql = String.Format("INSERT INTO {0} ({1}) VALUES ({2});"
, tableName
, String.Join(",", record.Select(r => r.Key))
, String.Join(",", record.Select(r => "#" + r.Key)));
using (var insertCommand = new SqlCommand(insertSql, con))
{
foreach (var field in record)
{
var param = new SqlParameter("#" + field.Key, field.Value);
insertCommand.Parameters.Add(param);
}
insertCommand.ExecuteNonQuery();
}
}
}
Note that this is not really tested(it compiles and looks good) but it should help you anyway.
Edit:
#Oded: I think the problem is that daniel doesn't want to run the
inserts, but show/output/save them. So using the SqlCommand parameters
is no good, because you don't see the generated SQL.
#Cylindric That's correct
That's not possible and a contradiciton, you cannot use a String with SqlParameters. So i'm afraid you're open to Sql-Injection if you would run these inserts later. I would suggest to use above code when you're actually running the statemenets.
I'm writing a general purpose function for feeding query string straight into a sproc. The algorithm is fairly basic - loop through query keys, use them as parameter names while the value are used as parameter values. So it looks like something like this:
ArrayList pars = new ArrayList();
SqlParameter p;
int tryInt;
for (int i = 0; i < req.QueryString.Count; i++)
{
key = req.QueryString.AllKeys[i];
if (int.TryParse(req[key], out tryInt))
{
p = new SqlParameter("#" + key, SqlDbType.Int);
p.Value = tryInt;
pars.Add(p);
}
}
This works fine so far, except that of course all query keys must match the parameters for the sproc, if they don't I get an SQL exception saying something like
#someParameter is not a parameter for procedure some_sproc
But I need to be able to pass in variables in the query string that won't be passed into the sproc, so I need a way to "ignore" them.
Is there a way to test whether a given stored procedure expects a certain parameter? So that I can do something along these lines
if (paramExists("#" + key, "some_sproc") && int.TryParse(req[key], out tryInt))
{
p = new SqlParameter("#" + key, SqlDbType.Int);
p.Value = tryInt;
pars.Add(p);
}
You can use SqlCommandBuilder to get SP parameters:
using (SqlConnection sqlConn = new SqlConnection(yourConnStr))
using (SqlCommand sqlCmd = new SqlCommand(yourProcedureName, sqlConn))
{
sqlConn.Open();
sqlCmd.CommandType = CommandType.StoredProcedure;
SqlCommandBuilder.DeriveParameters(sqlCmd);
// now you can check parameters in sqlCmd.Parameters
}
More details here
you can interrogate the ANSI information_schema.parameters view, it returns all parameters with their position
SELECT parameter_name, ordinal_position FROM INFORMATION_SCHEMA.parameters
WHERE SPECIFIC_NAME = 'some_sproc'
Most DBs store "metadata" such as the structure of stored procedures in accessible system tables/views. If you're using MSS, I would look at sys.procedures and sys.parameters; join these two views and filter by procedure name.
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);