I am having an issue understanding why when adding a new entity to a DbSet of ObjectContext, that entity is not found will looking it up again.
using (var db = new SmartrailDB())
{
var cart01 = db.Carts.SingleOrDefault(x => x.Number == 0);
if (cart01 == null)
{
cart01 = new Cart { Number = 0 };
db.Carts.Add(cart01);
}
var cart02 = db.Carts.SingleOrDefault(x => x.Number == 0); // Should find the cart I just added - right?
Assert.IsNotNull(cart02); // Fails because cart02 does not exist in the db.Carts collection
}
Is anyone able to tell me what I am doing wrong here?
Also late on a Friday here so brain half asleep now.
You have to update your context before you try to access the entity. Just do:
db.SaveChanges(); right after db.Cart.Add(cart01);
Related
I have searched and find 2 way to update object in EF
var attachedEntity = _context.EntityClasses.Local.First(t => t.Id == entity.Id);
//We have it in the context, need to update.
if (attachedEntity != null)
{
var attachedEntry = _context.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(entity);
}
else
{
////If it's not found locally, we can attach it by setting state to modified.
////This would result in a SQL update statement for all fields
////when SaveChanges is called.
var entry = _context.Entry(entity);
entry.State = EntityState.Modified;
}
_context.SaveChanges();
And other way is seem more easy
var entity = _context.EntityClasses.FirstOrDefault(t => t.Id == entity.Id);
_context.Entry(entity ).EntityState.Modified
_context.SaveChanges();
What is best way to update object?
NOTE: the performence is importance with me
_context.EntityClasses.Local.First(t => t.Id == entity.Id)
=> means that you want to double check the entity on local (the latest loading from DB) and it is not send to DB to find the record so the performance is faster.
_context.EntityClasses.FirstOrDefault(t => t.Id == entity.Id): This command is look up the entity in DB. That means EF creates the query and look up in DB.
The below link is the difference of between Entity.Local.Find & Entity.Find http://msdn.microsoft.com/en-us/data/jj592872.aspx
Hope it helps!
I am new to MVC. I am getting error while updating multiple records to database using EF.
I want to create a group and add members in it.
I created to list
boxes. I have loaded all members in left box.
First time, I created the Group and added members from first box to second box, and saved
the form. All data saved successfully.
While I come to edit the group, I want to add/remove some members from the group, I got
error.
I used the following code for edit mode.
if (!string.IsNullOrEmpty(groupView.GroupName) && groupView.MemberIDs.Count > 0)
{
Groups group = new Groups();
List<GroupMembers> existingMembers = null;
if (groupView.GroupID > 0)
{
group.GroupID = groupView.GroupID;
//db.Entry(group).State = EntityState.Modified;
existingMembers = db.GroupMembers.ToList();
}
else
{
group.GroupID = groupView.GroupID;
group.GroupName = groupView.GroupName;
group.IsActive = true;
db.Groups.Add(group);
}
GroupMembers groupMembers, member;
foreach (short memberId in groupView.MemberIDs)
{
groupMembers = new GroupMembers();
if (groupView.GroupID > 0)
{
//Check whether the Member already exists in the group
member = existingMembers.Where(gm => gm.GroupID == groupView.GroupID && gm.MemberID == memberId).FirstOrDefault();
if (member != null)
{
groupMembers.GroupMemberID = member.GroupMemberID;
db.Entry(groupMembers).State = EntityState.Modified;
}
else
groupMembers.GroupMemberID = 0;
}
groupMembers.GroupID = group.GroupID;
groupMembers.MemberID = memberId;
groupMembers.IsActive = Convert.ToBoolean(groupView.IsActive);
if (groupMembers.GroupMemberID == 0)
db.GroupMembers.Add(groupMembers);
}
db.SaveChanges();
At runtime I received the following exception:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key
If I removed the following code from the above coding, duplicate data inserted in DB.
db.Entry(group).State = EntityState.Modified;
I am new to MVC, EF, as per my knowledge I wrote the above coding. I searched and checked some questions with the error information, but I am not able to understand clearly. So I put my coding. Please guide me to resolve this issue.
Thanks in advance.
As you can see from the code below i use AsNoTracking to get my object.
I then even use ObjectSateManager to see what is going on and i can see
nothing being tracked in the l* collections and yet i still get
"An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key".
Any ideas?
==========================================
BasketRepository repo = new BasketRepository();
var ba = repo.GetById(8);
var bpro = new BasketProduct(ba, ba.BasketProducts.First().Product, 3);
repo.AddToBasket(bpro);
repo.Save();
==================================
public Basket GetById(int basketId)
{
// eager-load product info
var basket = dbContext.Baskets.Include("BasketProducts")
.Include("BasketProducts.Product.Brand").AsNoTracking().SingleOrDefault(b => b.BasketId == basketId);;
return basket;
}
======================================
public void AddToBasket(BasketProduct product)
{
var ctx = ((IObjectContextAdapter)dbContext).ObjectContext;
ObjectStateManager objectStateManager = ctx.ObjectStateManager;
var l1 = objectStateManager.GetObjectStateEntries(EntityState.Added);
var l2 = objectStateManager.GetObjectStateEntries(EntityState.Modified);
var l3 = objectStateManager.GetObjectStateEntries(EntityState.Deleted);
//var l4 = objectStateManager.GetObjectStateEntries(EntityState.Detached);
var l5 = objectStateManager.GetObjectStateEntries(EntityState.Unchanged);
var existingProductInBasket = dbContext.BasketProducts.AsNoTracking().SingleOrDefault(b => b.BasketId == product.BasketId && b.ProductId == product.ProductId);
var l6 = objectStateManager.GetObjectStateEntries(EntityState.Added);
var l7 = objectStateManager.GetObjectStateEntries(EntityState.Modified);
var l8 = objectStateManager.GetObjectStateEntries(EntityState.Deleted);
//var l4 = objectStateManager.GetObjectStateEntries(EntityState.Detached);
var l9 = objectStateManager.GetObjectStateEntries(EntityState.Unchanged);
//objectStateManager.
dbContext.Entry<BasketProduct>(product).State = existingProductInBasket == null ? EntityState.Added : EntityState.Modified;
}
When you use asNoTracking() you get an unconnected entity that cannot be updated normally with EntityState.Modified. Then, one trick is to replace the cached entity values with non-cached entity values.
Here is the source code I use for special case where asNoTracking is necessary. It can be
private T ReplaceEntity(T cachedEntity, T nonCachedEntity) {
dbContext.Entry(cachedEntity).CurrentValues.SetValues(nonCachedEntity);
return cachedEntity;
}
A common use can be:
public virtual T FindFirstBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate, bool asNoTracking = false)
{
if (asNoTracking)
{
T cachedEntity = dbContext.Set<T>().FirstOrDefault(predicate);
T nonCachedEntity = dbContext.Set<T>().AsNoTracking().FirstOrDefault(predicate);
return ReplaceEntity(cachedEntity, nonCachedEntity);
}
return dbContext.Set<T>().FirstOrDefault(predicate);
}
For your situation:
var cachedEntity = dbContext.BasketProducts.SingleOrDefault(b => b.BasketId == product.BasketId && b.ProductId == product.ProductId);
var nonCachedEntity = dbContext.BasketProducts.AsNoTracking().SingleOrDefault(b => b.BasketId == product.BasketId && b.ProductId == product.ProductId);
var product= ReplaceEntity(cachedEntity, nonCachedEntity);
dbContext.Entry<BasketProduct>(product).State = EntityState.Modified;
Hope it helps!!
That was actually by bad. It can be a bit difficult conceptually to get used to the idea of
how things get attached and tracked by EF. The secret is to remember that if you have an object
graph (objects with relationships to other objects) whenever an object of the graph gets attached with a state X then all the other objects in the graph seem to be attached as well in that state.
In my scenario i was quite stupidly using
var bpro = new BasketProduct(ba, ba.BasketProducts.First().Product, 3);
to create a test product that actually already existed in the database and it was part of the object
graph containing the basket and its products. When i tried to attach this "new" basket EF rightly complained that an object with the same key already exists in the attached graph!
Found the issue...I had MergeOption.NoTracking set on my context.entity...sigh
Not sure what I'm doing wrong.
The object cannot be deleted because it was not found in the ObjectStateManager.
var deleteOrders = db.TABLE.Where(x => x.WCCR_ID == WccrId && x.ADAM == null).ToList();
foreach (var item in deleteOrders)
{
db.TABLE.DeleteObject(item);
}
db.SaveChanges();
I tried attaching the item db.Attach(item), but that throws an error 'Object with the same Key already exists'.
thanks for your help. cheers
Your code looks good. Try using:
var deleteOrders = db.TABLE.Where(x => x.WCCR_ID == WccrId && x.ADAM == null)
.ToList();
foreach (var item in deleteOrders)
{
db.Entry(item).State = System.Data.EntityState.Deleted;
}
db.SaveChanges();
UPDATE
EF 4.0 uses the ObjectContext class.
using(YourContext ctx = new YourContext())
{
ctx.ObjectStateManager.ChangeObjectState(entity, System.Data.EntityState.Deleted);
}
EF 4.1 uses the DbContext class in which the methods like Set<T> and Entry are defined.
using(YourContext ctx = new YourContext())
{
ctx.Entry(entity).State = System.Data.EntityState.Deleted;
}
UPDATE 2
The NuGet package only includes the EF 4.1 runtime and does not include the Visual Studio item templates for using DbContext with Model First and Database First development.
Download: http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=26825
I am having a real issue with the EF v1. I have quite a big EDMX with maybe 50 entities mapped, but this one entity is causing me grief.
The entity has mappings to other entities which in effect are reference tables, but for some reason it is trying to do an insert and not just update itself.
Here is a fragment of my code:
using (var context = new someEntities()) {
var studentCourseJoin =
context.StudentCourseJoinSet.Where(o => o.Code == scjCode).First();
studentCourseJoin.EntryStatus = new EntryStatus { Code = viewModel.StudentDetails.EntryStatusCode };
studentCourseJoin.ParentalInHigherEducation = new ParentalInHigherEducation { Code = viewModel.StudentDetails.ParentalInHigherEducationCode };
studentCourseJoin.School = new School { Code = viewModel.StudentDetails.SchoolCode };
studentCourseJoin.Institution = new Institution { Code = viewModel.StudentDetails.InstitutionCode };
studentCourseJoin.LastSchoolEndYear = viewModel.StudentDetails.LastSchoolEndYear;
studentCourseJoin.LastInstitutionEndYear = viewModel.StudentDetails.LastInstitutionEndYear;
// Blows up here trying to do an insert on the studentCourseJoin.Institution.
// But if I removed this one, then it will blow up on another one.
context.SaveChanges(true);
}
If anyone has ANY ideas please, they would help a lot.
Try adding those lines before calling SaveChanges:
ObjectStateEntry entry = context.ObjectStateManager.GetObjectStateEntry(studentCourseJoin);
entry.ChangeState(EntityState.Modified);
Update:
Try this for Institution instead:
studentCourseJoin.Institution = context.Institutions.FirstOrDefault(i => i.Code == viewModel.StudentDetails.InstitutionCode);