i have 3 classes that are connected to eachother
class shake
class VmShake , that has 2 vars 1 is a shake object and the other is int amount
class Cart that has a list of VMShake
i first delete it from the cart , afterwards delete it from the vmshake and finally delete the shake itself.
when i delete it from the site , sql deletes it from shakes table .
but in the VMshake table the row still appears with null values .
what can cause this problem and how to overcome it
here's a snippet:
ShakesAndTusafim shake = DataLayer.Data.shakesAndTusafims.ToList().Find(x => x.ID == id);
if (shake != null)
{
int tempId = shake.ID;
foreach (Cart cart in DataLayer.Data.Carts)
{
foreach (VMShakes vm in cart.shakes)
{
if (vm.shakes.ID == tempId)
{
cart.shakes.Remove(vm);
break;
}
}
}
foreach (VMShakes Shake in DataLayer.Data.VMShakes)
{
if(Shake.Id== tempId)
DataLayer.Data.VMShakes.Remove(Shake);
}
DataLayer.Data.shakesAndTusafims.Remove(shake);
}
DataLayer.Data.SaveChanges();
First you delete it from the cart and that done well ,
The issue happens when you are deleting it from the VmShake , if you look at your code :
foreach (VMShakes Shake in DataLayer.Data.VMShakes)
{
if(Shake.Id== tempId)
DataLayer.Data.VMShakes.Remove(Shake);
}
you are not looking for a shake having tempId to delete , instead you are trying to delete a vmShake that has Id equal to tempId. It will work if you change it to this :
foreach (VMShakes vmShake in DataLayer.Data.VMShakes)
{
if(vmShake.shake.Id== tempId)
DataLayer.Data.VMShakes.Remove(vmShake);
}
Related
I have a user object that contains a nested list and i need to change the value of a element in the 3rd list and return the user object.
I want to do this with linq, below is the nested loop.
foreach (User itm in user)
{
if (itm.destinations!=null)
{
foreach (Models.Api.destinations.Destination dm in itm.destinations)
{
if (dm.destinationData != null)
{
foreach (Models.Api.destinations.DestinationData destData in dm.destinationData)
{
if (destData.type == "phone" & destData.data!="")
{
//i want to update the destData.data here .. something like
destData.data ='updated data';
}
}
}
}
}
}
I want the updated data to be available in the user object
can someone help me achieve this with the LINQ
Thanks in advance
Tarak
Try this:
foreach (var x in
(from itm in user
where itm.destinations!=null
from dm in itm.destinations
where dm.destinationData != null
from destData in dm.destinationData
where destData.type == "phone" & destData.data != ""
select new { itm, dm, destData }))
{
/* put your update code here. */
}
You didn't give us what the update code should look like or even the object models for us to work from.
I am very new to MVC and hope someone can assist me.
I have a controller method to save post back data from a form. It has a field called OrderStatus. If the order status value is "Received" then only I want to execute a block of code.
What I am doing in this code is, read the post values and read the EF data again using Find and compare the values. All seems ok but when I try to save the record, it gives me below error.
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
I do understand the problem but how can I check the existing values in the database and compare and save.
My code is below
// POST: /Purchasing/Edit/5
[HttpPost]
public ActionResult Edit(PurchaseMaster purchasemaster)
{
if (ModelState.IsValid)
{
if (purchasemaster.OrderStatus == "Received")
{
string myId = purchasemaster.PurchaseId;
//check if the existing status is already set as Received or not
PurchaseMaster pm = db.PurchaseMasters.Find(myId);
if (pm.OrderStatus != "Received") //this will prevent duplicate stock updates
{
//load the items and loop through to update the stock
List<PurchaseDetail> purchasedetails = db.PurchaseDetails.Where(x => x.PurchaseId == myId).ToList();
foreach (PurchaseDetail singleitem in purchasedetails)
{
string itemcode = singleitem.ItemCode;
Item item = db.Items.Find(itemcode);
item.QtyInHand = item.QtyInHand + singleitem.Quantity;
db.Entry(item).State = EntityState.Modified;
db.SaveChanges();
}
}
}
db.Entry(purchasemaster).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(purchasemaster);
}
Try this it should work
//don't get this object from database
//PurchaseMaster pm = db.PurchaseMasters.Find(myId);
if (db.PurchaseMasters.Any(x =>x.Id == myId && x.OrderStatus != "Received") {
// Do your stuff
}
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).
As a follow-up to my earlier question, I now know that EF doesn't just save all of the changes of the entire entity for me automatically. If my entity has a List<Foo>, I need to update that list and save it. But how? I've tried a few things, but I can't get the list to save properly.
I have a many-to-many association between Application and CustomVariableGroup. An app can have one or more groups, and a group can belong to one or more apps. I believe I have this set up correctly with my Code First implementation because I see the many-to-many association table in the DB.
The bottom line is that the Application class has a List<CustomVariableGroup>. My simple case is that the app already exists, and now a user has selected a group to belong to the app. I want to save that change in the DB.
Attempt #1
this.Database.Entry(application).State = System.Data.EntityState.Modified;
this.Database.SaveChanges();
Result: Association table still has no rows.
Attempt #2
this.Database.Applications.Attach(application);
var entry = this.Database.Entry(application);
entry.CurrentValues.SetValues(application);
this.Database.SaveChanges();
Result: Association table still has no rows.
Attempt #3
CustomVariableGroup group = application.CustomVariableGroups[0];
application.CustomVariableGroups.Clear();
application.CustomVariableGroups.Add(group);
this.Database.SaveChanges();
Result: Association table still has no rows.
I've researched quite a bit, and I've tried more things than I've shown, and I simply don't know how to update an Application's list with a new CustomVariableGroup. How should it be done?
EDIT (Solution)
After hours of trial and error, this seems to be working. It appears that I need to get the objects from the DB, modify them, then save them.
public void Save(Application application)
{
Application appFromDb = this.Database.Applications.Single(
x => x.Id == application.Id);
CustomVariableGroup groupFromDb = this.Database.CustomVariableGroups.Single(
x => x.Id == 1);
appFromDb.CustomVariableGroups.Add(groupFromDb);
this.Database.SaveChanges();
}
While I consider this a bit of a hack, it works. I'm posting this in the hopes that it helps someone else save an entire day's worth of work.
public void Save(Application incomingApp)
{
if (incomingApp == null) { throw new ArgumentNullException("incomingApp"); }
int[] groupIds = GetGroupIds(incomingApp);
Application appToSave;
if (incomingApp.IdForEf == 0) // New app
{
appToSave = incomingApp;
// Clear groups, otherwise new groups will be added to the groups table.
appToSave.CustomVariableGroups.Clear();
this.Database.Applications.Add(appToSave);
}
else
{
appToSave = this.Database.Applications
.Include(x => x.CustomVariableGroups)
.Single(x => x.IdForEf == incomingApp.IdForEf);
}
AddGroupsToApp(groupIds, appToSave);
this.Database.SaveChanges();
}
private void AddGroupsToApp(int[] groupIds, Application app)
{
app.CustomVariableGroups.Clear();
List<CustomVariableGroup> groupsFromDb2 =
this.Database.CustomVariableGroups.Where(g => groupIds.Contains(g.IdForEf)).ToList();
foreach (CustomVariableGroup group in groupsFromDb2)
{
app.CustomVariableGroups.Add(group);
}
}
private static int[] GetGroupIds(Application application)
{
int[] groupIds = new int[application.CustomVariableGroups.Count];
int i = 0;
foreach (CustomVariableGroup group in application.CustomVariableGroups)
{
groupIds[i] = group.IdForEf;
i++;
}
return groupIds;
}