I have an optional foreign key that I'm attempting to set to null. No matter what I've tried, on SaveChanges(), the update statement sets the foreign key to the previous value instead of null.
Simplified Child Class:
public class Child
{
[Key, Column(Order = 0), ScaffoldColumn(false)]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("Parent")]
public int? ParentId { get; set; }
public virtual Parent Parent { get; set; }
}
Simplified Parent Class:
public class Parent
{
[Key, Column(Order = 0), ScaffoldColumn(false)]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public virtual ICollection<Child> Children { get; set; }
}
Things I've tried:
Load the Child object and set the ParentId null and set the Parent to null
Load the Child object and set the ParentId null and force the entity state to be modified
Load the Child object including the Parent object, then setting the values to null and forcing the entity state to be modified
Load the Parent object, then the Child object and .Remove(child) from the Parent object
Load the Parent object, then the Child object and .Remove(child) from the Parent and set the Child.ParentId to null and the Child.Parent to null.
Currently I have:
public void RemoveChildFromParent(int childId, int parentId)
{
Parent parent = _context.Parents.Include(x => x.Children).FirstOrDefault(u => u.Id == parentId);
Child child = parent.Children.SingleOrDefault(u => u.Id == childId);
parent.Children.Remove(child);
child.ParentId = null;
child.Parent = null;
child.StateOfEntity = StateOfEntity.Modified;
_context.ApplyStateChanges();
_context.SaveChanges();
}
On Save Changes, the SQL Update Statement still sets the ParentId on the Child object to the old value and I get this error:
System.InvalidOperationException was unhandled by user code
HResult=-2146233079
Message=The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: A referential integrity constraint violation occurred: The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship.
Source=System.Data.Entity
StackTrace:
at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options)
at System.Data.Entity.Internal.InternalContext.SaveChanges()
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
at System.Data.Entity.DbContext.SaveChanges()
at Insight.DataLayer.InsightContext.SaveChanges()
at Insight.DataLayer.ChildRepository.RemoveChildFromParent(Int32 childId, Int32 parentId)
at Insight.BusinessLayer.ParentManager.RemoveChild(Int32 id, Int32 parentId)
at Insight.PresentationLayer.Controllers.ParentController.RemoveChild(Int32 id, Int32 parentId)
at lambda_method(Closure , ControllerBase , Object[] )
at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass37.<>c__DisplayClass39.<BeginInvokeActionMethodWithFilters>b__33()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49()
InnerException:
Also, not sure if it matters, but I have LazyLoadingEnabled = false and AutoDetectChangesEnabled = false.
I'm not sure if there is an "elegant" solution to this (maybe changing the table structure?), but instead of spending more time on this small issue, I've decided to use DbContext.Database.ExecuteSqlCommand() and manually write the update statement.
It definitely feels like a work around in the Entity Framework methodology, but it's limited to this scenario, takes little time to do, and works as intended.
Also, not sure if it matters, but I have ... AutoDetectChangesEnabled
= false.
Yes, it matters. Did you disable auto change detection by default (in your context constructor for example)? That is dangerous because you have to know and understand when you need to call change detection manually if you disable the automatic detection - which is not a trivial thing. Usually AutoDetectChangesEnabled = false should only be set if you are sure that it doesn't cause unexpected results and when you really need it (normally for performance reasons when running bulk operations with many entity updates, inserts or deletes). I would definitely leave in at true by default.
I don't know what _context.ApplyStateChanges exactly does (seems to be a custom method) but all other lines of your code after the query don't call any EF methods until SaveChanges (and the 5 described procedures neither) which is exactly one of the situations (as described in the linked blog post above) where disabling automatic change detection does not work without further care.
To fix the problem, you could try to call _context.DetectChanges(); in your code snippet before SaveChanges (or maybe before ApplyStateChanges). However, the procedure to load the parent with all children is far to expensive and the simplest solution would be just loading the child, set the FK to null and save the changes - all this with enabled automatic change detection:
using (var context = new MyContext())
{
// as said, the following line should be your default
context.Configuration.AutoDetectChangesEnabled = true;
var child = context.Children.SingleOrDefault(c => c.Id == childId);
if (child != null)
{
child.ParentId = null;
context.SaveChanges();
}
}
Related
Can someone help me work out whats going on? I'm trying to update a foreign key and I'm getting strange behaviour. We have change tracking and lazy loading disabled and are using POCO classes. I am unable to update the foreign key Id without it throwing the following error:
The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: A referential integrity constraint violation occurred: The property value(s) of 'Child.ChildId' on one end of a relationship do not match the property value(s) of 'Link.ChildId' on the other end.
var linkTable = await Context.LinkTable
.Include(x => x.Child).FirstAsync(_ => _.LinkId == linkId);
// .... Log details of linkTable.Child
var newChild = await Context.ChildTable.FirstAsync(_ => _.ChildId == newChildId);
linkTable.Child = newChild;
linkTable.ChildId = newChild.ChildId; // (1)
Context.Entry(linkTable).State = EntityState.Modified;
await Context.SaveChangesAsync();
var parent = await Context.ParentTable
.Include(x => x.Links.Select(y => y.Child))
.FirstAsync(x => x.ParentId == parentId); // (2)
If I remove line (1), the error goes away at the SaveChanges call but it doesn't update the ChildId so the change is lost. I have also tried just updating the ChildId on it's own, and also nulling the child object first. Any changes to the ChildId throw this error.
The parent at line (2) loads the link with the new child even though it still shows the old ChildId. The db is not updated so the change is never committed.
The POCO:
[Table("LinkTable")]
public class LinkTable
{
[Key]
public int LinkTableId { get; set; }
//[ForeignKey("Child")] // (1)
public int ChildId { get; set; }
[ForeignKey("ChildId")]
public virtual ChildTable Child { get; set; } // (2)
}
(1) I have tried with both variants of the ForeignKey attribute.
(2) I have tried with and without the virtual key word, I believe without lazy loading we shouldn't need it.
The error makes no sense to me as the ChildId is taken from the child object, how can they not match?
I am trying out EntityFrameworkCore. I looked at the documentation, but couldn't find a way to easily update a complex entity that is related to another entity.
Here is a simple example. I have 2 classes - Company & Employee.
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Company Company { get; set; }
}
Company is a simple class, and Employee is only slightly complex, as it contains a property with reference to the Company class.
In my action method, which takes in the updated entity, I could first look up the existing entity by id, and then set each property on it before I call SaveChanges.
[HttpPut]
public IActionResult Update(int id, [FromBody]Employee updatedEmployee)
{
if (updatedEmployee == null || updatedEmployee.Id != id)
return BadRequest();
var existingEmployee = _dbContext.Employees
.FirstOrDefault(m => m.Id == id);
if (existingEmployee == null)
return NotFound();
existingEmployee.Name = updatedEmployee.Name;
if (updatedEmployee.Company == null)
existingEmployee.Company = null; //as this is not a PATCH
else
{
var existingCompany = _dbContext.Companies.FirstOrDefault(m =>
m.Id == updatedEmployee.Company.Id);
existingEmployee.Company = existingCompany;
}
_dbContext.SaveChanges();
return NoContent();
}
With this sample data, I make an HTTP PUT call on Employees/3.
{
"id": 3,
"name": "Road Runner",
"company":
{
"id": 1
}
}
And that works.
But, I hope to avoid having to set each property this way. Is there a way I could replace the existing entity with the new one, with a simple call such as this?
_dbContext.Entry(existingEmployee).Context.Update(updatedEmployee);
When I try this, it gives this error:
System.InvalidOperationException: The instance of entity type
'Employee' cannot be tracked because another instance of this type
with the same key is already being tracked. When adding new entities,
for most key types a unique tem porary key value will be created if no
key is set (i.e. if the key property is assigned the default value for
its t ype). If you are explicitly setting key values for new entities,
ensure they do not collide with existing entities or temporary values
generated for other new entities. When attaching existing entities,
ensure that only one entity instance with a given key value is
attached to the context.
I can avoid this error if I retrieve the existing entity without tracking it.
var existingEmployee = _dbContext.Employees.AsNoTracking()
.FirstOrDefault(m => m.Id == id);
And this works for simple entities, but if this entity has references to other entities, this causes an UPDATE statement for each of those referenced entities as well, which is not within the scope of the current entity update. The documentation for the Update method says that as well:
// Begins tracking the given entity, and any other reachable entities that are not already being tracked, in the Microsoft.EntityFrameworkCore.EntityState.Modified state such that they will be updated in the database when Microsoft.EntityFrameworkCore.DbContext.SaveChanges is called.
In this case, when I update the Employee entity, my Company entity changes from
{
"id": 1,
"name": "Acme Products"
}
to
{
"id": 1,
"name": null
}
How can I avoid the updates on the related entities?
UPDATE:
Based on the inputs in the comments and the accepted answer, this is what I ended up with:
Updated Employee class to include a property for CompanyId in addition to having a navigational property for Company. I don't like doing this as there are 2 ways in which the company id is contained within Employee, but this is what works best with EF.
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int CompanyId { get; set; }
public Company Company { get; set; }
}
And now my Update simply becomes:
[HttpPut]
public IActionResult Update(int id, [FromBody]Employee updatedEmployee)
{
if (updatedEmployee == null || updatedEmployee.Id != id)
return BadRequest();
var existingEmployeeCount = _dbContext.Employees.Count(m => m.Id == id);
if (existingEmployeeCount != 1)
return NotFound();
_dbContext.Update(updatedEmployee);
_dbContext.SaveChanges();
return NoContent();
}
Based on documentation of Update
Ref:Update
Begins tracking the given entity in the Modified state such that it will be updated in the database when SaveChanges() is called.
All properties of the entity will be marked as modified. To mark only some properties as modified, use Attach(Object) to begin tracking the entity in the Unchanged state and then use the returned EntityEntry to mark the desired properties as modified.
A recursive search of the navigation properties will be performed to find reachable entities that are not already being tracked by the context. These entities will also begin to be tracked by the context. If a reachable entity has its primary key value set then it will be tracked in the Modified state. If the primary key value is not set then it will be tracked in the Added state. An entity is considered to have its primary key value set if the primary key property is set to anything other than the CLR default for the property type.
In your case, you have updatedEmployee.Company navigation property filled in. So when you call context.Update(updatedEmployee) it will recursively search through all navigations. Since the entity represented by updatedEmployee.Company has PK property set, EF will add it as modified entity. A point to notice here is Company entity has only PK property filled in not others. (i.e. Name is null). Therefore while EF determines that Company with id=1 has been modified to have Name=null and issues appropriate update statement.
When you are updating navigation by yourself, then you are actually finding the company from server (with all properties populated) and attaching that to existingEmployee.Company Therefore it works since there are no changes in Company, only changes in existingEmployee.
In summary, if you want to use Update while having a navigation property filled in then you need to make sure that entity represented by navigation has all data and not just PK property value.
Now if you have only Company.Id available to you and cannot get other properties filled in updatedEmployee then for relationship fixup you should use foreign key property (which needs PK(or AK) values only) instead of navigation (which requires a full entity).
As said in question comments:
You should add CompanyId property to Employee class. Employee is still non-poco (complex) entity due to navigation present.
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int? CompanyId {get; set; }
public Company Company { get; set; }
}
During update action pass updatedEmployee in following structure. (See this is the same amount of data, just structured bit differently.)
{
"Id": 3,
"Name": "Road Runner",
"CompanyId": 1,
"Company": null //optional
}
Then in your action, you can just call context.Update(updatedEmployee) and it will save employee but not modify the company.
Due to Employee being complex class, you can still use the navigation. If you have loaded employee with eager loading (Include) then employee.Company will have relevant Company entity value.
Notes:
_dbContext.Entry(<any entity>).Context gives you _dbContext only so you can write just _dbContext.Update(updatedEmployee) directly.
As you figured out with AsNoTracking, if you load the entity in the context, then you cannot call Update with updatedEmployee. At that point you need to modify each property manually because you need to apply changes to the entity being tracked by EF. Update function gives EF telling, this is modified entity, start tracking it and do necessary things at SaveChanges. So AsNoTracking is right to use in this case. Further, if the purpose of retrieving entity from server for you is to check existence of employee only, then you can query _dbContext.Employees.Count(m => m.Id == id); and compare return value to 1. This fetches lesser data from the server and avoids materializing the entity.
There is no harm in putting property CompanyId if you don't add it to CLR class then EF creates one for you in background as shadow property. There will be database column to store value of FK property. Either you define property for it or EF will.
I am using EF6 with Generic Repository pattern. Recently I experienced a problem trying to delete a composite entity in a single go. Here is a simplified scenario:
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Child> Children { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("Parent")]
public int ParentId { get; set; }
public virtual Parent Parent { get; set; }
}
For deleting the Parent entity with related Children I am doing something like this:
public virtual T GetById(int id)
{
return this.DBSet.Find(id);
}
public virtual void Delete(T entity)
{
DbEntityEntry entry = this.Context.Entry(entity);
if (entry.State != EntityState.Deleted)
{
entry.State = EntityState.Deleted;
}
else
{
this.DBSet.Attach(entity);
this.DBSet.Remove(entity);
}
}
First I find the parent object by ID and then pass it to the delete method to change it's state to deleted. The context.SaveChanges() finally commits the delete.
This worked fine. The find method only pulled up Parent object and Delete worked since I have a cascade on delete enabled on Children.
But the moment I added another property in Child class:
[ForeignKey("Gender")]
public int GenderId { get; set; }
public virtual Gender Gender { get; set; }
For some reason EF started pulling related Children on the Parent.Find() method. Because of this I get the following error:
The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
Even after reverting the changes (removing the Gender property) the problem still exists. I am not able to understand this weird behavior!!
All I want to do is Delete the Parent object along with the Children.
There are some solutions around it but none really serves my purpose:
Turn LazyLoading to false - this.Configuration.LazyLoadingEnabled = false; This works but in my real application I need this property to true.
Iterate all children first and Delete them and then delete the Parent. This seems at best a workaround and is very verbose.
Use Remove() rather than just changing the EntityState to Deleted. I need to track Changes for Auditing so EntityState helps there.
Can someone explain why EF is loading related Entities even when I am not using them?
It seems that the problem was related to the life-cycle of context. I am using Unit Of Work and injecting it into my service layers using ninject.
kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();
The UnitOWork class implements IDisposable.
public bool DeleteView(int viewId)
{
// This is a workaround. It seems ninject is not disposing the context.
// Because of that all the info (navigation properties) of a newly created view is presisted in the context.
// Hence you get a referential key error when you try to delete a composite object.
using (var context = new ApplicationDbContext())
{
var repo = new GenericRepository<CustomView>(context);
var view = repo.GetById(viewId);
repo.Delete(view);
context.SaveChanges();
}
//var model = _unitOfWork.CustomViews.GetById(viewId);
//_unitOfWork.CustomViews.Delete(model);
//_unitOfWork.Save();
return true;
}
The commented code throws and error, while the un-commented one (using block) works. A controller method before this call loads the CustomView entity (which is of a similar structure as Parent with a list of children). And a subsequent user action can be triggered to delete that view.
I believe this has something to do with the context not being disposed. Maybe this has something to do with Ninject or UnitOfWork, I haven't been able to pin-point yet. The GetById() might be pulling the whole entity from context cache or something.
But the above workaround works for me. Just putting it out there so that it might help somebody.
I have a pretty deep object hierarchy in my application, and I am having trouble saving the entities. Depending on the order I do things, I either one of two errors:
[OptimisticConcurrencyException: Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.]
or
[DbUpdateException: An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.]
Here is the classes I am working with:
public class SpecialEquipment : Entity
{
public Equipment Equipment { get; set; }
public virtual ICollection<AutoclaveValidation> Validations { get; set; }
}
public class Equipment : Entity
{
public string Model { get; set; }
public string SerialNumber { get; set; }
public Location Location { get; set; }
public EquipmentType EquipmentType { get; set; }
public ICollection<Identifier> Identifiers { get; set; }
}
public class Identifier : Entity
{
public IdentifierType Type { get; set; }
public string Value { get; set; }
}
public class Location : Entity
{
public Building Building { get; set; }
public string Room { get; set; }
}
What I was trying to do was populate one SpecialEquipment object based on form inputs and already existing objects in the database and then save the special equipment to push all changes through, it looks like this:
Building building = buildingService.GetExistingOrNew(viewModel.BuildingCode)); //checks to see if building exists already, if not, create it, save it, and re-query
Location location = locationService.GetExistingOrNew(viewModel.Room, building); //checks to see if location exists already, if not, create it, save it, and re-query
EquipmentType equipmentType = equipmentTypeService.GetOne(x => x.Name == EquipmentTypeConstants.Names.Special);
Equipment equipment = new Equipment{ EquipmentType = equipmentType, Location = location };
equipment.Identifiers = new Collection<Identifier>();
foreach (FormIdentifier formIdentifier in identifiers)
{
FormIdentifier fIdentifier = formIdentifier;
IdentifierType identifierType = identifierTypeService.GetOne(x => x.Id == fIdentifier.Key);
equipment.Identifiers.Add(new Identifier { Type = identifierType, Value = fIdentifier.Value });
}
EntityServiceFactory.GetService<EquipmentService>().Save(equipment);
SpecialEquipment specialEquipment = new SpecialEquipment();
specialEquipment.Equipment = equipment;
specialEquipmentService.Save(specialEquipment);
This code returns Store update, insert, or delete statement affected an unexpected number of rows (0). If I comment out the foreach identifiers OR put the foreach identifiers after the equipment save and then call equipment save after the loop the code works. If I comment out the foreach identifiers and the save equipment line, I get : The INSERT statement conflicted with the FOREIGN KEY constraint "SpeicalEquipment_Equipment". The conflict occurred in database "xxx", table "dbo.Equipments", column 'Id'.
So how can I make these errors not occur but still save my object? Is there a better way to do this? Also I don't like saving my equipment object, then associating/saving my identifiers and/or then my special equipment object because if there is an error occurring between those steps I will have orphaned data. Can someone help?
I should mention a few things that aren't inheritly clear from code, but were some answers I saw for similar questions:
My framework stores the context in the HttpContext, so all the service methods I am using in my API are using the same context in this block of code. So all objects are coming from/being stored in one context.
My Entity constructor populates ID anytime a new object is created, no entities have a blank primary key.
Edit: At the request of comments:
My .Save method calls Insert or Update depending on if the entity exists or not (in this example insert is called since the specialEquipment is new):
public void Insert(TClass entity)
{
if (Context.Entry(entity).State == EntityState.Detached)
{
Context.Set<TClass>().Attach(entity);
}
Context.Set<TClass>().Add(entity);
Context.SaveChanges();
}
public void Update(TClass entity)
{
DbEntityEntry<TClass> oldEntry = Context.Entry(entity);
if (oldEntry.State == EntityState.Detached)
{
Context.Set<TClass>().Attach(oldEntry.Entity);
}
oldEntry.CurrentValues.SetValues(entity);
//oldEntry.State = EntityState.Modified;
Context.SaveChanges();
}
GetExistingOrNew for Building and location both are identical in logic:
public Location GetExistingOrNew(string room, Building building)
{
Location location = GetOne(x => x.Building.Code == building.Code && x.Room == room);
if(location == null)
{
location = new Location {Building = building, Room = room};
Save(location);
location = GetOne(x => x.Building.Code == building.Code && x.Room == room);
}
return location;
}
Get one just passes that where predicate to the context in my repository with singleOrDefault. I am using a Service Layer/Repository Layer/Object Layer format for my framework.
Your Insert method does not seem to be correct:
public void Insert(TClass entity)
{
if (Context.Entry(entity).State == EntityState.Detached)
Context.Set<TClass>().Attach(entity);
Context.Set<TClass>().Add(entity);
Context.SaveChanges();
}
specialEquipment is a new entity and the related specialEquipment.Equipment as well (you are creating both with new)
Look what happens if you pass in the specialEquipment into the Insert method:
specialEquipment is detached because it is new
So, you attach it to the context
Attach attaches specialEquipment and the related specialEquipment.Equipment as well because both were detached from the context
Both are in state Unchanged now.
Now you add specialEquipment: This changes the state of specialEquipment to Added but not the state of specialEquipment.Equipment, it is still Unchanged.
Now you call SaveChanges: EF creates an INSERT for the added entity specialEquipment. But because specialEquipment.Equipment is in state Unchanged, it doesn't INSERT this entity, it just sets the foreign key in specialEquipment
But this FK value doesn't exist (because specialEquipment.Equipment is actually new as well)
Result: You get the FK constraint violation.
You are trying to fix the problem with calling Save for the equipment but you have the same problem with the new identifiers which will finally throw an exception.
I think your code should work if you add the specialEquipment (as the root of the object graph) at the end once to the context - without attaching it, so that the whole graph of new objects gets added, basically just:
context.Set<SpecialEquipment>().Add(specialEquipment);
context.SaveChanges();
(BTW: Your Update also doesn't look correct, you are just copying every property of entity to itself. The context won't detect any change and SaveChanges won't write any UPDATE statement to the database.)
My guess? It can't have an ID if you haven't saved it and that's the root of the problem (since it works if you save first).
Pop everything in a transaction, so if anything goes wrong all is rolled back. Then you don't have orphans.
See http://msdn.microsoft.com/en-us/library/bb738523.aspx for how to use transactions with EF.
This error has been asked about A LOT. But none of the cases I think apply to my particular case, or at least not quite.
I am creating a new entity with 2 navigation properties that are collections. Both the entity and the navigation properties are new entities that do not exist in the database. My problem is that whenever I try to attach the entity to the context, if either of the collections has more than 1 element I get to aforementioned exception.
I am getting this error on the Attach() instruction in the following code:
using (var context = new NSModel())
{
context.Notifications.Attach(e);
context.ObjectStateManager.ChangeObjectState(e,
StateHelpers.GetEquivalentEntityState(e.State));
foreach (NavigationProperty1 np in e.NavigationProperty1s)
context.ObjectStateManager.ChangeObjectState(np,
StateHelpers.GetEquivalentEntityState(np.State));
foreach (NavigationProperty2 np in e.NavigationProperty2s)
context.ObjectStateManager.ChangeObjectState(np,
StateHelpers.GetEquivalentEntityState(np.State));
context.SaveChanges();
return e;
}
The code is for a web site so the entities are stateless and the context is created and disposed of with every call...
Any ideas?
Both the entity and the navigation properties are new entities that do
not exist in the database.
Then the question is: Why do you use Attach? By using Attach you tell EF that the entites are in the database and EF will consider the values of the primary key properties as the PK column values in the database. Because those properties must be unique EF will complain as soon as you have two entites with the same key values. It's well possible that you have this situation when you have autogenerated identities as key properties and don't set the values when you create the entities.
Simple example:
public class Parent
{
public Parent
{
Children = new List<Child>();
}
public int Id { get; set; }
public ICollection<Child> Children { get; set; }
}
public class Child
{
public int Id { get; set; }
}
This code ...
using (var ctx = new MyContext())
{
var parent = new Parent();
var child1 = new Child(); // <- Id is 0
var child2 = new Child(); // <- Id is 0 as well
parent.Children.Add(child1);
parent.Children.Add(child2);
ctx.Parents.Attach(parent); // <- exception here
//...
}
... will throw your exception because EF tries to attach two different child instances with the same key 0. When the second child child2 will get attached it says: "An object (namely child1) with the same key (namely 0) already exists in the ObjectStateManager".
If you want to add the whole object graph as new entities to the database you could just call context.Notifications.AddObject(e); instead of Attach.