Load changes made in another DbContext - entity-framework

I have a WPF app that has a grid with a list of data that I loaded with EF. Some other window can make changes to the same data loaded on the grid but using a different dbcontext instance. How can I see the changed data on the grid? I know I can refresh a single entity with ctx.Entry<MyEntity>(instance).Reload(); - but I want to see all the changes and no matter what I do, I only see the old values. I can't use AsNoTracking neither create a new DbContext instance in this case.

To me looks like a very simple case and I cannot see why EF don't just
update the values of the entities.
EF has this mechanism as well but it is not exposed on DbContext API. You need to get back to ObjectContext. If you just want to reload set of entities you will call:
var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
objectContext.Refresh(RefreshMode.StoreWins, listOfEntitiesToReload);
RefreshMode.StoreWins causes all pending changes to be overwritten by reloaded values. You can also use RefreshMode.ClientWins which will keep your changes and merge them with reloaded data. The problem with this approach is that it only reloads entities you already have. You will not get new entities.
If you want to get new entities as well you must execute a query and you must tell EF that you want to reload values:
var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
var objectSet = objectContext.CreateObjectSet<MyEntity>();
objectSet.MergeOption = MergeOption.OverwriteChanges;
var result = objectSet.Where(...).ToList();
Again MergeOption.OverwriteChanges overwrites all pending changes but you can use MergeOption.PreserveChanges to merge reloaded values to your edited values.
I think there can be still some issues with refreshing values with some relations and maybe also entities which were deleted in the database.

Related

How do I add a new record to objectStateEntryList in SaveChanges override

I have several entities that contain datetime fields for EffectiveAsOf and ExpiredAsOf. When an entity is modified I want to override the SaveChanges method and rather than just update the existing entity have the code save the original record back to the database with an ExpiredAsOf datetime set to the current time, and a new record inserted with the new data and EffectiveAsOf set to the current time with ExpiredAsOf set to null.
I know that the ObjectStateEntry items in the objectStateEntryList contain CurrentValues and Original values objects, as well as an Entity object. What does EF use to write data to the DB the CurrentValues data or the Entity? How do I go about creating a new entry? Or, am I going about this the wrong way entirely?
I know that I can handle this in the entities outside of EF, but would rather have EF detect and handle these entities automatically.
Thanks in advance for your help and insight,
Jim
EF by default will use both - it uses original values to check for a concurrency issue (i.e. if the record has changed since you loaded the data from the DB) then uses the entity's current/modified values to update the DB record.
It is not possible to have EF "detect and handle these entities automatically". You will need to create a new instance of the entity object, copy the values from the existing entity object, set the appropriate effective and expired dates on both objects, add the new entity object to the DbContext, then save changes. The best place to do this is by overriding the SaveChanges() method of your DbContext. To keep it as clean and manageable as possible, I suggest using the repository pattern.

EF Code First - How does it know which objects to update?

As in the title, I have a method:
void method(MyDb db, Thread thread, Post post)
{
thread.Title = "changed";
db.SaveChanges();
}
(of course thread item is within MyDb object)
How does it recognize items that need to be updated? I didn't specify anywhere anything like db.Update(thread) or anything like that, it knew what to update without my help. What mechanisms are under it?
When you load entity Thread from database it becomes by default "attached". It means EF internally keep reference to your entity and it also keeps original values of the entity when you loaded it from the database.
When you updated a title there may be two scenarios:
You are using change tracking proxies and EF was notified about your change so it now knows that your instance was modified and it applies those changes to database when you call SaveChanges
You are not using change tracking proxies and when you call SaveChanges EF goes through its internally maintained list of entity references and check if any entity has any property different from original values - all such entities and their modified properties are updated to database during SaveChanges
You can read more about that process here.

Is it possible to tell if an entity is tracked?

I'm using Entity Framework 4.1. I've implemented a base repository using lots of the examples online. My repository get methods take a bool parameter to decide whether to track the entities. Sometimes, I want to load an entity and track it, other times, for some entities, I simply want to read them and display them (i.e. in a graph). In this situation there is never a need to edit, so I don't want the overhead of tracking them. Also, graph entities are sent to a silverlight client, so the entities are disconnected from the context. Hence my Get methods can return a list of entities that are either tracked or not. This is achieved dynamically creating the query as follows:
DbQuery<E> query = Context.Set<E>();
// Track the entities in the context?
if (!trackEntities)
{
query = query.AsNoTracking();
}
However, I now want to enable the user to interact with the graph and edit it. This will not happen very often, so I still want to get some entities without tracking them but to have the ability to save them. To do this I simply attach them to the context and set the state as modified. Everything is working so far.
I am auditing any changes by overriding the SaveChanges method. As explained above I may, in some low cases, need to save modified entities that were disconnected. So to audit, I have to retrieve the current values from the database and then compare to work out what was changed while disconnected. If the entity has been tracked, there is no need to get the old values, as I've got access to them via the state manager. I'm not using self tracking entities, as this is overkill for my requirements.
QUESTION: In my auditing method I simply want to know if the modified entity is tracked or not, i.e. do I need to go to the db and get the original values?
Cheers
DbContext.ChangeTracker.Entries (http://msdn.microsoft.com/en-us/library/gg679172(v=vs.103).aspx) returns DbEntityEntry objects for all tracked entities. DbEntityEntry has Entity property that you could use to find out whether the entity is tracked. Something like
var isTracked = ctx.ChangeTracker.Entries().Any(e => Object.ReferenceEquals(e.Entity, myEntity));

persisting an update query using openJPA

I am attempting to update an existing record using JPA. The following link seems to suggest that the only way to update a record would be to write the update query for it
enter link description here
Which is fine. But again, I am wondering why am I pulling this out of stored proc to use all f the magic of open JPA?
I thought that If I had an instance of a JPA object that if I tried to persist to the database using a call similar to this
emf.persist(launchRet)
the JPA framework would check to see if the record allready exists, if so, it would then proceed to make the changes to that record, if not, it would just add a new record. Which would be really cool. Instead, I am going to have to end up writing all that logic myself in an update query. Which is fine, But why can't I just use a stored proc and just pass it all the necessary values?
UPDATE: CODE EXPLAINING WHAT MY LAST COMMENT IS ALL ABOUT
try{
launchRet = emf.find(QuickLaunch.class, launch.getQuickLaunchId());
if(launchRet==null){
emf.getTransaction().begin();
emf.persist(launchRet);
emf.getTransaction().commit();
}
else{
emf.refresh(launchRet);
}
}
The variable launch is passed into the method...
public QuickLaunch UpdateQuickLaunchComponent(QuickLaunch launch)
Would I simple just set the found launch launchRet equal to the launch that was passed in?
Read the link that you posted:
You can modify an entity instance in one the following ways:
Using an Updating Query
Using the Entity's Public API
[...]
The way used in 99% of the cases is the second way:
Foo someExistingFoo = em.find(Foo.class, someExistingFooId);
someExistingFoo.setSomeValue(theNewValue);
// no need to call any persist method: the EM will flush the new state
// of the entity to the database when needed
Of course, the entity can also be loaded via a query, or by navigating through the graph of entities.
If you have a detached entity, and you want to persist its state to the database, use the EntityManager.merge() method. It finds the entity with the same ID as the detached one passed as argument, copies the state from the detached entity to the attached one, and returns the attached one:
Foo attachedModifiedFoo = em.merge(detachedFoo);
If the detached entity isn't persistent (i.e. doesn't have any ID), then it is created and made persistent.

Entity Framework - Why explicitly set entity state to modified?

The official documentation says to modify an entity I retrieve a DbEntityEntry object and either work with the property functions or I set its state to modified. It uses the following example
Department dpt = context.Departments.FirstOrDefault();
DbEntityEntry entry = context.Entry(dpt);
entry.State = EntityState.Modified;
I don't understand the purpose of the 2nd and 3rd statement. If I ask the framework for an entity like the 1st statement does and then modify the POCO as in
dpt.Name = "Blah"
If I then ask EF to SaveChanges(), the entity has a status of MODIFIED (I'm guessing via snapshot tracking, this isn't a proxy) and the changes are persisted without the need to manually set the state. Am I missing something here?
In your scenario you indeed don't have to set the state. It is purpose of change tracking to find that you have changed a value on attached entity and put it to modified state. Setting state manually is important in case of detached entities (entities loaded without change tracking or created outside of the current context).
As said, in a scenario with disconnected entities it can be useful to set an entity's state to Modified. It saves a roundtrip to the database if you just attach the disconnected entity, as opposed to fetching the entity from the database and modifying and saving it.
But there can be very good reasons not to set the state to Modified (and I'm sure Ladislav was aware of this, but still I'd like to point them out here).
All fields in the record will be updated, not only the changes. There are many systems in which updates are audited. Updating all fields will either cause large amounts of clutter or require the auditing mechanism to filter out false changes.
Optimistic concurrency. Since all fields are updated, this may cause more conflicts than necessary. If two users update the same records concurrently but not the same fields, there need not be a conflict. But if they always update all fields, the last user will always try to write stale data. This will at best cause an optimistic concurrency exception or in the worst case data loss.
Useless updates. The entity is marked as modified, no matter what. Unchanged entities will also fire an update. This may easily occur if edit windows can be opened to see details and closed by OK.
So it's a fine balance. Reduce roundtrips or reduce redundancy.
Anyway, an alternative to setting the state to Modified is (using DbContext API):
void UpdateDepartment(Department department)
{
var dpt = context.Departments.Find(department.Id);
context.Entry(dpt).CurrentValues.SetValues(department);
context.SaveChanges();
}
CurrentValues.SetValues marks individual properties as Modified.
Or attach a disconnected entity and mark individual properties as Modified manually:
context.Entry(dpt).State = System.Data.Entity.EntityState.Unchanged;
context.Entry(dpt).Property(d => d.Name).IsModified = true;