Why can't I add an object to the database? (mvc3) - c#

It's quite a simple question: When someone click on "Edit Plan" in my ASP.NET MVC project - He doesn't edit but create a new plan with. You can see it more clearly in my answer to my qeustion here: How do I duplicate an object?
Now I want to do the same thing to its references, and I did it like that:
var featurePlans = db.FeaturePlanBaseSet.Where(f => f.PlanId == plan.Id).ToList();
db.PlanSet.AddObject(plan);
db.SaveChanges();
for (var i = 0; i < featurePlans.Count(); i++ )
{
featurePlans[i].Plan = plan;
db.FeaturePlanBaseSet.AddObject(featurePlans[i]);
}
Plan is added when I do AddObject, but Feature isn't.
I get this error:
An object with the same key already exists in the ObjectStateManager.
The existing object is in the Unchanged state.
I'll be glad to know why does it happens.

It does not appear that you save after adding FeaturePlanBaseSet. You need to call db.SaveChanges() last to save all changes.
EDIT: It also appears that you are reading an existing FeaturePlanBaseSet record from the database and then adding that record back. The next line will retrieve an existing record.
var featurePlans = db.FeaturePlanBaseSet.Where(f => f.PlanId == plan.Id).ToList();
When you add featurePlans[i], you are adding the existing record. If you plan to add a new record, Do it as follows:
for (var i = 0; i < featurePlans.Count(); i++ )
{
var featurePlan = new FeaturePlanBaseSet();
featurePlan.Plan = plan;
...set other properties
db.FeaturePlanBaseSet.AddObject(featurePlan);
}

Related

How to put a value from array to another list in c#

Can anyone help me, guys?
this foreach is always error Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
even though I have given index 0, what's the solution
var store = _cartCollection.Find(x => x.id_cart == entity.id_cart).ToList();
int index = -1;
foreach (var cart in store)
{
var b = index++; // it shows
entity.cart[0].nama_produk = cart.nama_produk;
entity.cart[0].jumlah = cart.jumlah;
entity.cart[0].harga = cart.harga;
entity.cart[0].subtotal = cart.subtotal;
entity.cart[0].notes = cart.notes;
}
here is the class object
enter image description here
I think I declared the entity cart array / list incorrectly, do you know the correct method?
please dont be mean im a newbie
I am not sure what your exact issue is here, but I see a few "code smells" that might be causing the issue:
In this code block:
var store = _cartCollection.Find(x => x.id_cart == entity.id_cart).ToList();
int index = -1;
foreach (var cart in store)
It seems that you are retrieving a single cart based on its ID and then doing something with it. If I have that right, you can accomplish this much more simply with something like this:
var cart = _cartCollection.Find(x => x.id_cart == entity.id_cart).FirstOrDefault();
Then you don't need the for loop at all... just use the one cart you found.
On that note, you should also probably check cart for null... in case it doesn't exist... but that is up to your specific use case.
index is being incremented for each step in the loop... but its value is never used. Unless you have a reason to use it, I recommend just deleting it entirely... unless you intended to retrieve multiple carts and add them all to the entity.cart collection.
In this code block (which is what is actually throwing the error:
entity.cart[0].nama_produk = cart.nama_produk;
entity.cart[0].jumlah = cart.jumlah;
entity.cart[0].harga = cart.harga;
entity.cart[0].subtotal = cart.subtotal;
entity.cart[0].notes = cart.notes;
The code is not clear. My best guess at what you are trying to do is to add the details of the cart you found to a collection of cart information on the entity object. I also assume that entity.cart is a List or something like that.
If my assumptions are correct, then the reason you are getting the reason you are getting that exception is the cart[0] doesn't exist until you add a cart object to that list (which is probably what you are trying to do here. A more readable (and less error prone) way to do this would be:
var entityCart = new Cart {
nama_produk = cart.nama_produk,
jumlah = cart.jumlah,
harga = cart.harga,
subtotal = cart.subtotal,
notes = cart.notes
};
entity.cart.Add(entityCart);
It could be that you are trying to add products to a cart... in that case, you should change your variable names a bit, but the rest is very similar.

Updating entry adds a new entry to database

I am making a POST call to Add a flower to the database, but I want to update a record in another table if the if statement is true. If the statement is not true, it adds the flower without any problems but when I am trying to update PinkRoseId with a new id, it creates a new record instead of updating the old one.
Is there another way to update the database?
public void AddFlower(Flower flowerToAdd, int someId)
{
_flowerContext = _contextUtility.GetFlowerContext(flowerToAdd.FlowerName);
var pinkRoseId = _flowerService.GetFlowerIdByName(flowerToAdd.FlowerName, "PinkRose", "Rose");
if (flowerToAdd.SomeFlowerId.HasValue)
{
var flowerToUpdate = _updateService.UpdateFlowerBySomething(flowerToAdd.FlowerName, flowerToAdd.SomeFlowerId.Value, flowerToAdd.PetalAk);
flowerToUpdate.PinkRoseId = pinkRoseId;
_flowerContext.SaveChanges();
}
var firstId = _petalService.GetFlowerIdByName(flowerToAdd.FlowerName, "rose", "petal");
var secondId = _sunService.GetSunIdByTypeOf(flowerToAdd.FlowerName, flowerToAdd.SomeName, "sun");
flowerToAdd.FlowerId = secondId;
flowerToAdd.SomeOtherId = firstId;
flowerToAdd.CreatedDate = DateTime.UtcNow;
flowerToAdd.CreatedByUserID = someId;
_flowerContext.Flowers.Add(flowerToAdd);
_flowerContext.SaveChanges();
}
On reading more about Entity Framework, there is method AddOrUpdate which adds a new record or updates any existing records. Side note: I am using EF6 (not sure if AddOrUpdate is available in any older versions or not)
Solution 1
if (flowerToAdd.SomeFlowerId.HasValue)
{
var flowerToUpdate = _updateService.UpdateFlowerBySomething(flowerToAdd.FlowerName, flowerToAdd.SomeFlowerId.Value, flowerToAdd.PetalAk);
}
_flowerContext.Flower.AddOrUpdate(flowerToUpdate); -- this did the trick and updated the existing record instead of adding a new one
Using this method is highly discouraged as per my understanding as it can add new records. Yet to find another solution.
Solution 2
I figured out another way to get this done without using AddOrUpdate
var changeRequest = (from x in _flowerContext.FlowerUpdateSomething
where x.FlowerPrimaryId == flowerToUpdate.FlowerPrimaryId
select x).FirstOrDefault();
changeRequest.PinkRoseId = pinkRoseId;
_flowerContext.SaveChanges();
}
Update on Solution 2:
I copied the solution to another service (eg SunFlowerService) like the following:
public SomeDataBaseEntity UpdateFlower(int id)
{
var changeRequest = (from x in _flowerContext.FlowerUpdateSomething
where x.FlowerPrimaryId == id
select x).FirstOrDefault();
}
But now when I call this method like the following:
if (flowerToAdd.SomeFlowerId.HasValue)
{
var flowerToUpdate = _updateService.UpdateFlowerBySomething(flowerToAdd.FlowerName, flowerToAdd.SomeFlowerId.Value, flowerToAdd.PetalAk);
var update = _someOtherService.UpdateFlower(flowerToUpdate.Id);
update.PinkFlowerId = pinkFlowerId;
_flowerContext.SaveChanges();
}
It does not update the record in the database. I am not sure how to solve this problem, or why it would have such behavior. But on keeping the original LINQ query like the following:
var changeRequest = (from x in _flowerContext.FlowerUpdateSomething
where x.FlowerPrimaryId == flowerToUpdate.FlowerPrimaryId
select x).FirstOrDefault();
changeRequest.PinkRoseId = pinkRoseId;
_flowerContext.SaveChanges();
I am able to change the records in the database. Not sure why making another method doesn't work.

Efficient way to "apply to all" with EF

I have a search page which allows users to search for an item and add details to an item. I am working on allowing users to apply the same details to several items. The user will check all the items he would like to apply the same changes to and click a Comment button. This will send a list of items checked List<int> ids and the particular item clicked (to use as template in case it already exists) int id.
From here the user will edit the comment however he wishes and save. How will the HttpPost Edit action look? It is required to add a comment to the database if it doesn't exist for that item already, otherwise, overwrite what exists. Below is what I have come up with (basic outline). The problem I see with this is that it requires poking the database for every item I want the changes applied to. There has to be a better way to do this.
[HttpPost]
public ActionResult Edit(CommentVM model)
{
if (ModelState.IsValid)
{
foreach(int i in model.ids)
{
Comment comment = _db.Comments.Find(i);
if(comment == null){
//Create and add
{
else{
comment.Text = model.Text;
_db.Entry(comment).State = EntityState.Modified;
}
}
_db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
The problem I see with this is that it requires poking the database
for every item I want the changes applied to.
You needn't worry about this. The database gets called once when you save your changes. The looping affects only the objects in memory. As for your code you seem to be on the right track, but I do have one potential suggestion. Given your different steps for modifying or creating a comment, I assume that you have written and stored your comments as distinct entities which are linked by id to your items. If this is the case, I would suggest making "Comment" a property of the item, so that way you simply write to the property and simplify the process as a whole.
I ended up using a sort of homebrew method. Since both cases (add entry and edit entry needed to be handled separately, I handled them as such.
List<int> InDB = _db.Comments
.Where(r => model.ids.Contains(r.id))
.Select(r => r.id)
.ToList();
List<int> diff = model.ids.Except(InDB).ToList();
//Loop through items to ADD
foreach (int i in diff)
{
Comment comment = new Comment { Text = model.Text, id = i };
_db.Comments.Add(comment);
}
//Loop through items to edit
foreach (int i in InDB)
{
Comment comment = new Comment { Text = model.Text, id = i };
_db.Entry(comment).State = EntityState.Modified;
}

Entity Framework query after AddObject

Last week I had a bug that I tried to recreate. A piece of existing code did a mass insert on the database. Though, a value was not allowed to be duplicate in the database The old code persisted the changes to the database after every AddObject with a SaveChanges. This was not performing very well. So, instead of saving it every time I saved it after every 1000 records (and did this in a transaction,but that is not relevant for this sample).
this code below gives a overview of what I did.
TestEntities test = new TestEntities();
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
string q = j.ToString();
if (!test.warehouses.Any(x => x.combi == q))
{
warehouse wh = new warehouse();
wh.combi = j.ToString();
wh.ean = j.ToString();
test.warehouses.AddObject(wh);
}
}
}
test.SaveChanges();
What I did not know was that entity framework only queries the data in the database and not the pending data so the any query gives no result on the database (and I was assuming that there would be a result.) Hence, this results in unwanted duplicates in the code above.
For now I solved it to store all soon to be added objects in memory and then store them in the database. This works, but gives a lot of overhead in the code. Is there a way to work around this issue? Can you tell EF to work on the database and the pending changes? Are all ORM's working like this?
Thanks,
Patrick
Faced with similar problem I decided to check database as you do and check local data:
if( !test.warehouses.Local.Any(x => x.combi == q )
&& !test.warehouses.Any(x => x.combi == q ) )
This should work.
Edit:
But it doesn't. However, this does:
var isLocal = ( test as System.Data.Objects.ObjectContext ).ObjectStateManager
.GetObjectStateEntries( System.Data.EntityState.Added )
.Any( x => (x.Entity as warehouse).combi == q );
if( !isLocal && !test.warehouses.Any( x => x.combi == q ) ) {
// ...
}
I have to note that IDbSet<> (used by my code first instead of ObjectSet<> created by model designer) has .Local property so there's no need to query ObjectStateManager. I know that code first is completely different thing but you can be very efficient with it, too: design database with Microsoft SQL Server Management Studio and run excellent Reverse Engineer Code First from Entity Framework Power Tools with default or customized (if needed) T4 templates to get almost anything. http://en.wiktionary.org/wiki/your_mileage_may_vary

How to update a record by id?

I am very new To EntityFramework and having trouble to understand why first code doesn't update but second does. My aim is to update a record without querying db.
Is it too obvious?
using (CHATDBEntities db = new CHATDBEntities())
{
// update buddy
onlines buddy = new onlines();
buddy.id = 56;
buddy.last_seen = DateTime.Now;
buddy.status = (int)UserStatuses.Status.Chatting;
buddy.connected_to_id = 34;
buddy.room_id = 2;
db.SaveChanges();
}
using (CHATDBEntities db = new CHATDBEntities())
{
// update buddy
var buddy = db.onlines.First();
buddy.last_seen = DateTime.Now;
buddy.status = (int)UserStatuses.Status.Chatting;
buddy.connected_to_id = 34;
buddy.room_id = 2;
db.SaveChanges();
}
it looks like it is related to "id" which is primary identity key.
You have to retrieve an entity in order to update it, you can't just create a new one with the same id as a record from the database and expect EF to magically know that it needs to update the database. You could try attaching it to the ObjectContext but that is not how you are supposed to do updates.
Possibly you need to add your new buddy to onlines collection?
db.onlines.Add(buddy); // << this
db.SaveChanges();
The reason is because you need to select the object into the ObjectContect before you can update it. It looks like the only way to update the entity is to first read it.
Please see this other stackoverflow link which answers this.

Categories