I am trying to do CRUD operations in c# using Linq on Products of the NWTraders Database. While Adding a new product, I am trying to display Supplier Name and Category Name instead of the Supplier ID and Category ID(which are foreign keys of the Product table).
I have tried to Add new product and it crashes soon after I press OK to save it on to the database and update my data grid. But I noticed that the new product is getting updated into the database with supplier and Category IDs as Null which is further preventing me from accessing the Product Windows form itself as it is not able to retrieve the IDs of the corresponding Supplier and Category Names that I am giving to the new product during the ADD.
cmbSupplierName.SelectedIndex seem to receiving a NULL value and this.product.Supplier.Company is throwing the Null Reference Exception. Same is the problem with category. If I handle them with the if condition, then it still throws me an exception in the below code.
private void LoadProductInformation()
{
lblProductHeader.Text = "Information about :" + this.product.ProductName;
txtProductID.Text = this.product.ProductID.ToString();
txtProductName.Text = this.product.ProductName;
// Not loading for Add Products as User has to enter the values.
if (this.Mode != ProductViewMode.Add)
{
cmbSupplierName.SelectedIndex = cmbSupplierName.FindString(this.product.Supplier.CompanyName);
cmbCategory.SelectedIndex = cmbCategory.FindString(this.product.Category.CategoryName);
txtQuantityPerUnit.Text = this.product.QuantityPerUnit;
txtUnitPrice.Text = this.product.UnitPrice.Value.ToString("C");
txtUnitsInStock.Text = this.product.UnitsInStock.Value.ToString();
txtUnitsOnOrder.Text = this.product.UnitsOnOrder.Value.ToString();
txtReorderLevel.Text = this.product.ReorderLevel.Value.ToString();
chkDiscontinued.Checked = (this.product.Discontinued == true);
}
}
public void LoadDGVProducts(IEnumerable<Product> products)
{
// If there are no products, do nothing and return from the function.
if (products == null) return;
FetchData(); //fetching all the serach parameters
this.dgvProducts.SelectionChanged -= new System.EventHandler(this.DGVProducts_SelectionChanged);
if (dgvProducts.RowCount == 0)
FormatDGVProducts();
dgvProducts.Rows.Clear();
// Go through every product in the product collection and
// add it as a row in the dgv
foreach (Product prod in products)
{
dgvProducts.Rows.Add(
prod.ProductID, // The ID will not actually be shown since it is given to a column that has the Visible property set to False.
prod.ProductName,
prod.Supplier.CompanyName,
prod.Category.CategoryName,
prod.QuantityPerUnit,
prod.UnitPrice.Value.ToString("C"),
prod.UnitsInStock,
prod.UnitsOnOrder,
prod.ReorderLevel,
prod.Discontinued
);
...........................
}
}
Due to the supplier and category IDs receiving Null values on database, it is throwing me the exceptions at the 'foreach' as it won't let product to display on data grid if even one of the values in null in that condition.
I don't know where I am supposed to connect Supplier ID to Name for it to not receive Null values on the database.
Related
So I have the relation in the database as follows:
The idea is to add multiple product categories into the list before inserting it into the database.
I'm using ADO.NET framework, and the mapped class products contains list of product categories. So when the user fills out the form that is needed for product category I say:
Products p = new Products(... initializing);
ProductCategories p2 = new ProductCategories(...initializing);
p.ProductCategories.Add(p2);
What I don't understand is how to add more of these ProductCategories items into this list before I insert them into the database. Whenever I add new one, the old one seems to be deleted from the list, and I don't understand why? The order should be like this:
1st product category item
2nd
3rd...
(If the user stops adding them....) -> click save button -> insert all product categories from the list into product categories table...
Instead of that I just get overwritten old product category with new values that I just inserted. Can someone help me out with this??
EDIT: here is the entire code responsible for adding multiple items to the list
if (FileUpload1.PostedFile != null && FileUpload1.PostedFile.ContentLength > 0)
{
p = new Product();
// p is declared as property outside of all methods...
p.ProductName = txtProductTitle.Text;
p.Dimensions = ".";
p.PricePerUnit = float.Parse(txtProductPrice.Text);
System.Drawing.Image img = System.Drawing.Image.FromStream(FileUpload1.PostedFile.InputStream);
MemoryStream mem = new MemoryStream();
img.Save(mem, System.Drawing.Imaging.ImageFormat.Jpeg);
p.ProductPicture = mem.ToArray();
p.ProductPicThumb = mem.ToArray();
p.MeasurementUnitID = int.Parse(dropMeasure.SelectedValue);
p.PricePerFeet = 1.2F;
int pid = ServiceClass.InsertProduct(p);
ProductCategories p2 = new ProductCategories(...initializing);
p.ProductCategories.Add(p2);
Response.Write("<script>alert('Product successfully added!');</script>");
}
else
{
Response.Write("<script>alert('Something went wrong!');</script>");
}
I am looking to find the best way to cache the DB Lookup Tables which consists of about 75 table.
I want to cache these tables data to use them in my application so I won't open a connection with the DB each time I need them.
Here is what I am doing:
I have created a static class which contains static properties to each lookup table called MyApplicationCache.
In each property getter I am filling it with the intended data from DB.
I'm caching the data using HttpRuntime.Cache["PropertyName"]
Each time i GET this lookup table data I check if the HttpRuntime.Cache["PropertyName"] != null
If yes then I am getting it from cache else I am getting it from DB
Finally, I am invoking all properties at application start event in global.asax
Until now everything is good, but recently I've faced a performance issue and I can't solve it. If I wanted the cache object (Payer) to be updated from DB I am doing this:
MyApplicationCache.Payer = null;
This sets HttpRuntime.Cache["Payer"] = null so if I requested it again it reloads it from the DB.
list<payer> payerList = MyApplicationCache.Payer;
Now the Performance problem raises:
PayerList in DB are about 1700 record.
Each payer object has a List property called PayerBranches which requires looping on all PayerList List and getting PayerBranches for each PayerList item.
// MyApplicationCache Payer Property:
public static List<LDM.DataEntityTier.Payer> Payer {
get {
if (HttpRuntime.Cache["Payer"] != null)
return (List<LDM.DataEntityTier.Payer>)HttpRuntime.Cache["Payer"];
// request item from its original source
using (LDM.DataAccess.OracleManager OracleManager = new LDM.DataAccess.OracleManager()) {
OracleManager.OpenConnection();
List<LDM.DataEntityTier.Payer> result = new LDM.DataService.PayerService().GetPayersListWithFullName(3, OracleManager, "UTC");
//List<LDM.DataEntityTier.Payer> result = new LDM.DataService.PayerService().GetListOfPayer("Order by Name asc", OracleManager ,"UTC");
List<PayerBranches> payerBranchesList = new LDM.DataService.PayerBranchesService().GetListOfObject(OracleManager, "UTC");
OracleManager.CloseConnection();
foreach (Payer payerItem in result) {
payerItem.PayerBranches = new List<PayerBranches>();
foreach (PayerBranches item in payerBranchesList.FindAll(x => x.PayerID == payerItem.Id)) {
payerItem.PayerBranches.Add(item);
}
}
// add item to cache
HttpRuntime.Cache["Payer"] = result;
return result;
}
}
set {
if (value == null) {
HttpRuntime.Cache.Remove("Payer");
}
}
}
This problem occurs with each property that has a list in it
I don't know if there is a better way to cache data or if there is a problem in my code.
Is there is a better way to do caching?
I am trying to write a program to scan a directory containing tv show folders, look up some details about the shows using tvrage API and then save the details to a database using entity framework.
My TVShow table pkey is the same value as taken from the tvrage database show id, and I am having issues when duplicate or similar folder names are returning the same Show info. In a situation where I have a directory containing three folders, "Alias", "Alias 1" , "Band of Brothers" I get the following output from my code
* TV SHOWS *
Alias....... NO MATCH......ADDING........DONE
Alias 1 ...... NO MATCH.....ADDING....CANT ADD, ID ALREADY EXISTS IN DB
Band of Brothers ...... NO MATCH..ADDING....
Before getting an UpdateException on the context.SaveChanges(); line
Violation of PRIMARY KEY constraint 'PK_TVShows'.
I can see using SQL profiler that the problem is that my app is trying to perform an insert on the alias show for a second time with duplicate key, but I can't see why. When I step through the code on the second interaction of the foreach loop (second "alias" folder), the code to save the show entity to the database is bypassed.
It is only on the next iteration of the foreach loop when I have created a new TVShow entity for "Band of Brothers" do I
actually reach the code which adds a Tvshow to context and saves, at which point the app crashes. In visual studio I can see
at the point of the crash that;
"show" entity in context.TVShows.AddObject(show) is "Band of Brothers" w/ a unique ID
context.TVShows only contains one record, the first Alias Entity
But SQL profiler shows that EntityFramework is instead inserting Alias for a second time, and I am stumped by why this is
private void ScanForTVShowFolders( GenreDirectoryInfo drive ) {
IEnumerable<DirectoryInfo> shows = drive.DirInfo.EnumerateDirectories();
foreach (DirectoryInfo d in shows) {
//showList contains a list of existing TV show names previously queried out of DB
if (showList.Contains(d.Name)) {
System.Console.WriteLine(d.Name + ".....MATCH");
} else {
System.Console.Write(d.Name + "......NO MATCH..ADDING....");
TVShow show = LookUpShowOnline(d.Name, drive.GenreName);
if (show.Id == -1) { // id of -1 means online search failed
System.Console.Write("..........CANT FIND SHOW" + Environment.NewLine);
} else if (context.TVShows.Any(a => a.Id == show.Id)) { //catch duplicate primary key insert
System.Console.Write(".......CANT ADD, ID ALREADY EXISTS IN DB" + Environment.NewLine);
} else {
context.TVShows.AddObject(show);
context.SaveChanges();
System.Console.Write("....DONE" + Environment.NewLine);
}
}
}
private TVShow LookUpShowOnline( string name, string genre ) {
string xmlPath = String.Format("http://services.tvrage.com/feeds/search.php?show='{0}'", name);
TVShow aShow = new TVShow();
aShow.Id = -1; // -1 = Can't find
XmlDocument xmlResp = new XmlDocument();
try { xmlResp.Load(xmlPath); } catch (WebException e) { System.Console.WriteLine(e); }
XmlNode root = xmlResp.FirstChild;
if (root.NodeType == XmlNodeType.XmlDeclaration) { root = root.NextSibling; }
XmlNode tvShowXML;
//if (showXML["episode"] == null)
// return false;
tvShowXML = root["show"];
if (tvShowXML != null) {
aShow.Id = System.Convert.ToInt16(tvShowXML["showid"].InnerText);
aShow.Name = tvShowXML["name"].InnerText.Trim();
aShow.StartYear = tvShowXML["started"].InnerText.Trim();
aShow.Status = tvShowXML["status"].InnerText.Trim();
aShow.TVGenre = context.TVGenres.Where(b => b.Name.Trim() == genre).Single();
}
return aShow;
}
}
Edit
Doing some more reading I added context.ObjectStateManager to my debug watchlist and I can see everytime I create a new TVShow entity a new record is added to _addedEntityStore. Actually if I remove context.TVShows.AddObject(show) the code still updates the database so manually adding to the context seems redundant.
If your are inserting object by foreach loop > better to keep the Primary Key outside and make it increment!
eg: int newID= Shows.Select(d=>d.Id).Max();
foreach(............)
{
show.Id = newID++;
.
.
. //remaining fields
.
context.TVShows.AddObject(show);
}
context.SaveChanges();
it works for me...!!
Turns out context.TVShows.AddObject(show) is unnecessary in my case, I was inadvertently adding all created show entities to the context when this query runs
aShow.TVGenre = context.TVGenres.Where(b => b.Name.Trim() == genre).Single();
This is not what I wanted, I just wanted to create the object, then decide whether to add it. Will be pretty easy to fix now I know why it's happening.
I'm building a web-based store application, and I have to deal with many nested subcategories within each other. The point is, I have no idea whether my script will handle thousands (the new system will replace the old one, so I know what traffic I have to expect) - at the present day, respond lag from the local server is 1-2 seconds more than other pages with added about 30 products in different categories.
My code is the following:
BazaArkadiaDataContext db = new BazaArkadiaDataContext();
List<A_Kategorie> Podkategorie = new List<A_Kategorie>();
public int IdKat { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
List<A_Produkty> Produkty = new List<A_Produkty>(); //list of all products within the category and remaining subcategories
if (Page.RouteData.Values["IdKategorii"] != null)
{
string tmpkat = Page.RouteData.Values["IdKategorii"].ToString();
int index = tmpkat.IndexOf("-");
if (index > 0)
tmpkat = tmpkat.Substring(0, index);
IdKat = db.A_Kategories.Where(k => k.ID == Convert.ToInt32(tmpkat)).Select(k => k.IDAllegro).FirstOrDefault();
}
else
return;
PobierzPodkategorie(IdKat);
foreach (var item in Podkategorie)
{
var x = db.A_Produkties.Where(k => k.IDKategorii == item.ID);
foreach (var itemm in x)
{
Produkty.Add(itemm);
}
}
//data binding here
}
}
List<A_Kategorie> PobierzPodkategorie(int IdKat, List<A_Kategorie> kat = null)
{
List<A_Kategorie> Kategorie = new List<A_Kategorie>();
if (kat != null)
Kategorie.Concat(kat);
Kategorie = db.A_Kategories.Where(k => k.KatNadrzedna == IdKat).ToList();
if (Kategorie.Count() > 0)
{
foreach (var item in Kategorie)
{
PobierzPodkategorie(item.IDAllegro, Kategorie);
Podkategorie.Add(item);
}
}
return Kategorie;
}
TMC;DR*
My function PobierzPodkategorie recursively seeks through subcategories (subcategory got KatNadrzedna column for its parent category, which is placed in IDAllegro), selects all the products with the subcategory ID and adds it to the Produkty list. The database structure is pretty wicked, as the category list is downloaded from another shop service server and it needed to get our own ID column in case the foreign server would change the structure.
There are more than 30 000 entries in the category list, some of them will have 5 or more parents, and the website will show only main categories and subcategories ("lower" subcategories are needed by external shop connected with SOAP).
My question is
Will adding index table to the database (Category 123 is parent for 1234, 12738...) will improve the performance, or is it just waste of time? (The index should be updated when version of API changes and I have no idea how often would it be) Or is there other way to do it?
I'm asking because changing the script will not be possible in production, and I don't know how the db engine handles lots of requests - I'd really appreciate any help with this.
The database is MSSQL
*Too much code; didn't read
The big efficiency gain you can get is to load all subproducts in a single query. The time saved by reducing network trips can be huge. If 1 is a root category and 12 a child category, you can query all root categories and their children like:
select *
from Categories
where len(Category) <= 2
An index on Category would not help with the above query. But it's good practice to have a primary key on any table. So I'd make Category the primary key. A primary key is unique, preventing duplicates, and it is indexed automatically.
Moving away from RBAR (row by agonizing row) has more effect than proper tuning of the database. So I'd tackle that first.
You definitely should move the recursion into database. It can be done using WITH statement and Common Table Expressions. Then create a view or stored procedure and map it to you application.
With that you should be able to reduce SQL queries to two (or even one).
I need to know the best practice of creating an entity object and assigning the foreign key. Here is my scenario. I have a Product table with pid,name,unit_price etc.. I also have a Rating table with pid (foregin key),rate,votes etc... Currently i am doing the following to create the rating object:
var prod = entities.Product.First(p => p.product_id == pid);
prod.Rating.Load();
if (prod.Rating != null)
{
log.Info("Rating already exists!");
// set values and Calcuate the score
}
else
{
log.Info("New Rating!!!");
Rating rating = new Rating();
// set values and do inital calculation
prod.Rating = rating;
} entities.SaveChanges();
Even though this works fine, I would like to know the best practice in doing these kind of assignment.
Whenever you are always going to load an entity, just do one round trip, and include it in the query that will get generated by EF.
Product prod = entities
.Product.Include("Rating")
.First(p => p.product_id == pid);
if (prod.Rating != null)
{
log.Info("Rating already exists!");
// set values and Calcuate the score
}
else
{
log.Info("New Rating!!!");
Rating rating = new Rating();
// set values and do inital calculation
prod.Rating = rating;
}
entities.SaveChanges();