Using EntityFramework, I am trying the following in combination with MVC 3.
I have a ObjectContext, which is being initialized at the start of every request, and disposed at the end of the request. Much like a simple UnitOfWork implementation. Now think of having a Order class (EF generated), which is going to be initialised. I pass the ObjectContext to be used trough a overloaded constructor.
var order = new Order(myObjectContext);
Then inside the Order class this instance is kept (private field). After I set some properties of this Order instance (using the same ObjectContext). I want to put this instance in the Users' session because a second webpage is needed to populate some information before the order is ready to be saved.
This is were I'm stuck, after the user submits the second webpage I retrieve the Order object from the session, set some more properties and hit .SaveChanges() on the ObjectContext references by the Order object. So the one that was made in the first request. I think this is required because during the first request (when the Order was created) some other entities are being added to one of the orders collection. So I have to use the same context again. The problem is that after hitting the .SaveChanges() the method returns no error or whatsoever, but the data is not saved in the database. Even the identity value does not change from zero. Nothing is being committed.
When I first try to add the object to the collection:
this._context.Order.AddObject(this);
this._context.SaveChanges();
Please note this code is within a method, placed in a partial Order class. This time, at the .AddObject() I get the message:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
I tried to detach the Order object from it's original context and reattach it to the context made in the second request but of course this is not working since one of the Order properties is a collection of other entities, retrieved and added in the first request, thus with the first ObjectContext.
Probably I could overcome this issue by calling .SaveChanges() at the end of the first request. Then in the next request I would have to pull the order from the db with that request's ObjectContext. The thing is, I do not want to save the Order before the end of the second request.
To me it looks like the ObjectContext which is kept with the Order instance in the session needs a 'connection-refresh'?
I am not really sure I understand what you are trying to do but it seems that you want to make several changes to the Order object and only save it when all changes have been applied. Correct me if I am wrong but I take it that you are passing the ObjectContext as a parameter to the Order object and then putting the Order object in the session to embed the connection and maintain it to the next page?
This is probably where your entity object and the ObjectContext gets disconnected.
I suggest you take a look at the Self-Tracking Entity Generator - http://msdn.microsoft.com/en-us/library/ff407090.aspx . Maybe this will help you - http://henrycomputerworld.blogspot.no/2012/01/save-temporary-data-with-entity.html
This should allow you to do as many postbacks as necessary and let the Order instance track its own changes. You should then be able to commit the changes on your single ObjectContext instance.
If this doesn´t help, maybe you can provide a little more code and explanation.
Related
I've been debugging this program without any result, and unfortunately I can't see the root of the problem. I get this exception: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
There are 2 tables:
- CustomerSet
- OrderSet
A field named Customer_id in the Orders table ensures the relationship between the tables, and there is a virtual navigation property called Customer in the Orders table as well.
The scenario is the following:
I insert an element into the Orders table:
Order order = new Order();
Order.order_id = GenerateId(IdType.Order);
Order.date = DateTime.Now;
Order.Customer_id = GetCustomerId(tbCustomerName.Text);
Insert(order);
Within the Insert method there is DBContext in a using statement, so it automatically dispose when needed. I work inside this.
After that, I need data from the previously inserted element (for instance, I need some property of the Customer field).
And now I'm hoping that the Customer field got value:
Order o = GetOrder(order.order_id);
And I got this o with an exception in the Customer field: o.Customer threw an exception of type 'System.ObjectDisposedException'
I was playing with lazy loading, turning it on or off, but I didn't work out. The situation is the same...
What do I make a mess with?
What is real nice in this, that if I go step-by-step with F11, it often works correctly!
Please help! Thank you in advance.
Within the Insert method there is DBContext in a using statement, so it automatically dispose when needed
Not exactly "when needed". It calls IDisposable.Dispose() on the context object as soon as it goes out of scope of the using block.
After that, I need data from the previously inserted element
Your context is disposed at this point. If your action requires lazy loading, that will fail because the context is not available to perform the lazy load.
If you will generally need access to an object that has not been loaded, the most efficient approach is probably to use .Include to load it when you retrieve the rest of your object graph. That is called eager loading.
If you occasionally need access to an object that is not loaded when you load the rest of your object graph, you will need a new context.
For a discussion of loading related objects, I suggest
http://msdn.microsoft.com/en-us/data/jj574232.aspx
If you have a using statement within Insert then the context is gone.
That is fine as long as you make sure that as part of the Insert() order.order_id gets the new Id and that GetOrder() fires up a new context
I use ISession.Query<T>().ToList() to acquire a list of objects, add them to a ComboBox so the user can browse all the objects, and then I edit one of these and call ISession.Update() with that object. However this throws a NonUniqueObjectReferenceException.
Why does this happen and what approach should I use to avoid/fix this?
Problem explanation
When you load an entity, Session keeps track of the database's primary key for the entity as well as the entity's object reference (location in memory) - for the duration of the Session.
The NonUniqueObjectReferenceException is thrown if you attempt to persist an entity that has the same primary key but a different object reference as an already loaded entity for this Session.
In other words the Session is telling you, "I have an entity in memory with the same primary key as the entity you are trying to persist, but the object reference of my copy doesn't match your copy."
Problem example
Open a Session (#1).
Load the entity (object reference = A, primary key in database = 1).
Close the Session.
Open a new Session (#2).
Load the same entity again (this time, object reference = B, primary key in database = 1).
Change a property on object A and persist it inside Session #2.
NonUniqueObjectReferenceException will be thrown.
It's worth noting that this exception will be thrown, even if object A is simply part of a larger object graph that is being persisted in session #2 (even if object A didn't change).
It's also worth noting that you can load an entity directly (Session.Load, Session.Get, Session.QueryOver, etc) or indirectly (with a query that doesn't return the object, but causes the object to be loaded into memory). The NonUniqueObjectReferenceException can be thrown for both a directly or in-directly loaded entity.
Important note: This problem can be caused in other ways, for example it can be caused with a single Session if you load and then clone an entity and then use this clone to persist some changes using the Session. Reason being, the clone's object reference would differ from the original entity.
Solution explanation
There is a method called Merge on the Session object:
object Session.Merge(object obj)
Merge will take an entity and, using the entity's primary key, retrieve the already loaded version of this entity from the current Session. It will also update the properties of the Session's already loaded entity if they differ from the entity you have just passed.
This method does not change the entity that you pass in, but instead returns a different object which you should use.
One last note about Merge is that if the Session you are in doesn't have a loaded copy of this entity in memory, Merge will go ahead and load the entity from the database before performing it's usual merging functionality.
Solution example
// using the example above, we are at the beginning of step 6 inside session #2
// we have 2 important objects = ISession sessionTwo, Option objectA.
// Option is an entity defined by you, it is not part of NH.
objectA.SomeProperty = "blah";
var optionFromSessionTwo = (Option) sessionTwo.Merge(objectA);
// this will not throw and it will persist the changes to objectA
sessionTwo.Flush();
Hope this helps!
Update method as stated by Denis is used for the persistance of detached objects.This exception is thrown probably because you are trying to use Update() on those objects you probably already have in the session. You should use a transaction or Flush() in order to update your objects, if you already do so; deleting session.Update() from your code should do it.
Here is the appropriate part in the NHibernate documentation.
You need to read the documentation on:
How to use NHibernate on Winforms
What "Update" means.
The quick fix is: Don't call session.Update(), just call session.Flush() so the changes are reflected to the DB.
session.Update() doesn't update the entity, session does that transparently. Update and Save methods aren't related to INSERT and UPDATE, instead, Save() makes transient object persistent, where Update() makes detached object persisted.
I have bunch of entities and I would like to know which one has been Save() with NHibernate (no flush yet). How can I do it ?
According to the Hibernate's Documentation, the Session has a method called Contains(obj).
boolean contains(Object object)
Check if this instance is associated with this Session.
Parameters:
object - an instance of a persistent class
Returns:
true if the given instance is associated with this Session
Well, I suppose this should work in your case since your newly created object does not get in touch with the ISession before you call Save (or Get). Give it a try and tell us what happens.
Another option would be creating an abstraction on top of the ISession and implement your own code to check if the entity has already been saved or not.
If you are wondering if something has been persisted by NHibernate you could probably just check the 'ID' field and see if it has changed from the default value
I have a method on a data context that takes an object, calls attach to match it with its database entry, refresh and submit any of the changes i make to the object. The update works fine the first time but when i try to update the same entry a second time it starts throwing an exception. Here is whats in my update method:
this.GetTable(data.GetType()).Attach(data, false);
this.Refresh(RefreshMode.KeepCurrentValues, data);
this.SubmitChanges();
Here is the stack trace from the exception:
System.Data.Linq.DuplicateKeyException: Cannot add an entity with a key that is already in use.
at System.Data.Linq.Table`1.Attach(TEntity entity, Boolean asModified)
at System.Data.Linq.Table`1.System.Data.Linq.ITable.Attach(Object entity, Boolean asModified)
Am I missing some sort of table refresh?
When you update the entity the second time, are you attaching it to the same DataContext instance? If you are, that's why the exception is being thrown. You only need to attach an entity once during the lifetime of the DataContext.
Confirm data is not from this
I suspect the instance you are trying to attach (data) is from the same context you are attaching to (i.e. this). You don't need to attach if it is from the same instance (it will simply update when you SubmitChanges() thanks to change tracking. If your code above is part of a method, let's call it AttachAndSave(), you can't call it with data you retrieved from that context, or call it twice in a row with the same data as it will try and attach the same data twice.
If data is from a different DataContext, try using the asModified parameter:
this.GetTable(data.GetType()).Attach(data, true);
My suspicion if point 1. doesn't help is that SQL is determining that you want to insert a new instance rather than modify an existing one, and it's then throwing an exception because your instance has the same Primary Key as another existing instance (i.e. the one you are trying to update). However, this will require a timestamp tracking field, and is unlikely to be the cause. Point 1 above (attaching an instance belonging to the current context) is most likely your issue.
I am trying to use the attach method to update an entity that was retrieve via a stored proc.
The stored proc is set up to return a specific instance, which is present in my dbml. The retrieval works as expected and returns a fully populated object. The reason I need to use a stored proc is that I need to update a property on that entity at the same time that it is retrieved.
After I have retrieved this entity, I am mapping it using AutoMapper to another model which is used in another tier of the app. This tier performs a few operations, and makes a change to the entity, and passes it back to the repository for updating.
The repository converts this business model back into a database model, and attempts to attach it to the datacontext in order to take advantage of the automagic updating.
No matter what combination of Attach(entity, true) Attach(entity) etc, and it gives me messages like "Row not found or changed" or "Unable to add an entity with the same primary key".
Does anyone have any experience with the Attach method and how it can be used to update entities that did not necessarily come from the data context using query syntax (ie in this case a stored proc)?
Thanks alot
First, if you are creating a copy of the object, making changes and then trying to attach the copied object to the same DataContext as the one with the original object in it, then this would probably result in the "Unable to add an entity with the same primary key" message. One way to handle this is:
1. Get object from DataContext
2. Make changes and map object (or vice versa - whatever order)
3. Update the original object with the new values made in the other tier
4. SubmitChanges on the DataContext containing the original object
or
Get the object from a DataContext and close the DataContext
Make your changes and do your mapping
Retrieve the object from the DataContext to which you want to save
Update that object with the values from your mapped object
SubmitChanges
Alternately, when you say you are using the proc because you need to update a property at the same time that you retrieve it, I'd need to see the proc, but if you are somehow committing this update after retrieving the information, then indeed the message "row not found or changed" is correct. This would be hard to do, but you could do it if you're loading the data into a temp table, doing the update, and then using a select from the temp table to populate the object. One thing you could try is setting that property, in the L2S designer, to AutoUpdate = Never and see if that makes the problem go away. If so, this is your problem.
1: is it the same data-context, and
2: is it the same entity instance (or one that looks like it)
This would only happen for the same data-context, I suspect. If it is the same entity, then it is already there; just call SumbitChanges. Otherwise, either use a second data-context or detach the original entity.
So if I retrieved the entity via a stored proc, is it being tracked by the datacontext?
The thing is.. I'm going from the data model, to a another model that is used by another component, and then back. Its not.. really the same instance, but it does have all the same properties.
IE
public Models.Tag GetEntity()
{
var dbTag = db.PROJ_GetEntity((int)EntityStatuses.Created, (int)EntityStatuses.CreatingInApi).SingleOrDefault();
return FromDb Entity(dbEntity);
}
var appModel = GetEntity(); // gets an Entity from a stored proc (NOT GetEntity_RESULT)
appModel.MakeSomeChanges();
_Repo.Persist(appModel);
public void Persist(Models.AppModel model)
{
var dbEntity = Mapper.Map(model);
db.Attach(dbEntity);
db.SubmitChanges();
}
This is somewhat pseudo code like.. but it demostrates pretty much exactly what I am doing.
Thanks
I'm upvoting weenet's answer because he's right - you can't use Attach to apply the changes.
Unlike Entity Framework, you can only attach an L2S object to a datacontext if it has never been attached before - i.e. it's a newed entity that you want to Insert into a table.
This does cause numerous problems in multi-layered environments - however I've been able to get around many of the issues by creating a generic entity synchronisation system, which uses reflection and expression trees.
After an object has been modified, I run the dynamic delegate against a new object from the DC and the modified object, so that only the differences are tracked in the DC before generating the Update statement. Does get a bit tricky with related entities, though.