I want to get an object from entity framework and return it along with all its related objects to the user. When I set the MergeOption to MergeOption.NoTracking, I do get the first related objects even after the entities/context object get destroyed. But when I try to get the related objects of the related objects, I get an exception saying that the entities object doesn't exists any more! I tried setting the MergeOption on all entities that are retrieved, but that didn't work. Any idea how to solve this problem?! Here is my code:
MyFirstObject myObject;
using (var entities = new MyEntities())
{
entities.MyFirstObject.MergeOption = MergeOption.NoTracking;
entities.MySecondObject.MergeOption = MergeOption.NoTracking;
entities.MyThirdObject.MergeOption = MergeOption.NoTracking;
myObject = entities.MyFirstObject.First();
}
myObject1.MySecondObjects..... // Works fine.
myObject1.MySecondObjects.MyThirdObjects.... // Throw an exception.
MergeOption has no effect on this. MergeOption just tells EF how to handle materialized entities - NoTracking means that context must not track entities for changes. You must use eager loading.
myObject = entities.MyFirstObject
.Include("MySecondObjects.MyThirdObjects")
.First();
Related
I have read quite a few posts about this, and I can't see how my situation is different, but it must be because it still doesn't update.
Basically, my method receives a detached entity in a message. I check it's key to see if it already exists. If it does not exist I add it to the database. (this works fine) If it exists I would like to update its values.
Here is my code:
InteropObject clientObject = (InteropObject)message.ItemToAddUpdate;
bool exists = context.InteropObjects.Any(o => o.GUID == clientObject.GUID);
if (!exists)
{
context.InteropObjects.AddObject(clientObject);
}
else
{
context.Attach(clientObject);
context.ObjectStateManager.GetObjectStateEntry(clientObject).SetModified();
}
context.SaveChanges();
thanks for the help!
The problem is that in the old ObjectContext API, setting the state to Modified does not set the properties of the entity to Modified. In the DbContext API this is fixed internally by a call that does do that.
In the ObjectContext API, you can get the same effect by setting the properties of the attached entity:
context.Attach(clientObject);
var ose = context.ObjectStateManager.GetObjectStateEntry(clientObject);
// Obtain an object array containing current values
var values = new object[ose.CurrentValues.FieldCount];
ose.CurrentValues.GetValues(values);
// "Overwrite" CurrentValues by these values:
ose.CurrentValues.SetValues(values);
So you set the properties by the values they already have, but this triggers the state manager to mark the property as Modified.
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 == ...);
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.
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();
I have an entity with self reference (generated by Entity Designer):
public MyEntity: EntityObject
{
// only relevant stuff here
public int Id { get...; set...; }
public MyEntity Parent { get...; set...; }
public EntityCollection<MyEntity> Children { get...; set...; }
...
}
I've written a stored procedure that returns a subtree of nodes (not just immediate children) from the table and returns a list of MyEntity objects. I'm using a stored proc to avoid lazy loading of an arbitrary deep tree. This way I get relevant subtree nodes back from the DB in a single call.
List<MyEntity> nodes = context.GetSubtree(rootId).ToList();
All fine. But when I check nodes[0].Children, its Count equals to 0. But if I debug and check context.MyEntities.Results view, Children enumerations get populated. Checking my result reveals children under my node[0].
How can I programaticaly force my entity context to do in-memory magic and put correct references on Parent and Children properties?
UPDATE 1
I've tried calling
context.Refresh(ClientWins, nodes);
after my GetSubtree() call which does set relations properly, but fetches same nodes again from the DB. It's still just a workaround. But better than getting the whole set with context.MyEntities().ToList().
UPDATE 2
I've reliably solved this by using EF Extensions project. Check my answer below.
You need to assign one end of the relationship. First, divide the collection:
var root = nodes.Where(n => n.Id == rootId).First();
var children = nodes.Where(n => n.Id != rootId);
Now, fix up the relationship.
In your case, you'd do either:
foreach (var c in children)
{
c.Parent = root;
}
...or:
foreach (var c in children)
{
root.Children.Add(c);
}
It doesn't matter which.
Note that this marks the entities as modfied. You'll need to change that if you intend to call SaveChanges on the context and don't want this saved.
The REAL solution
Based on this article (read text under The problem), navigation properties are obviously not populated/updated when one uses stored procedures to return data.
But there's a nice manual solution to this. Use EF Extensions project and write your own entity Materilizer<EntityType> where you can correctly set navigation properties like this:
...
ParentReference = {
EntityKey = new EntityKey(
"EntityContextName.ParentEntitySetname",
new[] {
new EntityKeyMember(
"ParentEntityIdPropertyName",
reader.Field<int>("FKNameFromSP")
)
})
}
...
And that's it. Calling stored procedure will return correct data, and entity object instances will be correctly related to eachother. I advise you check EF Extensions' samples, where you will find lots of nice things.