I want to convert a list to EntityCollection.
List<T> x = methodcall();
EntityCOllection<T> y = new EntityCollection<T>();
foreach(T t in x)
y.Add(t);
I get this error.
The object could not be added to the
EntityCollection or EntityReference.
An object that is attached to an
ObjectContext cannot be added to an
EntityCollection or EntityReference
that is not associated with a source
object.
Anyone know about this error?
It sounds like x is the result of an ObjectContext query. Each ObjectContext tracks the entities it reads from the database to enable update scenarios. It tracks the entities to know when (or if) they are modified, and which properties are modified.
The terminology is that the entities are attached to the ObjectContext. In your case, the entities in x are still attached to the ObjectContext that materialized them, so you can't add them to another EntityCollection at the same time.
You may be able to do that if you first Detach them, but if you do that, the first ObjectContext stops tracking them. If you never want to update those items again, it's not a problem, but if you later need to update them, you will have to Attach them again.
Basically all entity objects are controlled by an object context which serves as change tracker. The idea here is that the entities themselves are dumb to their environment, but the object context knows what's going on.
This is an inversion of the DataSet model where the tables track their own changes.
So objects are added to an object context and its entity collections directly. Here you've created an EntityCollection that's not associated with an object context and therefore can't have other objects added to them. They must first be attached to the object context.
Really what you probably want is to return IQueryable instead of IList. That would allow you to execute queries against the results of methodcall().
Related
For auditing/history purposes, I am using the Entity Framework change tracker to determine, before writing changes, what has changed and serialize the changes. I can get the changed entities by calling this.ChangeTracker.Entries() in my DbContext derivative and looking at the values for anything marked EntityState.Added, EntityState.Deleted, or EntityState.Modified. This all works great.
My problem is that this method does not work to track changes to collections of EF objects (for instance, an ICollection<Person> property on a PersonGroup object).
I'm sure the EF context must track this somehow -- how else would the database update work, after all? But is it available to me?
What you're looking for is relationship change tracking. You can find it in ObjectStateManager of the underlying ObjectContext, here is how you get all added relationships:
//you need to call DetectChanges
((IObjectContextAdapter)context).ObjectContext.DetectChanges();
var addedRelations = ((IObjectContextAdapter)context).ObjectContext
.ObjectStateManager.GetObjectStateEntries(EntityState.Added)
.Where(e=>e.IsRelationship).ToList();
It turns out you can get at the relationships with this code (assuming it's running inside your DbContext derivative):
((IObjectContextAdapter) this).ObjectContext.ObjectStateManager
.GetObjectStateEntries(EntityState.Added)
.Where(e => e.IsRelationship)
.Select(r => new {EntityKeyInfo = r.CurrentValues[0],
CollectionMemberKeyInfo = r.CurrentValues[1], r.State});
Obviously you can tweak this based on what you need and it's up to do you something useful with it. The first two CurrentValues entries represent EntityKey objects which will allow you to get the IDs of the entities in question.
If you want to deal with deleted entities this won't work and you need to use reflection. Instead of CurrentValues[0] and CurrentValues[1] you can look at the internal properties Key0 and Key1, which are defined in an internal class you can't access at compile time. This will work: r.GetType().GetProperty("Key0", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(r, new object[0]). Note that this is probably not an intended use and could blow up whenever.
This question is pretty simple , but I can't seem to get a clear answer from MSDN docs.
I have a list of objects that map to a table in my database. Some of these objects have the ID filled out, some don't. I want to use something like
myContext.myTable.AttachAll(myList);
now , I was hoping that the objects that have an ID - which is Primary Key in the database will get attached and perform an update to those rows, and my objects that don't have an ID will be treated like an INSERT , and the db will automatically create an ID. Is that how this works?
Taken from documentation:
This method attaches all entities of a collection to the DataContext
in either a modified or unmodified state. If attaching as modified,
the entity must either declare a version member or must not
participate in update conflict checking. If attaching as unmodified,
the entity is assumed to represent the original value. After calling
this method, the entity's fields can be modified with other
information from the client before SubmitChanges is called. For more
information, see Data Retrieval and CUD Operations in N-Tier
Applications (LINQ to SQL).
When a new entity is attached, deferred loaders for any child
collections (for example, EntitySet collections of entities from
associated tables) are initialized. When SubmitChanges is called,
members of the child collections are put into an Unmodified state. To
update members of a child collection, you must explicitly call Attach
and specify that entity.
Can I track changes of child entities using ef 5?
Example:
Domain objects: Book (one to many) Page
var oldBook = context.Books.Include("Pages");
context.Entry(oldBook).CurrentValues.SetValues(updatedBook);
This code will update simple properties of the old book object with values of simple properties from updatedBook object.
It there any way to track children collection?(Pages in this case). Or any best practices how to do it?
Your questions is a bit ambiguous.
Can I track changes of child entities using ef 5?
Of course, unless you explicitly disable change tracking using IQueryable.AsNoTracking() or MergeOption.NoTracking then you can track changes of any entity attached to the DBContext ObjectStateManager.
If you are really asking if there's a function where you can do this:
context.Entry(oldBook).CurrentValues.SetValues(updatedBook);
And have the current values of the entire object graph -- where oldbook is the root -- set to the updated values of the updatedBook object graph then, no.
You have to loop through and call context.Entry(oldPage).CurrentValues.SetValues(updatedPage) for each page you wished to update.
I take it that you are in a disconnected scenario where you can't just pull the entities from the database and use automatic change tracking and set the modified values directly on the tracked entities, otherwise you could just use a single object graph attached to the context instead of working with two instances (ie oldbook, updated book).
If you already have a detached modified object graph that you need to work with an alternative to retrieving the entity from the db and using SetValues() is to attach the entities to the context as modified. You still need to loop through and do the same for all entities in the object graph.
context.Entry(updatedBook).State = EntityState.Modified;
foreach(var p in updatedBook.Pages) context.Entry(p).State = EntityState.Modified;
context.SaveChanges();
I am a bit confused on the usage of DbContext in Entity Framework. Here's the scenario I'm confused about.
I use a linq query from the dbcontext to get data. Something like:
List<Transactions> transactions = DefaultContext.Transactions.ToList();
Then I update a column in one of the transactions returned in that query directly in the database.
Then I call again:
List<Transactions> transactions = DefaultContext.Transactions.ToList();
When the list returns back this time, it doesn't reflect the updates/changes I made when running the update statement, unless I loop through all my transactions and Reload them:
foreach (DbEntityEntry<Transactions> item in DefaultContext.ChangeTracker.Entries<Transactions>())
{
DefaultContext.Entry<Transactions>(item.Entity).Reload();
}
Is this normal behavior? I assume that on my initial query, they are attached to the object context. Then when I query the second time, it doesn't make a trip to the database, and just pulls out the entities from the object context, unless I clear/detach or individually reload all of the entities.
It is normal and in case of DbContext API fixed behaviour because from some very strange reason neither DbSet or DbQuery expose MergeOption property. In case of ObjectContext API you can set the behaviour by MergeOption exposed on ObjectSet and ObjectQuery. So if you want to refresh values from database (and lose your changes) you can do:
ObjectContext objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
ObjectSet<Transactions> set = objectContext.CreateObjectSet<Transactions>();
set.MergeOption = MergeOption.OverwriteChanges;
List<Transactions> transactions = set.ToList();
If you just want to refresh transactions but you don't want to lose your changes you can use MergeOption.PreserveChanges instead.
That depends on the MergeOption of the DefaultContext.Transactions query. The default value, AppendOnly, won't overwrite objects already in your context. You can change this to OverwriteChanges to get the behavior you're expecting.
Related to the above, this is where I landed when I had this same error. But I wanted in my case to set Merge Option to No Tracking. I ran into this when I had an excel export method that was attempting to turn off object tracking of an IQueryable. Moving through lots of data that I wasn't going to be changing, I didn't need any change tracking.
A line of code similar to the below would fail on attempting to cast some IQueryables to class ObjectQuery (but succeed on others.)
var y = ((ObjectQuery)query).MergeOption = MergeOption.NoTracking;
Instead, I replaced this with usage of AsNoTracking
query = query.AsNoTracking();
Relating back to the original question, this would be potentially like the below, Extention method on DBQuery added in System.Data.Entity
List<Transactions> transactions = DefaultContext.Transactions.AsNoTracking().ToList();
Semi-Related Article:
https://msdn.microsoft.com/en-us/library/hh949853(v=vs.113).aspx
This question has been asked 500 different times in 50 different ways...but here it is again, since I can't seem to find the answer I'm looking for:
I am using EF4 with POCO proxies.
A.
I have a graph of objects I fetched from one instance of an ObjectContext. That ObjectContext is disposed.
B.
I have an object I fetched from another instance of an ObjectContext. That ObjectContext has also been disposed.
I want to set a related property on a bunch of things from A using the entity in B....something like
foreach(var itemFromA in collectionFromA)
{
itemFromA.RelatedProperty = itemFromB;
}
When I do that, I get the exception:
System.InvalidOperationException occurred
Message=The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects.
Source=System.Data.Entity
StackTrace:
at System.Data.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedTarget, Boolean applyConstraints, Boolean addRelationshipAsUnchanged, Boolean relationshipAlreadyExists, Boolean allowModifyingOtherEndOfRelationship, Boolean forceForeignKeyChanges)
at System.Data.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedEntity, Boolean applyConstraints)
at System.Data.Objects.DataClasses.EntityReference`1.set_ReferenceValue(IEntityWrapper value)
at System.Data.Objects.DataClasses.EntityReference`1.set_Value(TEntity value)
at
I guess I need to detach these entities from the ObjectContexts when they dispose in order for the above to work... The problem is, detaching all entities from my ObjectContext when it disposes seems to destroy the graph. If I do something like:
objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged)
.Select(i => i.Entity).OfType<IEntityWithChangeTracker>().ToList()
.ForEach(i => objectContext.Detach(i));
All the relations in the graph seem to get unset.
How can I go about solving this problem?
#Danny Varod is right. You should use one ObjectContext for the whole workflow. Moreover because your workflow seems as one logical feature containing multiple windows it should probably also use single presenter. Then you would follow recommended approach: single context per presenter. You can call SaveChanges multiple times so it should not break your logic.
The source of this issue is well known problem with deficiency of dynamic proxies generated on top of POCO entities combined with Fixup methods generated by POCO T4 template. These proxies still hold reference to the context when you dispose it. Because of that they think that they are still attached to the context and they can't be attached to another context. The only way how to force them to release the reference to the context is manual detaching. In the same time once you detach an entity from the context it is removed from related attached entities because you can't have mix of attached and detached entities in the same graph.
The issue actually not occures in the code you call:
itemFromA.RelatedProperty = itemFromB;
but in the reverse operation triggered by Fixup method:
itemFromB.RelatedAs.Add(itemFromA);
I think the ways to solve this are:
Don't do this and use single context for whole unit of work - that is the supposed usage.
Remove reverse navigation property so that Fixup method doesn't trigger that code.
Don't use POCO T4 template with Fixup methods or modify T4 template to not generate them.
Turn off lazy loading and proxy creation for these operations. That will remove dynamic proxies from your POCOs and because of that they will be independent on the context.
To turn off proxy creation and lazy loading use:
var context = new MyContext();
context.ContextOptions.ProxyCreationEnabled = false;
You can actually try to write custom method to detach the whole object graph but as you said it was asked 500 times and I haven't seen working solution yet - except the serialization and deserialization to the new object graph.
I think you have a few different options here, 2 of them are:
Leave context alive until you are done with the process, use only 1 context, not 2.
a. Before disposing of context #1, creating a deep clone of graph, using BinaryStreamer or a tool such as ValueInjecter or AutoMapper.
b. Merge changes from context #2 into cloned graph.
c. Upon saving, merge changes from cloned graph into graph created by new ObjectContext.
For future reference, this MSDN blogs link can help decide you decide what to do when:
http://blogs.msdn.com/b/dsimmons/archive/2008/02/17/context-lifetimes-dispose-or-reuse.aspx
I don't think you need to detach to solve the problem.
We do something like this:
public IList<Contact> GetContacts()
{
using(myContext mc = new mc())
{
return mc.Contacts.Where(c => c.City = "New York").ToList();
}
}
public IList<Sale> GetSales()
{
using(myContext mc = new mc())
{
return mc.Sales.Where(c => c.City = "New York").ToList();
}
}
public void SaveContact(Contact contact)
{
using (myContext mc = new myContext())
{
mc.Attach(contact);
contact.State = EntityState.Modified;
mc.SaveChanges();
}
}
public void Link()
{
var contacts = GetContacts();
var sales = GetSales();
foreach(var c in contacts)
{
c.AddSales(sales.Where(s => s.Seller == c.Name));
SaveContact(c);
}
}
This allows us to pull the data, pass it to another layer, let them do whatever they need to do, and then pass it back and we update or delete it. We do all of this with a separate context (one per method) (one per request).
The important thing to remember is, if you're using IEnumerables, they are deferred execution. Meaning they don't actually pull the information until you do a count or iterate over them. So if you want to use it outside your context you have to do a ToList() so that it gets iterated over and a list is created. Then you can work with that list.
EDIT Updated to be more clear, thanks to #Nick's input.
Ok I get it that your object context has long gone.
But let's look at it this way, Entity Framework implements unit of work concept, in which it tracks the changes you are making in your object graph so it can generate the SQL corresponding to the changes you have made. Without attached to context, there is no way it can tack changes.
If you have no control over context then I don't think there is anything you can do.
Otherwise there are two options,
Keep your object context alive for longer lifespan like session of user logged in etc.
Try to regenerate your proxy classes using self tracking text template that will enable change tracking in disconnected state.
But even in case of self tracking, you might still get little issues.