Save order data to local database - c#

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

Related

Update or delete current row of database results

I'm trying to port some old VB6 code to C# and .NET.
There are a number of places where the old code uses a RecordSet to execute a SQL query and then loop through the results. No problem so far, but inside the loop the code makes changes to the current row, updating columns and even deleting the current row altogether.
In .NET, I can easily use a SqlDataReader to loop through SQL query results, but updates are not supported.
So I've been playing with using a SqlDataAdapter to populate a DataSet, and then loop through the rows in a DataSet table. But the DataSet doesn't seem very smart compared to the VB6's old RecordSet. For one thing, I need to provide update queries for each type of edit I have. Another concern is that a DataSet seems to hold everything in memory at once, which might be a problem if there are many results.
What is the best way to duplicate this behavior in .NET? The code below shows what I have so far. Is this the best approach, or is there another option?
using (SqlConnection connection = new SqlConnection(connectionString))
{
DataSet dataset = new DataSet();
using (SqlDataAdapter adapter = new SqlDataAdapter(new SqlCommand(query, connection)))
{
adapter.Fill(dataset);
DataTable table = dataset.Tables[0];
foreach (DataRow row in table.Rows)
{
if ((int)row["Id"] == 4)
{
if ((int)row["Value1"] > 0)
row["Value2"] = 12345;
else
row["Value3"] = 12345;
}
else if ((int)row["Id"] == 5)
{
row.Delete();
}
}
// TODO:
adapter.UpdateCommand = new SqlCommand("?", connection);
adapter.DeleteCommand = new SqlCommand("?", connection);
adapter.Update(table);
}
}
Note: I'm new to the company and can't very well tell them they have to change their connection strings or must switch to Entity Framework, which would be my choice. I'm really looking for a code-only solution.
ADO.NET DataTable and DataAdapter provide the closest equivalent of ADO Recordset with applies separation of concens principle. DataTable contains the data and provides the change tracking information (similar to EF internal entity tracking) while DataAdapter provides a standard way to populate it from database (Fill method) and apply changes back to the database (Update method).
With that being said, what are you doing is the intended way to port the ADO Recordset to ADO.NET. The only thing you've missed is that you are not always required to specify Insert, Update and Delete commands. As soon as your query is querying a single table (which I think was a requirement to get updateable Recordset anyway), you can use another ADO.NET player called DbCommandBuilder:
Automatically generates single-table commands used to reconcile changes made to a DataSet with the associated database.
Every database provider provides implementation of this abstract class. The MSDN example for SqlCommandBuilder is almost identical to your sample, so all you need before calling Update is (a bit counterintuitive):
var builder = new SqlCommandBuilder(adapter);
and that's it.
Behind the scenes,
The DbCommandBuilder registers itself as a listener for RowUpdating events that are generated by the DbDataAdapter specified in this property.
and dynamically generates the commands if they are not specifically set in the data adapter by you.
I came up with an (untested) solution for a data table.
It does require you to do some work, but it should generate update and delete commands for each row you change or delete automatically, by hooking up to the RowChanged and RowDeleted events of the DataTable.
Each row will get it's own command, equivalent to ADODB.RecordSet update / delete methods.
However, unlike the ADODB.RecordSet methods, this class will not change the underling database, but only create the SqlCommands to do it. Of course, you can change it to simply execute them on once they are created, but as I said, I didn't test it so I'll leave that up to you if you want to do it. However, please note I'm not sure how the RowChanged event will behave for multiple changes to the same row. Worst case it will be fired for each change in the row.
The class constructor takes three arguments:
The instance of the DataTable class you are working with.
A Dictionary<string, SqlDbType> that provides mapping between column names and SqlDataTypes
An optional string to represent table name. If omitted, the TableName property of the DataTable will be used.
Once you have the mapping dictionary, all you have to do is instantiate the CommandGenerator class and iterate the rows in the data table just like in the question. From that point forward everything is automated.
Once you completed your iteration, all you have to do is get the sql commands from the Commands property, and run them.
public class CommandGenerator
{
private Dictionary<string, SqlDbType> _columnToDbType;
private string _tableName;
private List<SqlCommand> _commands;
public CommandGenerator(DataTable table, Dictionary<string, SqlDbType> columnToDbType, string tableName = null)
{
_commands = new List<SqlCommand>();
_columnToDbType = columnToDbType;
_tableName = (string.IsNullOrEmpty(tableName)) ? tableName : table.TableName;
table.RowDeleted += table_RowDeleted;
table.RowChanged += table_RowChanged;
}
public IEnumerable<SqlCommand> Commands { get { return _commands; } }
private void table_RowChanged(object sender, DataRowChangeEventArgs e)
{
_commands.Add(GenerateDelete(e.Row));
}
private void table_RowDeleted(object sender, DataRowChangeEventArgs e)
{
_commands.Add(GenerateDelete(e.Row));
}
private SqlCommand GenerateUpdate(DataRow row)
{
var table = row.Table;
var cmd = new SqlCommand();
var sb = new StringBuilder();
sb.Append("UPDATE ").Append(_tableName).Append(" SET ");
var valueColumns = table.Columns.OfType<DataColumn>().Where(c => !table.PrimaryKey.Contains(c));
AppendColumns(cmd, sb, valueColumns, row);
sb.Append(" WHERE ");
AppendColumns(cmd, sb, table.PrimaryKey, row);
cmd.CommandText = sb.ToString();
return cmd;
}
private SqlCommand GenerateDelete(DataRow row)
{
var table = row.Table;
var cmd = new SqlCommand();
var sb = new StringBuilder();
sb.Append("DELETE FROM ").Append(_tableName).Append(" WHERE ");
AppendColumns(cmd, sb, table.PrimaryKey, row);
cmd.CommandText = sb.ToString();
return cmd;
}
private void AppendColumns(SqlCommand cmd, StringBuilder sb, IEnumerable<DataColumn> columns, DataRow row)
{
foreach (var column in columns)
{
sb.Append(column.ColumnName).Append(" = #").AppendLine(column.ColumnName);
cmd.Parameters.Add("#" + column.ColumnName, _columnToDbType[column.ColumnName]).Value = row[column];
}
}
}
As I wrote, this is completely untested, but I think it should be enough to at least show the general idea.
Your constraints:
Not using Entity Framework
DataSet seems to hold everything in memory at once, which might be a
problem if there are many results.
a code-only solution ( no external libraries)
Plus
The maximum number of rows that a DataTable can store is 16,777,216
row MSDN
To get high performance
//the main class to update/delete sql batches without using DataSet/DataTable.
public class SqlBatchUpdate
{
string ConnectionString { get; set; }
public SqlBatchUpdate(string connstring)
{
ConnectionString = connstring;
}
public int RunSql(string sql)
{
using (SqlConnection con = new SqlConnection(ConnectionString))
using (SqlCommand cmd = new SqlCommand(sql, con))
{
cmd.CommandType = CommandType.Text;
con.Open();
int rowsAffected = cmd.ExecuteNonQuery();
return rowsAffected;
}
}
}
//------------------------
// using the class to run a predefined patches
public class SqlBatchUpdateDemo
{
private string connstring = "myconnstring";
//run batches in sequence
public void RunBatchesInSequence()
{
var sqlBatchUpdate = new SqlBatchUpdate(connstring);
//batch1
var sql1 = #"update mytable set value2 =1234 where id =4 and Value1>0;";
var nrows = sqlBatchUpdate.RunSql(sql1);
Console.WriteLine("batch1: {0}", nrows);
//batch2
var sql2 = #"update mytable set value3 =1234 where id =4 and Value1 =0";
nrows = sqlBatchUpdate.RunSql(sql2);
Console.WriteLine("batch2: {0}", nrows);
//batch3
var sql3 = #"delete from mytable where id =5;";
nrows = sqlBatchUpdate.RunSql(sql3);
Console.WriteLine("batch3: {0}", nrows);
}
// Alternative: you can run all batches as one
public void RunAllBatches()
{
var sqlBatchUpdate = new SqlBatchUpdate(connstring );
StringBuilder sb = new StringBuilder();
var sql1 = #"update mytable set value2 =1234 where id =4 and Value1>0;";
sb.AppendLine(sql1);
//batch2
var sql2 = #"update mytable set value3 =1234 where id =4 and Value1 =0";
sb.AppendLine(sql2);
//batch3
var sql3 = #"delete from mytable where id =5;";
sb.AppendLine(sql3);
//run all batches
var nrows = c.RunSql(sb.ToString());
Console.WriteLine("all patches: {0}", nrows);
}
}
I simulated that solution and it's working fine with a high performance because all updates /delete run as batch.

Save listbox items in database after ordering

I have an asp.net page with two listboxes and a save button. The user needs to transfer items from listbox1 to listbox2 according to his order I mean that user can choose witch item come in the first place and which come next.
After that my system suggest the results (most commonly) that achieve the ordered items, I hope you understand
I did the transfer method between the two listboxes and it work good. But I'm stuck in the save method, it can save the items after being in listbox2 but not in the same order of transferring (user order).
Here is my code for save button:
protected void Button1_Click(object sender, EventArgs e)
{
string strConnString = ConfigurationManager.ConnectionStrings["ConnectionString2"].ConnectionString;
string str;
OracleCommand com;
OracleConnection con = new OracleConnection(strConnString);
try
{
con.Open();
for (int i = 0; i < ListBox2.Items.Count; i++)
{
str = "insert into Criteria values('" + ListBox2.Items[i].ToString() + "')";
com = new OracleCommand(str, con);
com.ExecuteNonQuery();
}
LabOut.Visible = true;
LabOut.Text = "Upload succesful!";
con.Close();
//Response.Write("Inserted");
}
catch (Exception)
{
LabOut.Visible = true;
LabOut.Text = "Upload failed!";
}
finally
{
con.Close();
con.Dispose();
}
}
The problem here is that database tables do not inherently have a concept of order. Retrieving rows from a table never guarantees an order unless you use a order by clause.
You should add an additional column to your table (named, for example, "Sequence" or "Order"). It can be an int or some other sortable value. You would then need to modify your insert statement:
str = "insert into Criteria(ItemValue, Sequence) values('" + ListBox2.Items[i].ToString() + "'," + i.ToString() + ")";
and retrieve the list using
str = "Select ItemValue from Criteria order by Sequence"
#fifo follow me
1st add a column in your database say it is SeqNo.
Then save your data with that. I mean generate SeqNo. for each listbox2 item and save it.
When you will fetch data from database into lintbox2 then use sql/linq ORDER BY on SeqNo. column and display them on which Order that you were saved. Simple.. :)

Inserting string data into database in c#

I am working on sharp nlp where i am extracting all the adjective now i need to store this in database and i have successfully added this to database but the problem is with me that i want to store adjective separately to database how can i store the adjective separately or for example we have string and we want to store each word separately into database and we have only one column how can we do this? .please help me out
here is my code.
private void button2_Click(object sender, EventArgs e)
{
try
{
string cleaned = richTextBox1.Text.Trim();
string st = "INSERT INTO TABLE1(adjective)VALUES('" + cleaned + "')";
SqlConnection con = new SqlConnection("Data Source=ZAZIKHAN\\SQLEXPRESS;Initial Catalog=mis;Integrated Security=True");
con.Open();
SqlCommand cmd = new SqlCommand(st, con);
if (cmd.ExecuteNonQuery() == 1)
{
MessageBox.Show(" succesfully added");
}
else
{
MessageBox.Show("Sorry we couldnt add the Values Please try Again");
}
con.Close();
}
catch (Exception ex)
{
MessageBox.Show("" + ex);
}
}
now i have this data to be stored which is in richtextbox.
local/JJ daily/JJ n/price/rate/JJ human/JJ possible/JJ correct/JJ exact/JJ local/JJ
local/JJ daily/JJ n/price/rate/JJ human/JJ possible/JJ correct/JJ exact/JJ local/JJ
dry/JJ nd/JJ
new/JJ full/JJ OK/JJ final/JJ white/JJ OK/JJ howajaa/JJ smajder/JJR agaa/JJ nasambd/JJ Live/JJ
final/JJ
great/JJ s3/JJ final/JJ
resistant/JJ Z1/JJ white/JJ new/JJ black/JJ amaa.Kintact/JJ possible/JJ main/JJ mobile/JJ rapak/JJ mil/JJ
important/JJ mil/JJ smart/JJ
35-h/JJ OK/JJ full/JJ
Want/JJ complete/JJ white/JJ same/JJ
available/JJ perfect/JJ
interested/JJ
First off, the lines
string cleaned = richTextBox1.Text.Trim();
string st = "INSERT INTO TABLE1(adjective)VALUES('" + cleaned + "')";
create a massive security hole known as SQL Injection.
In order to store the adjectives separately in a properly denormalized database, you would have a parent table where e.g. the original sentence is stored, and a child table with a 1:N relationship to the parent where you store one row per adjective plus the appropriate ID of the parent row.
Since you only have one column available, you can use any convenient format to store the array of adjectives in a single column. You could serialize that array (to Binary, XML, JSON, etc) and store it, or since you know you have a limited input character set, you could even store it as a comma separated list.
You can prefix your words with some characters to indicate whether they are verb , noun , adjective and tehn insert those value in database
eg
N_JonSkeet - Noun
V_Solve - Verb
A_Highest - Adjective
string cleaned = extractAdjective(richTextBox1.Text.Trim());
string st = "INSERT INTO TABLE1(word) VALUES(#Word)";
SqlConnection con = new SqlConnection("Data Source=ZAZIKHAN\\SQLEXPRESS;Initial Catalog=mis;Integrated Security=True");
con.Open();
SqlCommand cmd = new SqlCommand(st, con);
SqlParameter param = new SqlParameter("#Word", SqlDbType.NChar);
param.Value = "A_"+cleaned;
cmd.Parameters.Add(param);
I would separate the string into a list and then iterate over the list and insert into your DB:
var vals = "local/JJ daily/JJ n/price/rate/JJ human/JJ possible/JJ...";
var results = vals.Replace(" ", "")
.Replace("/JJ", "|")
.Replace("/", "|")
.Split('|')
.Distinct();
while(var result in results)
{
// DO insert here
}

Populating a dropdown list from database

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.

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