I am using a list collection and i need to insert each item of my collection inside sql using also parameters.
Here is an example
foreach (var item in listCollection)
{
cmd.CommandText += "Insert into WebAppOrders (id) values (#id)";
cmd.Parameters.Add("#id", SqlDbType.Int).Value = item.id;
}
cmd.ExecuteNonQuery();
I am getting an error
The variable name '#id' has already been declared. Variable names must be unique within a query batch or stored procedure.
Is there any way which I can make this work?
You can use a simple count variable, which you then append to the parameter-name #id.
A sample would look like...
Example Item-class:
public class Item
{
public Item(int id)
{
this.Id = id;
}
public int Id { get; set; }
}
Simulation:
var listCollection = new List<Item>() { new Item(1), new Item(2), new Item(3), new Item(4) };
using (var connection = new SqlConnection("<your connectionstring>"))
using (var cmd = new SqlCommand("", connection))
{
connection.Open();
int i = 0; // count
foreach (var item in listCollection)
{
cmd.CommandText += $"Insert into WebAppOrders (id) values (#id{i})"; // add count here
cmd.Parameters.Add($"#id{i}", SqlDbType.Int).Value = item.Id; // ... and here
i++;
}
cmd.ExecuteNonQuery();
}
Related
I have table
itemID
storeID
qty
103
LAB
20
I want to add qantity of specific item for example:'103' stored in warehouse 'LAB'.
public void addQuantity(string store, string item, int qty)
{
con.Open();
string sql = "SELECT qty,warehouse.storeID,item.itemID FROM Item,warehouse,stocker WHERE stocker.storeID=warehouse.storeID AND stocker.itemID=item.itemID AND warehouse.storeID='"+store+"' AND Item.itemID='"+item+"' ";
using (MySqlDataAdapter adapter = new MySqlDataAdapter(sql, con))
{
using (DataTable tempTable = new DataTable())
{
adapter.Fill(tempTable);
if (tempTable.Rows.Count == 0) throw new Exception("No such product");
foreach (DataRow r in tempTable.Rows)
{
int newQty = (int)r["qty"] + qty;
if (newQty > 0)
{
r["qty"] = newQty;
qty = 0;
break;
}
else
{
MessageBox.Show("error");
}
}
using (MySqlCommandBuilder cb = new MySqlCommandBuilder(adapter))
{
adapter.UpdateCommand = cb.GetUpdateCommand();// there is error
adapter.Update(tempTable);
}
}
}
con.Close();
}
it says:"Dynamic SQL generation is not supported for multiple base tables".
what would you advice me?
If qty is integer column, you may try to:
Increment its current value by some value:
using (var updateCommand = new MySqlCommand())
{
updateCommand.CommandText = "UPDATE mytable t SET t.qty = t.qty + #newQty WHERE *...Your WHERE clause...*`"
updateCommand.Parameters.AddWithValue("#newQty", newQtyValue);`
// ...
}
Or append entire new value:
using (var updateCommand = new MySqlCommand())
{
updateCommand.CommandText = "UPDATE mytable t SET t.qty = #newQtyValue WHERE *...Your WHERE clause...*`"
updateCommand.Parameters.AddWithValue("#newQtyValue", newQtyValue);`
// ...
}
As #sticky bit said, it is preferred to use Command.Parameters.AddWithValue instead of string concat/interpolation.
I need help in reading data from DataTable and adding key, value to a Dictionary object.
//Here is the object:
public class DBName
{
public int ID { get; set; } // key
public string DatabaseName { get; set; } // value
}
My DataTable is getting data, but can't figure out how to add to Dictionary
reading DataTable and adding to Dictionary -
public static Dictionary<int,DBName> ReadToDictionary()
{
string con = #"MyConnectionString;";
SqlConnection sqlconn = new SqlConnection(con);
string sqlQuery = #"SELECT DISTINCT DisplayName AS DatabaseName, RANK() OVER(ORDER BY MIN(id)) AS ID
FROM dbo.MyTable
GROUP BY DisplayName";
Dictionary<int, DBName> dictionaryDBName = new Dictionary<int, DBName>();
SqlCommand sqlcmd = new SqlCommand(sqlQuery, sqlconn);
{
using (SqlDataAdapter da = new SqlDataAdapter(sqlcmd))
{
try
{
DataTable dt = new DataTable();
da.Fill(dt);
foreach (DataRow dr in dt.Rows)
{
foreach (DataColumn col in dt.Columns)
{
//how to add key and Value?
dictionaryDBName.Add(dr[1], DBName);
}
// anything here?
}
}
catch (Exception ex)
{
//
}
finally
{
sqlconn.Close();
}
}
}
return dictionaryDBName;
}
Please guide.
You are probably much better off just using a DataReader. No need to fill a DataTable and then try looping through it, just loop the reader.
Further points:
Don't swallow exceptions, deal with them properly
You need to have a using on the connection, and then you don't need to call Close
Are you aware of the differences between RANK, DENSE_RANK and ROW_NUMBER?
public static Dictionary<int,DBName> ReadToDictionary()
{
string con = #"MyConnectionString;";
using(SqlConnection sqlconn = new SqlConnection(con))
{
string sqlQuery = #"SELECT DISTINCT
DisplayName AS DatabaseName
,CAST(RANK() OVER(ORDER BY MIN(id)) AS int) AS ID
FROM
dbo.MyTable
GROUP BY
DisplayName";
sqlconn.Open();
Dictionary<int, DBName> dictionaryDBName = new Dictionary<int, DBName>();
using(SqlCommand sqlcmd = new SqlCommand(sqlQuery, sqlconn))
using (var reader = sqlcmd.ExecuteReader())
{
while(reader.Read())
{
DBName n = new DBName { ID = reader.GetInt32(1), DatabaseName = reader[0] as string};
dictionaryDBName.Add(n.ID, n);
}
}
return dictionaryDBName;
}
}
Here's the LINQ solution. The key is to use the powerful ToDictionary extension method.:
var reader = sqlCmd.ExecuteReader();
var dataTable = new DataTable()
dataTable.Load(reader);
var dictionary = dataTable.Rows.Cast<DataRow>().ToDictionary
(
x => (int)x[0], //Convert column 0 into the dictionary's key
x => (string)x[1] //Convert column 1 into the dictionary's value
);
However, since SqlDataReader implements IEnumerable, you can skip the data table entirely like this:
var reader = sqlCmd.ExecuteReader();
var dictionary = reader.Cast<IDataRecord>().ToDictionary
(
x => x.GetInt32(0), //Convert column 0 into the dictionary's key
x => x.GetString(1) //Convert column 1 into the dictionary's value
);
See also this answer.
I want an efficient way to retrieve all info that shares the same Foreign key in a table, and store the data in list/array.
I can read several rows from one column:
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
int idForeignKey = inputIdFkey //Implemented on the WebPage for testing purposes
List<string> result = new List<string>();
string oString = "Select Column from Table where foreignKey = #fKey";
conn.Open();
SqlCommand oCmd = new SqlCommand(oString, conn);
oCmd.Parameters.AddWithValue("#fKey", idForeignKey);
using (SqlDataReader oReader = oCmd.ExecuteReader())
{
while (oReader.Read())
{
result.Add(oReader.GetString(0));
}
}
conn.Close();
And I can read several columns if I am targeting one specific row:
int sqlData1;
int sqlData2;
int sqlData3;
string oString = "Select * from Table where TableID = #tId";
SqlCommand oCmd = new SqlCommand(oString, conn);
oCmd.Parameters.AddWithValue("#tId", 1001);
conn.Open();
using (SqlDataReader oReader = oCmd.ExecuteReader())
{
while (oReader.Read())
{
sqlData1 = oReader["Row1"].ToString();
sqlData2 = oReader["Row2"].ToString();
sqlData3 = oReader["Row3"].ToString();
}
}
conn.Close();
But I would love to be able to read all/specific data that has the same foreign key. So I want to be able to retrieve several rows, save them to a list, and retrieve several other row data from a different column that shares the same foreign key.
I imagine it to be something like this:
int idForeignKey = inputIdFkey //Implemented on the WebPage for testing purposes
List<int> intList = new List<int>();
List<string> stringList = new List<string>();
List<DateTime> dateList = new List<DateTime>();
string oString = "Select * from Table where ForeignKey = #fKey";
conn.Open();
SqlCommand oCmdSleep = new SqlCommand(oString, conn);
oCmdSleep.Parameters.AddWithValue("#fKey", idForeignKey);
using (SqlDataReader oReader = oCmdSleep.ExecuteReader())
{
while (oReader.Read())
{
intList.Add(oReader["Column1"].GetDateTime(0));
dstringList.Add(oReader["Column3"].GetDateTime(0));
dateList.Add(oReader["Column4"].GetDateTime(0));
}
}
conn.Close();
But this does not working... Please advice me
If you use something like Dapper it would simplify mapping your query result to List<T>.
Add Dapper to your project using nuget.
Install-Package Dapper -Version 1.50.5
Add using for Dapper at the top of your class where you are running the query.
using Dapper;
Add a class that matches the structure of your query results. There are ways to do this using a script or utility app. Here's an app for that.
public class MyClass
{
public int MyId { get; set; }
public string MyName { get; set; }
public DateTime MyDateTime { get; set; }
}
Then where you run the query do so like this.
using (conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString))
{
var MyList = conn.Query<MyClass>(#"select * from Table where ForeignKey = #fKey",
new { fKey = "SomeKey" }).ToList();
}
Once the query runs, you can then iterate across MyList.
foreach (var myItem in MyList)
{
// Do something with myItem
}
If you want to bind the results, simply remove .ToList() from the end of the query as its default is IObservable<T>.
No need to give the name of the column, simply use the already established index:
int idForeignKey = inputIdFkey //Implemented on the WebPage for testing purposes
List<int> intList = new List<int>();
List<string> stringList = new List<string>();
List<DateTime> dateList = new List<DateTime>();
string oString = "Select * from Table where ForeignKey = #fKey";
conn.Open();
SqlCommand oCmdSleep = new SqlCommand(oString, conn);
oCmdSleep.Parameters.AddWithValue("#fKey", idForeignKey);
using (SqlDataReader oReader = oCmdSleep.ExecuteReader())
{
while (oReader.Read())
{
intList.Add(oReader.GetDateTime(0));
dstringList.Add(oReader.GetDateTime(3));
dateList.Add(oReader.GetDateTime(4));
}
}
conn.Close();
Here is a breakdown:
listVariable.Add(oReader.GetDataType("Index of column"));
This way you get to retrieve all row data for shared foreign key and the option to do so for as many columns as you want.
Trying to communicate with the database, I am little bit confused about how to pass a value as a parameter(for ex. an itemID) and get back the records that are having this ID.
Here is my stored procedure:
ALTER PROCEDURE [dbo].[sp_lightItem]
(
#itemID INT
)
AS
BEGIN
SELECT [itemID],
[itemName],
[itemLocation],
[itemChBy]
FROM [dbo].[Item]
WHERE itemSystemType='E' and itemID=#itemID ORDER BY itemID DESC;
END
And this is my c# code so far..
public string LoadItemNew(int ItemID)
{
var acf = new AcFunctions();
var newstorevalue = SqlHelper.ExecuteDataset(acf.AcConn(), "sp_lightItem", ItemID);
}
As you can see in stored procedure, what I want is to get back those 4 elements:
[itemID],[itemName],[itemLocation],[itemChBy]
Unfortunately I do not know how to get them back/how to call them in c# function.
Any help is welcome.
i dont have enough repo to comment , can you provide the definition of
AcFunctions();
i am sure you it must be returning ConnectionString
try this
public string LoadItemNew(int ItemID)
{
var acf = new AcFunctions();
var newstorevalue = SqlHelper.ExecuteDataset(acf.AcConn(), "sp_lightItem", new SqlParameter ("#itemID",ItemID));
}
Edit 1
try this
public string LoadItemNew(int ItemID)
{
List<string> testList = new List<string>();
var acf = new AcFunctions();
var newstorevalue = SqlHelper.ExecuteReader(acf.AcConn(), "sp_lightItem", new SqlParameter ("#itemID",ItemID));
if(newstorevalue.HasRows)
{
while(newstorevalue.Read())
{
testList.Add(newstorevalue["itemID"].ToString());
testList.Add(newstorevalue["itemName"].ToString());
testList.Add(newstorevalue["itemLocation"].ToString());
testList.Add(newstorevalue["itemChBy"].ToString());
}
}
}
You can try with this approach, I will use Data Transfer Object for holding data retrieved from database and Execute DataReader for reading.
First of all, you need to create a DTO class, I will call it LightItemDTO
public class LightItemDTO
{
public int Id { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public string ChangedBy { get; set; }
}
Note: How to know the type of properties, you can reference this link: SQL Server Data Type Mappings
And now, I will using ADO.NET for execute the stored procedure to get data from database
public IEnumerable<LightItemDTO> GetLightItem(string itemText, string sqlConnectionString)
{
var results = new List<LightItemDTO>();
using (var con = new SqlConnection(sqlConnectionString))
{
using (var cmd = new SqlCommand("sp_lightItem", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ItemID", SqlDbType.VarChar).Value = itemText;
con.Open();
using (var reader = cmd.ExecuteReader())
{
results.Add(new LightItemDTO
{
Id = Convert.ToInt32(reader["itemID"]),
Name = reader["itemName"].ToString(),
Location = reader["itemLocation"].ToString(),
ChangedBy = reader["itemChBy"].ToString()
});
}
}
}
return results;
}
Using DataReader is the best practice with high performance.
ADO.NET is the manual way to achieve this task, you can use some ORM framework for do it easier, such as: Entity Framework, Dapper.NET ...
You could execute stored procedure with parameters in following:
using (SqlConnection con = new SqlConnection(dc.Con)) {
using (SqlCommand cmd = new SqlCommand("sp_lightItem", con)) {
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ItemID", SqlDbType.VarChar).Value = itemId.Text;
con.Open();
cmd.ExecuteNonQuery();
}
}
first of all set the Commandtype as stored procedure and that procedure will return some data which you will save in dataset and then return the data set to where ever you want to populate the data
public DataSet LoadItemNew(int ItemID)
{
var acf = new AcFunctions();
return DataSet ds = SqlHelper.ExecuteDataset(acf.AcConn(),CommandType.StoredProcedure, "sp_lightItem",new SqlParameter("#itemID" ItemID);
}
You can try like this ..
public string LoadItemNew(int ItemID)
{
var acf = new AcFunctions();
List<SqlParameter> parameters = new List<SqlParameter>();
parameters.Add(new SqlParameter("#itemID", ItemID));
DataSet Ds = SqlHelper.ExecuteDataset(acf.AcConn(),CommandType.StoredProcedure, "sp_lightItem" , parameters.ToArray());
return "ok";
}
when you select items from the listbox, you want to delete selecteditems. Why doesnt it work when selected data removed from database? I must have missed something. I got error message
No mapping exists from object type.
This is a method parameter:
IsDelete = _dinnerRemover.RemoveDinners(lstDinner.SelectedItems);
This class is to delete data from database
public bool RemoveDinners(dynamic dinnerItems)
{
Dinners = new List<FoodInformation>();
using (var sqlConn = new SqlConnection(_sqlConnectionString))
{
const string sqlQuery = "delete from DinnerTemplates where Dinner = #dinner";
using (var command = new SqlCommand(sqlQuery, sqlConn))
{
try
{
//command.CommandType = CommandType.StoredProcedure;
//command.CommandText = "sp_dinner";
foreach (var item in dinnerItems)
{
command.CommandType = CommandType.Text;
command.Parameters.AddWithValue("#dinner", item);
command.ExecuteNonQuery();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
sqlConn.Close();
}
}
}
return Dinners;
}
If dinnerItems is a list of strings then say that, don't use dynamic unless you absolutely have to.
To delete a bunch of items, issue one sql query with an IN clause. Don't issue lots of individual queries.
Try this:
public int RemoveDinners(List<string> dinnerItems)
{
using (var sqlConn = new SqlConnection(_sqlConnectionString))
{
const string sqlQuery = "delete from DinnerTemplates where Dinner in ({0})";
using (var command = new SqlCommand())
{
var paramNames = new string[dinnerItems.Count];
int i = 0;
foreach (string item in dinnerItems)
{
string paramName = "#Dinner" + i;
command.Parameters.AddWithValue(paramName, item);
paramNames[i] = paramName;
i += 1;
}
command.CommandText = String.Format(sqlQuery, String.Join(",", paramNames));
command.Connection = sqlConn;
command.CommandType = CommandType.Text;
sqlConn.Open();
return command.ExecuteNonQuery();
}
}
}
You have to bear in mind that you kind of left out some really relevant code, like what is a DinnerItem, since you're getting the error on a line related to its type.
However, the reason you're getting that error is because item can't be marshaled to a type of something like string or int.
That's probably because item is likely a custom class. One option would be to override the ToString method of the class:
public override string ToString() {
// return some property value, or set of property values
// strung together here.
}
another option would be to send in the actual Property you want off of item when issuing AddWithValue.
You need to define SqlDbType for command's parameter.
don't use dynamic type,use string..
if i were you,i would rather
IsDelete = _dinnerRemover.RemoveDinners(lstDinner.SelectedItems.ToString());
change the parameter to :
public bool RemoveDinners(string dinnerItems)
and the query to :
const string sqlQuery = "delete from DinnerTemplates where Dinner = dinnerItems";