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
Related
I am using entity framework 5.0. I am in a rocess od changing my app from ObjectContext to DbContext model. DbContext should be according to microsoft the recommended approach. I use database forst approach and I have generated model form database.
But, at a very first simple task there is a problem. Namely simple update of a record is broken.
Let's have a simple table Item, for ilustration only:
Item
(
ItemId int NOT NULL, -- Primary key
Name nvarchar(50) NOT NULL,
Description NVARCHAR(50)
)
I have noticed that using DbContext does not support updating a record not as ObjectContext does.
In my application I have a simple update method.
public void UpdateItem()
{
MyContext context = new MyContext();
Item item = new Item();
item.ItemId = 666;
context.Items.Attach(item);
// From this point onward EF tracks the changes I make to Item
Item.Description = "Some description";
context.SaveChanges();
}
Using ObjectContext this method correctly updates a record. Using SQL profiler I can see that it generates something like this (greatly simplified!!!)
UPDATE Item
SET Description = 'Some description'
WHERE ItemId = 666
If, however I try to do the same thing in DbContext I get the exception:
System.Exception: Items.aspx.cs - logged from CustomError() ---> System.Data.Entity.Validation.DbEntityValidationException: Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
at System.Data.Entity.Internal.InternalContext.SaveChanges()
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
at System.Data.Entity.DbContext.SaveChanges()
And no database UPDATE is issued to Sql server.
I guess that DbContext validates all the properties and the property Name is null. This by design. I do not intend to modify it, I do not even know what is it and I do not need to know what is it.
Only the property Description was changed. Clearly ObjectContext does not track changes correctly.
How can this problem be resolved?
I have researched the issue and found the something on updating records.
For example this link: https://stackoverflow.com/a/15339512/4601078
db.Users.Attach(updatedUser);
var entry = db.Entry(updatedUser);
entry.Property(e => e.Email).IsModified = true;
// other changed properties
db.SaveChanges();
But this is horrible code. For every property on should add a line like:
entry.Property(e => e.Email).IsModified = true;
This produces ugly unreadable code, an I suspect lamda expression are not stelar in performance.
Even worse are those who propose to make a roundtrip to DB to fetch existing records with all properties populated, update it and the save changes. This is a no go with regard to performance.
So, how to tackle with simple entity updates or is this DbContext just another item in microsofts collection of dead ends which serve no real purpose?
DbContext doesn't really track changes by watching properties, it compares the values to previously known values. And validation always works on the entire entity so the only real choice if you want to do things this way is to disable validation during this operation. See Entity Framework validation with partial updates
If you know for sure that the changes you apply are valid, or you have custom code to validate them, you can turn off validation by EF altogether:
db.Configuration.ValidateOnSaveEnabled = false;
This works OK as long as you do it your way: attach a new entity with a known Id (aka a stub entity) and then modify its properties. EF will only update the properties it detects as having been modified (indeed by comparing original and current values, not, as ObjectContext did, by change notifications). You shouldn't mark the entity itself as modified.
If you don't want to turn off EF's validation, but neither want to mark individual properties as modified, I think this could be a useful alternative (brought to my attention by Alex's answer).
I'm building my application using User Controls so I have Main Form and I display User Controls with this code:
Modules.CtrlListContractors mo = new Modules.CtrlListContractors();
splitContainerControl.Panel1.Controls.Clear();
splitContainerControl.Panel1.Controls.Add(mo);
and within User Control I can put anything with it but my problem about this error message :
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
I have some relations with my tables that is displayed as GridView inside CtrlListContractors and when I click the menu button to show CtrlListContractors I get the above error message and I think it's because about Lazy Loading so it will need to execute more queries to get additional data from related tables.
I have this code to execute at User Control Load event:
using (ContractorsEntities context = new ContractorsEntities(Properties.Settings.Default.Connection))
{
memberBindingSource.DataSource = context.Members.ToList();
}
I think the problem will be solved if I can get all data from main table and related tables at the same query before ObjectContext get disposed
If you want to dispose the ObjectContext (or DbContext), you'll have to grab all the data you need first. Including the related entities.
That can be done using Include, there are two overloads: Include(string) and more strongly typed Include (available from EF 4.1)
So, assuming there's a relationship Member (0-1) ---- (*) Contractors, you can Include the collection:
context.Members.Include(x => x.Contractors).ToList();
But as I said, you'd have to include everything you need before disposing the context. Either that or don't dispose the context and lazily load the details.
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.
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 a Linq object, and I want to make changes to it and save it, like so:
public void DoSomething(MyClass obj) {
obj.MyProperty = "Changed!";
MyDataContext dc = new MyDataContext();
dc.GetTable<MyClass>().Attach(dc, true); // throws exception
dc.SubmitChanges();
}
The exception is:
System.InvalidOperationException: An entity can only be attached as modified without original state if it declares a version member or does not have an update check policy.
It looks like I have a few choices:
put a version member on every one of my Linq classes & tables (100+) that I need to use in this way.
find the data context that originally created the object and use that to submit changes.
implement OnLoaded in every class and save a copy of this object that I can pass to Attach() as the baseline object.
To hell with concurrency checking; load the DB version just before attaching and use that as the baseline object (NOT!!!)
Option (2) seems the most elegant method, particularly if I can find a way of storing a reference to the data context when the object is created. But - how?
Any other ideas?
EDIT
I tried to follow Jason Punyon's advice and create a concurrency field on on table as a test case. I set all the right properties (Time Stamp = true etc.) on the field in the dbml file, and I now have a concurrency field... and a different error:
System.NotSupportedException: An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported.
So what the heck am I supposed to attach, then, if not an existing entity? If I wanted a new record, I would do an InsertOnSubmit()! So how are you supposed to use Attach()?
Edit - FULL DISCLOSURE
OK, I can see it's time for full disclosure of why all the standard patterns aren't working for me.
I have been trying to be clever and make my interfaces much cleaner by hiding the DataContext from the "consumer" developers. This I have done by creating a base class
public class LinqedTable<T> where T : LinqedTable<T> {
...
}
... and every single one of my tables has the "other half" of its generated version declared like so:
public partial class MyClass : LinqedTable<MyClass> {
}
Now LinqedTable has a bunch of utility methods, most particularly things like:
public static T Get(long ID) {
// code to load the record with the given ID
// so you can write things like:
// MyClass obj = MyClass.Get(myID);
// instead of:
// MyClass obj = myDataContext.GetTable<MyClass>().Where(o => o.ID == myID).SingleOrDefault();
}
public static Table<T> GetTable() {
// so you can write queries like:
// var q = MyClass.GetTable();
// instead of:
// var q = myDataContext.GetTable<MyClass>();
}
Of course, as you can imagine, this means that LinqedTable must somehow be able to have access to a DataContext. Up until recently I was achieving this by caching the DataContext in a static context. Yes, "up until recently", because that "recently" is when I discovered that you're not really supposed to hang on to a DataContext for longer than a unit of work, otherwise all sorts of gremlins start coming out of the woodwork. Lesson learned.
So now I know that I can't hang on to that data context for too long... which is why I started experimenting with creating a DataContext on demand, cached only on the current LinqedTable instance. This then led to the problem where the newly created DataContext wants nothing to do with my object, because it "knows" that it's being unfaithful to the DataContext that created it.
Is there any way of pushing the DataContext info onto the LinqedTable at the time of creation or loading?
This really is a poser. I definitely do not want to compromise on all these convenience functions I've put into the LinqedTable base class, and I need to be able to let go of the DataContext when necessary and hang on to it while it's still needed.
Any other ideas?
Updating with LINQ to SQL is, um, interesting.
If the data context is gone (which in most situations, it should be), then you will need to get a new data context, and run a query to retrieve the object you want to update. It's an absolute rule in LINQ to SQL that you must retrieve an object to delete it, and it's just about as iron-clad that you should retrieve an object to update it as well. There are workarounds, but they are ugly and generally have lots more ways to get you in trouble. So just go get the record again and be done with it.
Once you have the re-fetched object, then update it with the content of your existing object that has the changes. Then do a SubmitChanges() on the new data context. That's it! LINQ to SQL will generate a fairly heavy-handed version of optimistic concurrency by comparing every value in the record to the original (in the re-fetched) record. If any value changed while you had the data, LINQ to SQL will throw a concurrency exception. (So you don't need to go altering all your tables for versioning or timestamps.)
If you have any questions about the generated update statements, you'll have to break out SQL Profiler and watch the updates go to the database. Which is actually a good idea, until you get confidence in the generated SQL.
One last note on transactions - the data context will generate a transaction for each SubmitChanges() call, if there is no ambient transaction. If you have several items to update and want to run them as one transaction, make sure you use the same data context for all of them, and wait to call SubmitChanges() until you've updated all the object contents.
If that approach to transactions isn't feasible, then look up the TransactionScope object. It will be your friend.
I think 2 is not the best option. It's sounding like you're going to create a single DataContext and keep it alive for the entire lifetime of your program which is a bad idea. DataContexts are lightweight objects meant to be spun up when you need them. Trying to keep the references around is also probably going to tightly couple areas of your program you'd rather keep separate.
Running a hundred ALTER TABLE statements one time, regenerating the context and keeping the architecture simple and decoupled is the elegant answer...
find the data context that originally created the object and use that to submit changes
Where did your datacontext go? Why is it so hard to find? You're only using one at any given time right?
So what the heck am I supposed to attach, then, if not an existing entity? If I wanted a new record, I would do an InsertOnSubmit()! So how are you supposed to use Attach()?
You're supposed to attach an instance that represents an existing record... but was not loaded by another datacontext - can't have two contexts tracking record state on the same instance. If you produce a new instance (ie. clone) you'll be good to go.
You might want to check out this article and its concurrency patterns for update and delete section.
The "An entity can only be attached as modified without original state if it declares a version member" error when attaching an entitity that has a timestamp member will (should) only occur if the entity has not travelled 'over the wire' (read: been serialized and deserialized again). If you're testing with a local test app that is not using WCF or something else that will result in the entities being serialized and deserialized then they will still keep references to the original datacontext through entitysets/entityrefs (associations/nav. properties).
If this is the case, you can work around it by serializing and deserializing it locally before calling the datacontext's .Attach method. E.g.:
internal static T CloneEntity<T>(T originalEntity)
{
Type entityType = typeof(T);
DataContractSerializer ser =
new DataContractSerializer(entityType);
using (MemoryStream ms = new MemoryStream())
{
ser.WriteObject(ms, originalEntity);
ms.Position = 0;
return (T)ser.ReadObject(ms);
}
}
Alternatively you can detach it by setting all entitysets/entityrefs to null, but that is more error prone so although a bit more expensive I just use the DataContractSerializer method above whenever I want to simulate n-tier behavior locally...
(related thread: http://social.msdn.microsoft.com/Forums/en-US/linqtosql/thread/eeeee9ae-fafb-4627-aa2e-e30570f637ba )
You can reattach to a new DataContext. The only thing that prevents you from doing so under normal circumstances is the property changed event registrations that occur within the EntitySet<T> and EntityRef<T> classes. To allow the entity to be transferred between contexts, you first have to detach the entity from the DataContext, by removing these event registrations, and then later on reattach to the new context by using the DataContext.Attach() method.
Here's a good example.
When you retrieve the data in the first place, turn off object tracking on the context that does the retrieval. This will prevent the object state from being tracked on the original context. Then, when it's time to save the values, attach to the new context, refresh to set the original values on the object from the database, and then submit changes. The following worked for me when I tested it.
MyClass obj = null;
using (DataContext context = new DataContext())
{
context.ObjectTrackingEnabled = false;
obj = (from p in context.MyClasses
where p.ID == someId
select p).FirstOrDefault();
}
obj.Name += "test";
using (DataContext context2 = new ())
{
context2.MyClasses.Attach(obj);
context2.Refresh(System.Data.Linq.RefreshMode.KeepCurrentValues, obj);
context2.SubmitChanges();
}