Populating a dropdown list from database - c#

I have a asp.net page listing products (pulled from a database) with edit/delete buttons. The user can edit the product by clicking the edit button. I've been able to pull in data from the db to the textboxes based on the product selected. However, I am getting duplicate items in the dropdown box. It's only supposed to have 32 items and has 160 items (each item is appearing 5 times). I've used Items.Clear() but am still getting duplicates. Also the dropbox just shows the first item in the list rather than the appropriate item for that product that is currently in the db. Can anyone see what I may be doing wrong?
Thanks.
protected void Page_Load(object sender, EventArgs e)
{
this.Master.HighlightNavItem("Products");
string Mode = (Request.QueryString["Mode"]);
//Upon opening page, if this is an edit to existing product (populate product data)
if (Mode == "E")
{
if (!IsPostBack)
{
int ProductID = Int32.Parse(Request.QueryString["ID"]);
//Declare the connection object
SqlConnection Conn = new SqlConnection();
Conn.ConnectionString = ConfigurationManager.ConnectionStrings["MyDatabase"].ConnectionString;
//Connect to the db
Conn.Open();
//Define the query
//string sql = "SELECT dbo.Vendor.VendorName, dbo.Vendor.VendorID, dbo.Product.ProductName, dbo.Product.ProductNumber, dbo.lu_Category.CategoryID, dbo.lu_Category.Description FROM dbo.Product INNER JOIN dbo.Vendor ON dbo.Product.VendorID = dbo.Vendor.VendorID INNER JOIN dbo.lu_Category ON dbo.Product.CategoryID = dbo.lu_Category.CategoryID WHERE ProductID=#ProductID";
string sql = "SELECT ProductName, ProductNumber, ProductDescription, Cost, Markup, Unit, QtyOnHand, ShippingWeight, dbo.Vendor.VendorID, VendorName, dbo.lu_Category.CategoryID, Description FROM Vendor, Product, lu_Category WHERE ProductID=#ProductID";
//Declare the Command
SqlCommand cmd = new SqlCommand(sql, Conn);
//Add the parameters needed for the SQL query
cmd.Parameters.AddWithValue("#ProductID", ProductID);
//Declare the DataReader
SqlDataReader dr = null;
//Fill the DataReader
dr = cmd.ExecuteReader();
//Loop through the DataReader
ddlVendor.Items.Clear();
while (dr.Read())
{
txtProductName.Text = dr["ProductName"].ToString();
txtProductNo.Text = dr["ProductNumber"].ToString();
txtDescription.Text = dr["ProductDescription"].ToString();
txtCost.Text = dr["Cost"].ToString();
txtMarkup.Text = dr["Markup"].ToString();
txtUnit.Text = dr["Unit"].ToString();
txtQty.Text = dr["QtyOnHand"].ToString();
txtWeight.Text = dr["ShippingWeight"].ToString();
ListItem li = new ListItem();
li.Text = dr["VendorName"].ToString();
li.Value = dr["VendorID"].ToString();
ddlVendor.Items.Add(li);

You should change your SQL query and remove the , type of joins.
Then test your query directly in your database to make sure you don't get doubles.
The rest of your code looks fine so i'm sure testing your query will solve your problem.
Do not use Comma Joins it's deprecated.

I think you should have used inner or outer join to get your data. With ',' separated joins you are receiving data from both tables. like 32 rows of one table are getting appneded with 5 rows of other table.

Related

How to show different data to different users?

Lets say parents have logged into system, and I would like to show them only their children details in GridView control. Up to one child can be studying at the same school. How can I achieve that? I've tried to join 3 tables in my database and display into GridView like this:
protected void Page_Load(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection("Data Source = DESKTOP-H7KQUT1; Initial Catalog = SAOS; Integrated Security = True");
String query = "SELECT s.FName FROM student s INNER JOIN student_parent sp ON s.SID = sp.SID INNER JOIN parent p ON sp.PID = p.PID WHERE p.PID = ";
SqlCommand cmd = new SqlCommand(query, con);
SqlDataReader dr;
con.Open();
dr = cmd.ExecuteReader();
GridView1.DataSource = dr;
GridView1.DataBind();
con.Close();
}
I can display student details successfully but I don't know how to display different student details to different parents. I stuck at this line:
String query = "SELECT s.FName FROM student s INNER JOIN student_parent sp ON s.SID = sp.SID INNER JOIN parent p ON sp.PID = p.PID WHERE p.PID = ";
Any Advises & References are appreciated
If I'm understanding your schema, it looks like you've mostly got it: you need to get the parent id (pid) by looking at which user has logged into your web app. Then you would use that information to most likely execute a query to lookup the pid. Then you pass that as a parameter to your SQL statement in the form 'where p.pid=#PID'. Here's some documentation on how to construct a query with parameters: https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand.parameters?view=netframework-4.7.2
Just one word of warning: be very careful about how you get and verify the parent id (i.e. don't just pass it in as a URL parameter, make sure it's something you authenticate and keep on the server side so it can't easily subject you to spoofing and injection attacks)

Save order data to local database

I am using C# to create a Windows form application. How do I create a local database table to save the data described below? Currently my data table consists of the columns of "inv_Id", "Item_Id" and "quantity". I know I need to create the columns to store each of the data items below, but I do not know how to structure the table.
item={chair,table,pen, thumbdrive}
Price={5,10,2,48}
subtotal
tax
total
I am new at C#. I did a search for this, and found things like e.g.https://www.youtube.com/watch?v=I5qvyyhUKfY
The data is shown in a list box and looks like:
Ordered Item:chair Price:$5.00
Ordered Item:table Price:$10.00
Ordered Item:pen Price:$2.00
Ordered Item:thumbdrive Price:$48.00
Subtotal:$65.00 Tax:$3.90 Total:$68.90
The purpose for me is to create the invoice then save it in the database after calculating everything.
Here is the code that I get the data load the data from db into drop down list (for user to select which item they want to buy), then (cust selected item will be listed in the listOuput box for calculating) user will select the item, then list box will show the selected output and calculate the total like a receipt.
After calculating, I wish to store all the data at the listOutput box to my db, but I having problem here.
Problem: I do not know how to move all my data from list box to
database, and link them together in the structure.
public partial class Select_Item : Form
{
SqlConnection con = new SqlConnection( #"Data Source=(LocalDB)\v11.0;AttachDbFilename=C:\Users\oo\Documents\Data.mdf;Integrated Security=True;Connect Timeout=30");
SqlDataAdapter da = new SqlDataAdapter();
DataTable dt = new DataTable();
public struct Orders
{
public string item;
public double price;
}
const double TAX=0.06;
Orders order = new Orders();
static double subtotal=0;
static double totalTaxes=0;
static double total;
string finalBill = "FINAL BILL: \n";
public Select_Item()
{
InitializeComponent();
}
private void getValues(string custOrder)
{
order.item = custOrder;
String a = comboBox1.Text;
order.price = Convert.ToDouble(custOrder);
listOutput.Items.Add("Price: " + order.price);
finalBill += "Ordered Item: " + a + "\nPrice: " + order.price.ToString("C2") + "\n";
updateBill();
}
private void updateBill()
{
subtotal += order.price;
total += order.price + (order.price * TAX);
totalTaxes += order.price * TAX;
listOutput.Items.Clear();
listOutput.Items.AddRange(finalBill.Split('\n'));
listOutput.Items.Add("Subtotal:" + subtotal.ToString("C2"));
listOutput.Items.Add("Tax:" + totalTaxes.ToString("C2"));
listOutput.Items.Add("Total:" + total.ToString("C2"));
}
private void dropdownSelection(object sender, EventArgs e)
{
if (sender == comboBox1)
System.Diagnostics.Debug.WriteLine("test " + comboBox1.SelectedValue.ToString());
getValues(comboBox1.SelectedValue.ToString());
}
Edited Code:
private void StoreData()
{
int invoiceID;
using (var con1 = new SqlConnection(#"Data Source=(LocalDB)\v11.0;AttachDbFilename=C:\Users\choo\Documents\Data.mdf;Integrated Security=True;Connect Timeout=30"))
{
con.Close();
con.Open();
using (var cmd = con.CreateCommand())
{
cmd.CommandText = #"insert into Invoice(subtotal,tax,total) values (#subtotal,#tax,#total); select SCOPE_IDENTITY() as InvoiceID;";
cmd.Parameters.AddWithValue("#subtotal", subtotal);
cmd.Parameters.AddWithValue("#tax", tax);
cmd.Parameters.AddWithValue("#total", total);
using (var reader = cmd.ExecuteReader())
{
if (reader.Read())
invoiceID = (int)reader["InvoiceID"];
}
}
foreach (var item in OrderItems.Rows)
{
using (var cmd = con.CreateCommand())
{
cmd.CommandText = #"insert into InvoiceItem(invoiceID,Item_Id,quantity) values (#invoiceID,#Item_Id,#quantity);";
// cmd.Parameters.AddWithValue("#InvoiceID", invoiceID);
cmd.Parameters.AddWithValue("#invoiceID", Convert.ToInt32("invoiceID"));
cmd.Parameters.AddWithValue("#Item_Id", Convert.ToInt32("Item_Id"));
cmd.Parameters.AddWithValue("#quantity", Convert.ToInt32("quantity"));
cmd.ExecuteNonQuery();
}
}
}
}
I am assuming you are saving the data to an SQL database. Your invoice and item tables share a many to many relationship, so you should use a third table to link them together.
Invoice: invoiceID, subtotal, tax, total
Item: itemID, price
InvoiceItem: invoiceItemID, invoiceID, itemID, quantity
The InvoiceItem table has foreign keys to the other two. This way you keep your invoice and item data separate and clean; there's no mucking about with 10 different "pen" items because 10 different orders included a pen.
Note that you can calculate Invoice.subtotal by selecting all the items from that invoice and calculating the sum of quantity * price. I recommend including it on the Invoice table for convenience's sake.
To get the order into the database, you want something like this:
private void StoreData()
{
int invoiceID;
using(var con = new SqlConnection( #"Data Source=(LocalDB)\v11.0;AttachDbFilename=C:\Users\oo\Documents\Data.mdf;Integrated Security=True;Connect Timeout=30"))
{
con.Open();
using(var cmd = con.CreateCommand())
{
cmd.CommandText = #"insert into Invoice(subtotal,tax,total) values (#subtotal,#tax,#total); select SCOPE_IDENTITY() as InvoiceID;";
cmd.Parameters.AddWithValue("#subtotal",subtotal);
cmd.Parameters.AddWithValue("#tax",tax);
cmd.Parameters.AddWithValue("#total",total);
using(var reader = cmd.ExecuteReader())
{
if(reader.Read())
invoiceID = cmd.GetInt32("InvoiceID");
}
}
foreach(var item in orderItems)
{
using(var cmd = con.CreateCommand())
{
cmd.CommandText = #"insert into InvoiceItem(InvoiceID,ItemID,quantity) values (#InvoiceID,#ItemID,#quantity);";
cmd.Parameters.AddWithValue("#InvoiceID",invoiceID);
cmd.Parameters.AddWithValue("#ItemID",item.ItemID);
cmd.Parameters.AddWithValue("#quantity",item.Quantity);
cmd.ExecuteNonQuery();
}
}
}
}
Please understand this is a rudimentary, bare-bones idea of what you need to do. I've also written it without actually checking it in an IDE, so there might be a mistake or two. Importantly, it's not compatible with your existing code. Here's what you need to do to work this in:
Create a collection of items for your order, called orderItems. Each item in this collection should be some kind of object that represents a line in your ListBox. Note that your OrderItems struct is not sufficient to represent a single item (can you tell why?). Right now you're passing things around as strings of data. You need to be working with genuine objects to get a handle on the power of OOP.
Remove the SqlConnection declaration at the top of your form. You don't want connection objects just sitting around. The using blocks ensure a limited lifetime and that the object gets closed and disposed of properly. If you're using this object elsewhere (e.g. to get a list of items to show your user), then you need to modify that code to use this pattern.
Determining a good way to get itemID, subtotal, tax and total into this method. You could pass them as parameters, or you could use objects.
There are a lot of improvements that can be made, both to the code I've posted and to what you have already. This is meant only to be enough for basic functionality. Here are things that I leave to you as an exercise, but which you should do:
Error handling
Creating a proper collection of item objects and binding it to your UI elements
Getting static data like price and itemID from item objects and not out of the UI elements (ComboBox and ListBox)
Getting more familiar with the database interaction functionality, so you can understand how it works

Listing a number twice in a list box when pulling data from a database using the and key word in the sql statment in C#

Sorry for the large heading, I don't know what is going on with my code. I am pulling all serial numbers for a given work order number and status code and populating a list box with the results. My issue is, my code is pulling the number but listing it twice in the list box control. I am using a postgres database. Here is my code.
private void Get_Serial_Numbers()
{
NpgsqlConnection conn = Connection.getConnection();
try
{
conn.Open();
NpgsqlCommand cmd = new NpgsqlCommand("select product_serial_number from master_product where product_wo_number = :WorkOrder and status = :Status;", conn);
cmd.Parameters.Add(new NpgsqlParameter("WorkOrder", IdStorage.WorkOrderNumber));
cmd.Parameters.Add(new NpgsqlParameter("Status", 128));
NpgsqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
object serialNumber = dr["product_serial_number"];
lstProductsInWO.Items.Add(serialNumber.ToString());
}
if (lstProductsInWO.Items.Count == 0)
{
lstProductsInWO.Items.Add("No Data");
lblSerialInWO.Text = "Products in WO 0";
}
else
{
ProductTotal = lstProductsInWO.Items.Count;
lblSerialInWO.Text = "Products in WO " + ProductTotal.ToString();
}
dr.Close();
cmd.Dispose();
}
There are two possilities:
You accidently call the method Get_Serial_Numbers() twice in some event handlers - check it by debugging to make sure that the code runs only once.
You get the items twice from the table. Verify what the query returns (under the debugger) or running it manually against the database.

SQL Command not filling data reader

I am working on a nested repeater. My issue seems to be at the moment, that when I execute my SQL command that no data is returned to the data reader. Even when I run the exact same query (Copy and Pasted) into SQL Server.
My noteDrClient reader does not contain data, it does however know that there are 5 columns in the table. I have no idea what to do at this point or why no data is being passed into the data reader. Can anyone see an obvious problem?
SqlConnection con = new SqlConnection("Data Source=***;Initial Catalog=*;User ID=*;Password=*;Integrated Security=False;MultipleActiveResultSets=true;");
Above is my connection string. Please notice that I have Multiple Active Result Sets set to true. I did this because I kept getting errors about my data reader being open, even though it was closed.
protected void rptList_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
RepeaterItem item = e.Item;
if ((item.ItemType == ListItemType.Item) ||
(item.ItemType == ListItemType.AlternatingItem))
{
System.Data.Common.DbDataRecord rd = (System.Data.Common.DbDataRecord)e.Item.DataItem;
Repeater nestedRepeater = e.Item.FindControl("NotesRepeater") as Repeater;
string FID = rd[0].ToString();
using (cmd2 = new SqlCommand("SELECT * FROM notes WHERE FID = 1356;", con)) ;
SqlDataReader noteDrClient = cmd2.ExecuteReader(); //no data is being filled to the data reader... even though this command pulls data in SQL Server Management Studio.
if (noteDrClient.Read()) { //bind the repeater if there is data to bind
nestedRepeater.DataSource = noteDrClient;
nestedRepeater.DataBind();
}
noteDrClient.Close();
}
You're using statement is disposing the SqlCommand before you have a chance to use it. Additionally, you're attempting to bind to a DataReader. Get the results from the data reader into a collection of "Note" entities and bind to the collection instead.
using (SqlCommand cmd2 = new SqlCommand("SELECT * FROM notes WHERE FID = 1356;", con))
{
using(SqlDataReader noteDrClient = cmd2.ExecuteReader())
{
while (noteDrClient.Read())
{
Note n = new Note();
... get note from data reader
notes.Add(n); // add note to collection
}
}
}
// bind child
nestedRepeater.DataSource = notes;
nestedRepeater.DataBind();
Edit:
You might want to look into the DataAdapter - http://www.mikesdotnetting.com/Article/57/Displaying-One-To-Many-Relationships-with-Nested-Repeaters
I solved the problem by creating an additional connection string instead of reusing the same connection string I had been using for primary repeater. The Data is still not binding, but it does exist.
using (cmd2 = new SqlCommand("SELECT * FROM notes WHERE FID = 1356;", con2)) ;
I think the semicolon in your query may cause problems.
Try using quotation marks around the value like this:
SELECT * FROM notes WHERE FID = '1356;'
If the semicolon is not part of the value:
SELECT * FROM notes WHERE FID = '1356'

The Page Page_Load Event Vs The Repeater ItemCommand

I'm running some code in Page_Load then I store the results from this code in some local variables.
Now I use this local variables to assign them to the controls inside my repeater item template
The Problem Is >> the page doesn't displays the item template but there's no data bound to it..I think the repeater can't load the data assigned to it in the Page_Load and it get's initialized -life cycle related issues-
Any idea what the problem is exactly ? and how to solve this ?
EDIT
Some example code :
public partial class MyPage: System.Web.UI.Page
{
int UserId = 0;
protected void Page_Load(object sender, EventArgs e)
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["MyDbConn"].ConnectionString);
SqlCommand comm = new SqlCommand("SELECT * From Users, conn);
SqlDataReader reader;
conn.Open();
reader = comm.ExecuteReader();
//I'm not sure if I need those two lines:
AllBrandsRptr.DataSource = reader;
AllBrandsRptr.DataBind();
while (reader.Read())
{
UserId = (int)reader["UserId"];
}
conn.Close();
}
}
protected void AllBrandsRptr_ItemCommand(object source, RepeaterCommandEventArgs e)
{
Label LabelTest = (Label)e.Item.FindControl("MyTestLabel");
LabelTest.Text = UserId.ToString();
}
EDIT 2
My Sql SELECT Statement
string command1 = "SELECT Brands.BrandId, Brands.BrandName, Brands.BrandLogo, Brands.BrandWebsite, Brands.IsBrandVisible, Cuisines.CuisineType, VenueTypes.VenueTypeName FROM Brands FULL OUTER JOIN BrandCuisines ON BrandCuisines.BrandId = Brands.BrandId FULL OUTER JOIN Cuisines ON Cuisines.CuisineId = BrandCuisines.CuisineId FULL OUTER JOIN BrandVenueTypes ON BrandVenueTypes.BrandId = Brands.BrandId FULL OUTER JOIN VenueTypes ON VenueTypes.VenueTypeId = BrandVenueTypes.VenueTypeId";
My Filtration Code
conn.Open();
reader = comm.ExecuteReader();
AllBrandsRptr.DataSource = reader;
AllBrandsRptr.DataBind();
while (reader.Read())
{
if (((int)reader["BrandId"]) == BrandId) //this line to pass collecting some info, if I already iterated through the same Id
{
BrandId = (int)reader["BrandId"];
BrandName = (string)reader["BrandName"];
BrandLogo = (string)reader["BrandLogo"];
BrandWebsite = (string)reader["BrandWebsite"];
IsVisible = (bool)reader["IsBrandVisible"];
}
if (reader["CuisineType"] != DBNull.Value)
{
Cuisines += (string)reader["CuisineType"];
}
if (reader["VenueTypeName"] != DBNull.Value)
{
VenueTypes += ", " + (string)reader["VenueTypeName"];
}
conn.Close();
My Initial Problem
How to use in my application a SELECT statement that must return more than one record to show multiple values for a certain field (m:m relation)
You shouldn't manually iterate over the DataReader at all. It is a forward-only tool. Only the Repeater or your while loop may iterate through the results. I believe your immediate problem is that your while loop is exhausting the DataReader before the Repeater renders.
When you call DataBind(), you're instructing the Repeater to render its template for every item in the collection you assigned as its DataSource. So, any filtration would need to happen before. Ideally, you should probably add that filtration logic as a where clause to your SQL statement.
Can you be more specific about the real problem you're trying to solve? It's hard to give you accurate advice otherwise.
Update:
Keep in mind that while(reader.Read()) does not work like an event handler or otherwise similar to how it might semantically sound. In other words, you aren't telling the program to do something when the DataReader is read, you're telling it to start reading through the data immediately (unrelated to the Repeater).
Here's what I suggest you try:
string sql = "Your current SQL here";
string connectionString = "Your connection string here";
// The using block ensures that the connection will be closed and freed up,
// even if an unhandled exception occurs inside the block.
using (SqlConnection conn = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand(sql, conn);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
AllBrandsRptr.DataSource = dt;
AllBrandsRptr.DataBind();
var cuisineTypes = from row in dt.AsEnumerable()
select row["CuisineType"];
string cuisines = string.Join(", ", cuisineTypes.Distinct());
var venueTypes = from row in dt.AsEnumerable()
select row["VenueTypeName"];
string venues = string.Join(", ", venueTypes.Distinct());
}
By using a DataTable instead of DataReader, you're able to iterate through the data as many times as necessary. The DataReader is more performant, if a single, forward-only read through the data is all you need, but the DataTable is much more powerful and is helpful in these situations beyond the DataReader's capabilities.
It's worth mentioning that if you care about this project in the long-term and want to make it maintainable, you should consider eventually moving some of this data access code to a separate layer. You should strive to never have SQL statements or direct data access code in your .aspx.cs files.
Then, your code in Page_Load could be strongly typed and easier to work with:
List<Brand> brands = Brand.GetAllBrands();
AllBrandsRptr.DataSource = brands;
AllBrandsRptr.DataBind();
var cuisineTypes = from brand in brands
select brand.CuisineType;
string cuisines = string.join(", ", cuisineTypes.Distinct());
var venueTypes = from brand in brands
select brand.VenueType;
string venues = string.join(", ", venueTypes.Distinct());

Categories

Resources