I'm trying to run a no tracking query on my entities so that I can update them outside of the context. However, when the no tracking is not working and I get an exception stating
"The ObjectContext instance has been disposed and can no longer be used for operations that require a connection."
This exception is thrown by a property which in terms of the database model is a foreign key to a separate table, do I need to somehow set notracking for this separate entity as well?
My code is:
List<EmailQueue> result = null;
using (Entities context = new Entities())
{
var emailQueueQuery = context.EmailQueues;
emailQueueQuery.MergeOption = System.Data.Objects.MergeOption.NoTracking;
result = emailQueueQuery.Execute(System.Data.Objects.MergeOption.NoTracking).ToList<EmailQueue>();
}
return result;
My wild guess is that you have lazy loading turned on?
If you dispose the ObjectContext, then lazy loading can't work.
Related
I use Spring Data Neo4j 4 GraphRepository to save and retrieve data. Using GraphRepository save() and findAll() methods.
When I update an existing entity property to null, it seems that changes are not reflected in the returned data.
If I update the property to any other non-null value, the changes are reflected correctly.
I can see that the null property update is performed on the DB server. But the findAll() method doesn't reflect the change and keeps the old value.
Is this a known bug? Any workaround? Or is it some kind of caching problem?
UPDATE
After trying to understand what happens, I found that this problem will occur when you have two different Java objects for the same entity. The null property will never be updated (but other properties with non-null values will).
Example code:
#Autowired
MovieRepository repository;
public void test() {
repository.deleteAll();
Movie movie1 = new Movie();
movie1.setName("Pulp Fiction");
movie1.setDirector("Quentin Tarantino");
movie1 = repository.save(movie1);
System.out.println("Movie1: " + movie1);
Movie movie2 = new Movie();
movie2.setId(movie1.getId());
movie2.setName(movie1.getName());
movie2.setDirector(null); // implicit...
movie2 = repository.save(movie2);
System.out.println("Movie2: " + movie2);
Movie movie3 = repository.findOne(movie1.getId());
System.out.println("Movie3: " + movie3);
}
Real life case: when using SDN with a Spring MVC form, it looks like entities are created from Model attributes. When a value is set to null in a form, the update is performed correctly in Neo4j, but the values are not returned correctly when using any find...() methods. Therefore it leads to stale data.
Side note: this problem happens when the Neo4J session scope is per "session" and doesn't happen when the session scope is per "request".
#Bean
#Override
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Session getSession() throws Exception {
return super.getSession();
}
If you are using HttpSession-scoped persistence in SDN, you should ensure the the objects bound to your Controller via #ModelAttribute have the same scope as the persistence layer. Use the #SessionAttribute annotation on the Controller to achieve this.
If you use HttpRequest-scoped objects in your Controller and HttpSession-scoped persistence, you will get different objects representing the same graph entity at the web layer, and this will confuse the persistence mechanism.
Should not be a problem at all. I just tried
#Test
public void shouldPersistNulls() {
TempMovie movie = new TempMovie( "Pulp Fiction" );
tempMovieRepository.save( movie );
assertSameGraph( getDatabase(), "CREATE (m:Movie {name:'Pulp Fiction'})");
TempMovie loadedMovie = tempMovieRepository.findAll().iterator().next();
loadedMovie.setName(null);
tempMovieRepository.save(loadedMovie);
assertSameGraph( getDatabase(), "CREATE (m:Movie)");
TempMovie loadedAgainMovie = tempMovieRepository.findAll().iterator().next();
assertNull(loadedAgainMovie.getName());
}
and it passed.
Update based on edited question
The property representing the #GraphId must never be set manually i.e. via your code. You should load the entity by id when you require to update it. This ensures that the entity is known to the mapping context of the OGM and is managed correctly.
I've read multiple questions similar to this one but none are exactly my situation.
Using linq-to-sql I insert a new record and submit changes. Then, in the same web request, I pull that same record, and update it, then submit changes. The changes are not saved. The DatabaseContext is the same across both these operations.
Insert:
var transaction = _factory.CreateTransaction(siteId, userId, questionId, type, amount, transactionId, processor);
using (IUnitOfWork unitOfWork = UnitOfWork.Begin())
{
transaction.Amount = amount;
_transactionRepository.Add(transaction);
unitOfWork.Commit();
}
Select and Update:
ITransaction transaction = _transactionRepository.FindById(transactionId);
if (transaction == null) throw new Exception(Constants.ErrorCannotFindTransactionWithId.FormatWith(transactionId));
using (IUnitOfWork unitOfWork = UnitOfWork.Begin())
{
transaction.CrmId = crmId;
transaction.UpdatedAt = SystemTime.Now();
unitOfWork.Commit();
}
Here's the unit of work code:
public virtual void Commit()
{
if (_isDisposed)
{
throw new ObjectDisposedException(GetType().Name);
}
_database.SubmitChanges();
}
I even went into the designer.cs file and put a breakpoint on the field that is being set but not updated. I stepped through and it entered and execute the set code, so the Entity should be getting "notified" of the change to this field:
public string CrmId
{
get
{
return this._CrmId;
}
set
{
if ((this._CrmId != value))
{
this.OnCrmIdChanging(value);
this.SendPropertyChanging();
this._CrmId = value;
this.SendPropertyChanged("CrmId");
this.OnCrmIdChanged();
}
}
}
Other useful information:
ObjectTracking is enabled
No errors or exceptions when second SubmitChanges is called (just silently fails update)
SQL profiler shows insert and select but not the subsequent update statement. Linq-To-Sql is not generating the update statement.
There is only one database, one database string, so the update is not going to another database
The table has a primary key.
I don't know what would cause Linq-To-Sql to not issue the update command and not raise some kind of error. Perhaps the problem stems from using the same DataContext instance? I've even refreshed the object from the database using the DataContact.Refresh method before it is pulled for the update, but that didn't help.
I have found what is likely to be the root cause. I am using Unity. The initial insert is being performed in a service class with a PerWebRequest lifetime. The select and update is happening in a class with a Singleton lifetime. So my assumption that the DataContext instances are the same was incorrect.
So, in my class with the Singleton lifetime, I get a fresh instance of the database repository and perform the update and no problem.
Now I still don't know why the original code didn't work and my approach could still be considered more a workaround than a solution, but it did solve my problem and hopefully will be useful to others.
I'll preface this question with the following: I know there are a million posts on the internet about the old "An object with the same key already exists in the ObjectStateManager" issue. My scenario is a bit more complicated, I think.
I have a UnitOfWork class which creates a DbContext and passes it to any repository which is called. The pattern I'm using closely follows the Unit of Work tutorial on the ASP.NET site. Unlike the tutorial, my repositories take in Business entities, map them to data entities, and perform some CRUD action. My Business logic only works with Business entities. Here is what I'm trying to do in a sample Business Manager class:
_unitOfWork.Repository.Add(entity);
_unitOfWork.Save(); // context.SaveChanges() under the hood
...Perform some operations on the model...
_unitOfWork.Repository.Update(entity);
_unitOfWork.Save();
Here is a sample Update method from the repository:
public virtual void Update(entity)
{
var dataEntity = // map from business entity to data;
_context.Entry(dataEntity).State = EntityState.Modified;
}
It obviously fails on the last line. Here is where my confusion sets in:
The entity's State is Detached
When I attempt to change the State to Modified or Unchanged, it gives me the ObjectStateManager exception above.
When I attempt to detach the entity from the context (((IObjectContextAdapter)_context).ObjectContext.Detach(entity);) I get an exception about how the entity is not attached to the context, therefore, it cannot detach it. Very confusing (something fundamental I'm missing, for sure).
Many other posts suggest I make a database call, update that entity in the repository, then _unitOfWork.Save(). I don't like this approach. I shouldn't need to make an unnecessary network call to update an entity.
The Update method in the repository needs to handle two scenarios: 1) updating an entity which is not currently tracked by the context, and 2) updating an entity which IS currently tracked by the context. The second piece is what I'm struggling with.
Any help or insight is appreciated.
Thanks!
This means that there already is an object attached to the context with the same key as the new dataEntity. The existing object and the new entity both represent the same entry in the database but they are two different objects.
This may indicate that the lifespan of your _context is too long, but that's hard to judge from your code. It is certain though that the context was previously used to fetch an entity from the database that is subsequently duplicated by var dataEntity = ....
You may have to shorten the lifespan of the context, I can't tell. If you think it's OK you may want to use the Local collection to check whether the entity is already there. That will save the database round trip that Find may still make.
I found a hybrid solution which appears to work:
public virtual void Update(TB entity)
{
var dataEntity = Mapper.Map<TB, TD>(entity);
var pkey = _dbSet.Create().GetType().GetProperty("Id").GetValue(dataEntity);
var entry = _context.Entry(dataEntity);
if (entry.State == EntityState.Detached)
{
var attachedEntity = _dbSet.Find(pkey);
if (attachedEntity != null)
{
var attachedEntry = _context.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(dataEntity);
}
else
{
entry.State = EntityState.Modified;
}
}
else
{
entry.State = EntityState.Modified;
}
}
I'm using EF 5.0 to create a web and I have some issues disposing my context. All the times that I use a context is inside a using sentence, so the context should be disposed automatically but in a specific moment I get the next error when I try to attach an entity to a context:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
It semms that the entity is not disposed. How is the way to manage this situation? Do I have to dispose the ObjectContext to dispose the entities or is there any way to check if the Entity is attached?
Regards.
One way to do it is to detach the existing object before attaching. I don't have VS in front of me so I apologize if the code isn't exactly correct.
var existingObject = dbContext.Users.Local
.FirstOrDefault(x => x.id = newObject.id);
if (existingObject != null)
{
// remove object from local cache
dbContext.Entry(existingObject).State = EntityState.Detached;
}
dbContext.Users.Attach(newObject);
In case this doesn't fix the problem, you'll have to go to the old way of detaching objects.
// remove object from local cache
ObjectContext objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
objectContext.Detach(existingObject);
If you do something like that:
User u;
using (Entities ent = new Entities())
{
u = ent.Users.Single(a => a.ID == 123);
}
using (Entities ent2 = new Entities())
{
//loading the same user
User user2 = ent2.Users.Single(a => a.ID == 123);
//trying to attach the same object with the same key
ent2.Attach(u);
}
then you will get this error (I haven't tested this code).
EDIT: one of the solutions is to change the object's state:
ent2.Attach(u);
ent2.ObjectStateManager.ChangeObjectState(u, EntityState.Modified);
another solution is to check if the entity is already attached:
ObjectStateEntry state = null;
if(!ent2.ObjectStateManager.TryGetObjectStateEntry(((IEntityWithKey)u).EntityKey, out state))
{
ent2.Attach(u);
}
Dispose doesn't mean "reset to factory settings". It is a way to clean up unmanaged resources like database connections and such.
The problem has nothing to do with disposing a context or not. It even has nothing to do with having multiple contexts somewhere in place. If this would be the problem you would get the "An entity object cannot be referenced by multiple instances of IEntityChangeTracker" exception which is totally different to your exception.
You can simulate your exception quite easily with only a single context:
using (var ctx = new MyContext())
{
var customer1 = new Customer { Id = 1 };
var customer2 = new Customer { Id = 1 }; // a second object with the same key
ctx.Customer.Attach(customer1);
ctx.Customer.Attach(customer2); // your exception will occur here
}
The problem causing this exception is normally more hidden, expecially if you keep in mind that attaching or setting a state (for example to Modified) will also attach all related entities in the object graph of the entity you are attaching. If in this graph are two objects with the same key you'll get the exception as well, although you didn't attach those related entities explicitly.
But it's impossible to find the exact reason without more details about your code.
I've an Asp.net MVC website, in which I'm using entity framework in my data store to access the database(with POCO entities).
I don't why but sometimes, it's looks like the lazy loading is just not done:
example of code not working:
using(BusinessEntities context = new BusinessEntities()){
User user = context.Users.First(u=>u.Id == parameterId);
foreach(Post post in user.Posts){//user.Posts is null here and thrown an exception
//.. doing some things
}
}
But if I do this, it perfeclty works
using(BusinessEntities context = new BusinessEntities()){
User user = context.Users.Include("Posts").First(u=>u.Id == parameterId);
foreach(Post post in user.Posts){//user.Posts is null here and thrown an exception
//.. doing some things
}
}
But I don't understand why the lazy loading doesn't work:
Context isn't disposed
It's not a project anonym object or something like that
I know there is a lot of places in my code where I didn't have to indicate this .Include and doing relative works
I've the Lazy Loading Enabled set to True on my edmx model
What could leads to this behavior?
Declare Posts property as virtual so that proxy entity created by EF can lazy load the propety.
Please install EF 4.1 from http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=8363 to stop using magic strings inside Include.
Add
using System.Data.Entity;
to your using statements.
Then write the following query:
using(BusinessEntities context = new BusinessEntities()){
var user = context.Users.Include(p=>p.Posts).FirstOrDefault(u=>u.Id == parameterId);
if(user != null)
{
foreach(Post post in user.Posts)
{
//.. do something
}
}
}
For more information refer to the following http://blogs.msdn.com/b/adonet/archive/2011/01/31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx