I've an object which is called Uczestnik which just got saved to database
var konsultant = uczestnik.Konsultanci;
uczestnik.Konsultanci = null; // null attached object and reuse it's ID later on for SAVE purposes
uczestnik.KonsultantNazwa = konsultant.KonsultantNazwa;
uczestnik.Szkolenie = null; // null attached object and reuse it's ID later on for SAVE purposes
uczestnik.SzkolenieID = szkolenie.SzkolenieID;
context.SzkolenieUczestnicies.AddObject(uczestnik);
context.SaveChanges();
context.Detach(uczestnik); // detatch to prevent Context problems
uczestnik.Szkolenie = szkolenie;// reassign for use in ObjectListView
uczestnik.Konsultanci = konsultant; // reassign for use in ObjectListView
After it's saved it's back into ObjectListView where user decided to change some value and the value was changed (one value from multiple to be exact). If I check value's entity state it's in Unchanged state so calling .Attach and .SaveChanges() won't do anything. I can use ChangeObjectState but if there's nothing changed then there's no sense to do so.
context.SzkolenieUczestnicies.Attach(uczestnik);
//context.ObjectStateManager.ChangeObjectState(uczestnik, EntityState.Modified);
context.SaveChanges();
How can I detect the change and prevent unnecessary traffic (I can imagine situation where nothing is changed in the object that holds files 5mb big) so resaving it makes no sense. Unless Entity is smart enough to detect that only one field was changed from 15 and change only that field?
If the entity is detached from a context you can't find out what has changed unless you are reloading the original entity from the database or you are using self-tracking entities or manage a tracking somehow yourself.
If you reload the entity you can use ApplyCurrentValues:
var originalEntity = context.MyEntities.Single(e => e.Id == detachedEntity.Id);
context.MyEntities.ApplyCurrentValues(detachedEntity);
context.SaveChanges();
This method marks the properties as modified which have different values between original and detached entity. SaveChanges will create an UPDATE statement which includes only those changed properties. If no property did change, SaveChanges does nothing.
But you are not completely free from "unnecessary traffic" because you have to load the original entity, you will save an unnecessary UPDATE statement though.
If you Detach entity it is not tracked by the context. In such case you are responsible for detecting when the object has changed and inform the context about changes by using ChangeObjectState. So you must track what user has modified or implement something directly to your entities. For example implement INotifyPropertyChanged (if you are using EntityObject based entities this interface should be already implemented).
Related
I'm currently developing an MVVM app using a Model-Designer based code-first design. So far I've been able to get all the basic CRUD operations working on single entities, however I can't seem to change the properties of collection objects at all using SaveChanges() - I've used an SQL profiler to see that it's attempting to UPDATE with the old value, and a step right after SaveChanges() my changes get reverted to their old values!
Some other info:
my dbContext is loaded using DI from PRISM/Unity and kept as a Unit-of-Work for a "details" page the user will edit and then save.
My WPF UI is bound correctly and can modify the changes on an object-level.
I can successfully use Add() to insert entities.
I've verified the entity state of the entity in the child collection is Modified both by setting it and simplify debugging.
I've attempted to manually Attach() and AddOrUpdate() on any or all items.
I've turned off all Lazy Loading and instead manually included all collections.
I've manually set the Entry() properties of IsModified and CurrentValue to their desired settings.
I've tried binding my VM properties to their data by either
dbContext.Classes.Local.ToBindingList() or new ObservableCollection<Class>(Entity.Property).
Is there anything that I could be missing here? Here's one attempt I've tried:
// Assigning an Index object that contains relationships
Index = await _model.PersonIndexes.Include(i => i.PersonAddresses).FirstAsync(i => i.Id == IndexId);
// Grabbing a filtered set of Addresses based on their data
var query = Index.PersonAddresses.Where(e => e.Condition == true );
Addresses = new ObservableCollection<PersonAddress>(await query.ToListAsync());
// Ensure state is tracked (I've tried with and without all combinations of these)
foreach (PersonAddress addr in Addresses)
{
//_model.PersonAddresses.AddOrUpdate(a => a.Id, addr);
if (addr.PendingRemoval)
{
_model.PersonAddresses.Attach(addr);
_model.Entry(addr).Property(a => a.PendingRemoval).CurrentValue = true;
_model.Entry(addr).Property(a => a.PendingRemoval).IsModified = true;
}
}
// Saving (after this line the properties of the entities in the related collection get reverted to their old values - i.e. if I change a phone number in the UI, the save below will replace the new values with the previous number.
await _model.SaveChangesAsync();
So it turns out this was an unfortunate error of configuration and a bad coincidence:
1) Check your models and server schema to ensure they are in sync (especially if using generated code from EF). In my case they were not, which lead to...
2) SaveChanges() was overwriting my properties in question because I had not noticed they were incorrectly set to have their StoredGeneratorPattern set to Computed in my model code. This caused the changed values to be ignored and overwritten.
3) The test case surrounding this had only implemented the same properties that were marked incorrectly, making it appear that all changes were being reverted - causing the confusion on where the problem code actually was. If another column had been modified and watched along with the others, this might have been caught sooner.
I am trying to figure out how to make EntityFramework think that current state of the entity is unchanged.
The reason I try to do it is that entity is connected to a "template" entity (1 to 1 relation) from which it inherits values if these are null for the current entity.
The template property inheritance occurs when ObjectMaterialized event is executed and this is when the entity state becomes EntityState.Modified.
Now setting it's state to EntityState.Unchanged does not work (State is still modified, should it behave like this ?). I tried to resolve this problem by calling Detach and Attach but several referential issues exist as I suspect Entity Framework (6 pre) is finding duplicate relations.
The source of an issue seems to be the byte array property which among others I reset using following code:
var entry = context.Entry(this);
foreach(var p in entry.CurrentValues.PropertyNames.Where(p => entry.Property(p).IsModified))
{
if(entry.CurrentValues[p] is byte[])
{
entry.OriginalValues[p] = (entry.CurrentValues[p] as byte[])/*.ToArray()*/;
}
else
{
entry.OriginalValues[p] = entry.CurrentValues[p];
}
}
entry.State = EntityState.Unchanged;
When reading entry state using entry.State it becomes Modified again and entry.CurrentValues.PropertyNames.Where(x => entry.Property(x).IsModified) returns one entry: byte array. I compared arrays, they are identical ! I also tried assigning a clone of the array to OriginalValues[propertyName] without success.
My question is how to reset the entity state to Unchanged when it contains byte array for EF to think the object was retrieved from the database exactly as it is now ?
ObjectStatemanager only records if an item has been changed. It doesn't reflect if the entry is the same as from the database, i.e. there is no magic involved.
Try something like:
ObjectStateEntry state = context.ObjectStateManager.GetObjectStateEntry(entity);
state.ChangeState(EntityState.Unchanged);
Actually EntityFramework keeps track of all modified properties but it seems byte[] is an exception and it's only irreversibly marked as changed.
I found an article about EF tracking: http://code.msdn.microsoft.com/How-to-undo-the-changes-in-00aed3c4
I solved the problem by not changing byte[] properties at all (as I can afford it because such are only used for serialization which can be postponed until actual saving to the database takes place) and instead of changing, only marking the object as modified which seems to to the job:
if(context.Entry(o).State == EntityState.Unchanged)
{
context.Entry(o).State = EntityState.Modified;
}
I am trying to update a record using Entity Framework but want to avoid manually setting each property and then calling save changes. I have an instance of this class bound to a dataform that updates all its properties as a user changes them. The nicest way I can see using this would be to set the object in the context equal to the object bound to my dataform but this does not work. I tried removing the object from the context then adding the dataform bound object but it creates a completely new record (I expected this but decided it was worth a shot to try it out). I assume there is some type of flag with in the context that is used to detect if a record is a new row or not considering the fact that replacing the context object with the databound object (which has the same pk value) does not work.
using (var scope = new TransactionScope())
{
try
{
using (var context = new CIS_DEVEntities())
{
GangMemberBio bio = context.GangMemberBios.First(P => P.id == this.id);
bio = this; //this does not work.
context.SaveChanges();
}
//If the top code is successfully completed then the transaction will be commited
//if not this line will be skipped and execution will be given to the catch block
scope.Complete();
}
catch
{
}
}
Edit Idea 1
I was thinking I could create a context object on my wpf window itself and then bind the dataform to my gangmemberbio object retrieved from this context. Then when I call save changes nothing else needs to be done. I heard that having a datacontext in memory is bad though.. Or is it just bad if I use it outside the context of a using statement?
DbContext keeps track of each entity's state internally, so it knows whether to add/update/delete any given one. When you Add() an entity to the context, even though it might already have an id, it's default state is Added (might be New, not sure), which causes the existing id to be ignored and a new record inserted into the db.
What you want to do is attach the already existing entity to the DbContext, and then let the DbContext know that it's to be treated as an existing entity so that it's updated, rather than inserted.
You do that by setting the EntityState in the DbContext for the given entity to Modified (by default it's New when adding an entity to the DbContext).
Try something like this:
using (var context = new CIS_DEVEntities())
{
context.Entry(this).State = EntityState.Modified;
context.SaveChanges();
}
In your edit page, load the object from the database first which will cause your form's fields to all be populated with existing data from the database. When the form is posted then, all the values of your data object model will be set.
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'm having a small problem with ASP.NET MVC and Entity Framework 4. I have an entity called "UF" and another one called "Pais", and they have this relation:
UF [* ... 0..1] Pais
I can access the Pais object directly from UF using a navigation property:
UF.Pais.Whatever = "foobar";
Currently I have a View which inserts a new item into the database, and it has an editor for "Pais.Codigo" ("Codigo" is the primary key for Pais). So when I insert a new UF, the framework creates an instance of class UF with a reference to an instance of class Pais. Then this is done:
if (ModelState.IsValid)
{
db.UFs.AddObject(uf);
db.SaveChanges();
return View();
}
The problem is that the EF is inserting a new Pais into the database, so it basically ignores the existing one.
For example, if let's say my object UF has a Pais with an ID of 1. The current value of uf.Pais.Codigo is 1. Other attributes, like the description, is currently null. When I execute the SaveChanges, both "uf" and "uf.Pais" are with the state of Added. The correct state for "uf.Pais" should be Unchanged, since it already exists on the database.
My question is: there's some way of changing the default relationship EntityState for Unchanged? The following code solves the problem, but adding it to each function with adds a new entry to the database (and for each FK) is overkill!
db.ObjectStateManager.ChangeObjectState(uf.Pais, EntityState.Unchanged);
That's it. I'm not sure if I was clear enough. Feel free to ask more information if needed. And sorry for any english mistakes!
Thanks,
Ricardo
PS: "Pais" stands for Country and "UF" for State.
My question is: there's some way of changing the default relationship
EntityState for Unchanged?
Yes by calling Attach instead of Unchanged.
The following code solves the problem, but adding it to each function
with adds a new entry to the database (and for each FK) is overkill!
No it is not overkill, it is a solution because either Attach or AddObject will always make the operation for all entities and associations in entity graph. That means that calling AddObject will make everything what context is not aware of yet as Added and Attach will make everything what context is not aware of as Unchanged (so you will in turn have to set each modified or inserted entity to its correct state). That is how EF works if you are using detached entities.
Another solution for the problem is making the connection after the UF is added:
// Here UF.Pais is null
db.UFs.AddObject(uf);
// Create dummy Pais
var pais = new Pais { Id = "Codigo" };
// Make context aware of Pais
db.Pais.Attach(pais);
// Now make the relation
uf.Pais = pais;
db.SaveChanges();
If you are working with detached entities you are always responsible for setting the correct state for each entity (and independent association). So you will either use attached entities to let EF make the magic for you (as shown in the example) or you will use the approach you dislike. In more complex scenarios you can find out that best approach is to load entity graph again and merge incoming changes into the attached graph.