I have an Entity Framework v1 project. I have two entities (Roles and Permissions), which have a many-to-many relationship with each other. I pass in a object to be saved (through a WCF call, I do not create it from a context myself), which has new entries in the many-to-many relationship.
I use "context.ApplyPropertyChanges" to update the record with the new properties. I know that this does not update relationships though. I attempt to either do a ChildCollection.Add(relatedObject); or ChildCollection.Attach(relatedObject).
When I use the "Add" method, I get the error that: The object cannot be added to the ObjectStateManager because it already has an EntityKey. Use ObjectContext.Attach to attach an object that has an existing key.
When I use the "Attach" method, I get the error that: The object cannot be added to the ObjectStateManager because it already has an EntityKey. Use ObjectContext.Attach to attach an object that has an existing key.
I am getting quite frustrated, and I think I can hear the Entity Framework laughing at me.
Does anyone know how I can resolve this?
MyRole x = context.Roles.FirstOrDefault(a => a.RoleId == this.RoleId);
context.ApplyPropertyChanges("Roles", this);
foreach (MyPermission p in this.Permissions)
{
x.Permissions.Add(p);
// ^ or v
x.Permissions.Attach(p);
}
context.SaveChanges();
Thanks.
Wow. After 20 or so straight hours on this problem, I'm starting to hate the Entity Framework. Here is the code that appears to be working currently. I would appreciate any advice on how to make this more streamlined.
I did rework the WCF service so that there is only the one data context. Thanks Craig.
Then I had to change the code to the following:
MyRole x = context.Roles.FirstOrDefault(a => a.RoleId == this.RoleId);
if (x == null) // inserting
{
MyApplication t = this.Application;
this.Application = null;
context.Attach(t);
this.Application = t;
}
else // updating
{
context.ApplyPropertyChanges("Roles", this);
x.Permissions.Load();
IEnumerable<Guid> oldPerms = x.Permissions.Select(y => y.PermissionId);
List<MyPermission> newPerms = this.Permissions.Where(y => !oldPerms.Contains(y.PermissionId)).ToList();
IEnumerable<Guid> curPerms = this.Permissions.Select(y => y.PermissionId);
List<MyPermission> deletedPerms = x.Permissions.Where(y => !curPerms.Contains(y.PermissionId)).ToList();
// new
foreach (MyPermission p in newPerms)
{
x.Permissions.Add(context.Permissions.First(z => z.PermissionId == p.PermissionId));
}
// deleted
foreach (MyPermission p in deletedPerms)
{
x.Permissions.Remove(context.Permissions.First(z => z.PermissionId == p.PermissionId));
}
}
You are using multiple ObjectContexts concurrently (the variable context and whereever this came from). Don't do that. It will only make things very difficult for you. Use one ObjectContext at a time.
I can give more specific advice if you show more code.
I suspect you are getting the errors because the ObjectContext thinks you are trying to add a new entity but finds it already has a EntityKey. I use the AttachTo method of the ObjectContext to attach my already existing entities to their EntitySet. I have had results generating my entities from stubs or hitting the database. This way when you add the entity to the navigation property on your entity, the ObjectContext finds the entity in it's EntitySet and knows it is an existing entity and not a new one. I don't know if this is clear. I could post some code if it would help. As Mr Stuntz said in his answer, posting more of your code would help.
Related
I'm watching some tutorial programming about asp.net core
In some tutorial lecturers use this code for update data in database
DataContext db = new DataContext();
var query = db.TblUsers.where(x => x.Id == 3).single();
query.Name = "Sami";
db.TblUsers.Attach(query);
db.Entry(query).state = EntityState.Modified;
db.SaveChanges();
But some lecturers use this code for updating data in database
DataContext db = new DataContext();
var query = db.TblUsers.where(x => x.Id == 3).single();
query.Name = "Sami";
db.Update(query);
db.SaveChanges();
In fact I'm confuse to use which of them? Because both code working.
Please tell me what is exactly different between those codes ?
For your current code, there is no need to use Attach for the first way. If you want to update Model by retriving record from database, kindly go with second way which is much convenience.
For Attach, it will put entity in the graph into the Unchanged state, and set entity as Modified by db.Entry(query).state = EntityState.Modified, then the changes in query will be saved to database. Since db.TblUsers.where(x => x.Id == 3).single() is already tracking the query, there is no need to use Attach.
There are two types for query, tracking and no-tracking. If you did not specificy the query as no-tracking expecitly like db.TblUsers.AsNoTracking().where(x => x.Id == 3).single(), the entity will be tracking which is Unchanged state.
For db.Update(query);, it will begins tracking the given entity in the EntityState.Modified state such that it will be updated in the database when Microsoft.EntityFrameworkCore.DbContext.SaveChanges is called.
I am new in Entity Framework. I want to remove multiple entities in one database context. If I used DBContext.Remove(Object) then It delete only the one entity from database. Please consider my code:
CCSRequest objCCSRequest = DBContext.CCSRequest.Find(ccsRequestId);
if (objCCSRequest != null)
{
DBContext.CCSRequest.Remove(objCCSRequest);
DBContext.SaveChanges();
}
CCProducts objCCProducts = DBContext.CCProducts.Find(ccsRequestId);
if (objCCProducts != null)
{
DBContext.CCProducts.Remove(objCCProducts);
DBContext.SaveChanges();
}
I want to remove entity in both CCSRequest and CCProducts table.
Thank you in advance.
If you want to have a loop that will remove entities of different types you might use this:
object[] entities = new object[]{
DBContext.CCSRequest.Find(ccsRequestId),
DBContext.CCProducts.Find(ccsRequestId)
};
foreach(object entity in entities)
{
DBContext.Entry(entity).State = EntityState.Deleted;
}
Going by the answer to a similar question, you will need to do this in a loop. Please check the answer to this question:
how to delete multiple rows of data with linq to EF using DbContext
I have the following piece of code
private void DoAddPropertyType()
{
var ctx = Globals.DbContext;
var propType = new PropertyType()
{
ID = Guid.NewGuid(),
Name = "NewType",
Description = "New Property Type",
ModifiedDate = DateTime.Now
};
ctx.AddToPropertyTypes(propType);
PropertyTypes.Add(propType);
}
Globals.DbContext provides a static reference to the objectcontext initiated on startup. For some reason the ctx.AddToPropertyTypes(propType); bit does not add the entity to the context. If I breakpoint after that line and browse the ctx.PropertyTypes entity set it is not there. Any ideas?
EDIT 1:
If I add a ctx.SaveChanges() after the ctx.AddToPropertyTypes(propType) and step the actual adding appears to happen only once SaveChanges execute. This however does not suit my requirements as I want to first validate objects prior to saving and wanted to iterate through the entities in the entity set. Does any one know of an alternative approach?
So that is the point of your issue. ctx.PropertyTypes is not a real collection - it is entrance to the database and your "browsing" actually executes query to the database where your new object was not yet stored. If you want to find a new object added to the context without saving it first you must search the object inside the ObjectStateManager:
var entity = ctx.ObjectStateManager
.GetObjectStateEntries(EntityState.Added)
.Where(e => !e.IsRelationship)
.Select(e => e.Entity)
.OfType<PropertyType>()
.SingleOrDefault(p => p.ID == ...);
Hi I use C# and EF 4.
I have two Entities CmsContent and CmsJob.
CmsJob has a navigational property to CmsContent.
I need add a CmsContent Object to CmsJob using the navigational property.
My code run with no error but I cannot persist the new entry om DataBase.
Could you tell me what I'm doing wrong?
Please provide me an example of code. Thanks for your support!
using (CmsConnectionStringEntityDataModel context = new CmsConnectionStringEntityDataModel())
{
CmsContent myContent = context.CmsContents.FirstOrDefault(x => x.ContentId == contentId);
CmsJob myJob = context.CmsJobs.FirstOrDefault(x => x.JobId == jobId);
myJob.CmsContents.Add(myContent);
}
Based on comments under #Hasan's answer you have incorrectly defined database. Your Job and Content are in many-to-many relation so you have junction table called CmsJobsContents but this table is missing primary key. Because of that it is read-only for EF and you cannot create new relations in your application. You must go to your database and mark both FKs in the CmsJobsContents as primary keys. After that update your model from database and you should be able to save changes.
That's because you haven't saved changes. Try this:
using (CmsConnectionStringEntityDataModel context = new CmsConnectionStringEntityDataModel())
{
CmsContent myContent = context.CmsContents.FirstOrDefault(x => x.ContentId == contentId);
CmsJob myJob = context.CmsJobs.FirstOrDefault(x => x.JobId == jobId);
myJob.CmsContents.Add(myContent);
context.SaveChanges();
}
When I have an entity that holds a reference to a singular entity I can create an EntityKey and assign that value to the EntityNameReference.Value property. It works perfectly and like a charm.
If I have an entity that holds a reference to multiple entities I cannot succeed in doing the same thing. Suppose an entity called Application that has a property that contains references to Modules (so Application has a List property called Modules).
How can I programmatically attach entity keys to that kind of property?
I tried something like this, without any success:
foreach(int idModule in selectedModules)
{
Module m = new Module();
m.EntityKey = new EntityKey("myModel.ModuleSet", "idModule", idModule);
ctx.Attach(m); //Here I have an exception
app.Modules.Add(m);
Thanks a lot for your help.
Marco
Does Module have Application navigation property? It should.
I would write something like:
foreach(int idModule in selectedModules)
{
Module m = new Module();
m.EntityKey = new EntityKey("myModel.ModuleSet", "idModule", idModule);
m.Application = app;
app.Modules.Add(m);
}
ctx.SaveChanges();