Getting All Changes Made to an Object in the Entity Framework

Getting all changes made to an object in the Entity Framework

You can use ObjectContext's ObjectStateManager,GetObjectStateEntry to get an object's ObjectStateEntry, which holds its original and current values in the OriginalValues and CurrentValues properties. You can get the names of the properties that changed using the GetModifiedProperties method.

You can write something like:

var myObjectState=myContext.ObjectStateManager.GetObjectStateEntry(myObject);
var modifiedProperties=myObjectState.GetModifiedProperties();
foreach(var propName in modifiedProperties)
{
Console.WriteLine("Property {0} changed from {1} to {2}",
propName,
myObjectState.OriginalValues[propName],
myObjectState.CurrentValues[propName]);
}

EntityFramework Core: Get changes occured in entity and related data

Based on this article (Entity Change Tracking using DbContext in Entity Framework 6), you should override SaveChanges() method to track entity changes and its related entities.

public override int SaveChanges()
{
return base.SaveChanges();
}

Actually, You should change the above code to the following sample:

public override int SaveChanges()
{
var modifiedEntities = ChangeTracker.Entries()
.Where(p => p.State == EntityState.Modified).ToList();
var now = DateTime.UtcNow;

foreach (var change in modifiedEntities)
{
var entityName = change.Entity.GetType().Name;
var primaryKey = GetPrimaryKeyValue(change);

foreach(var prop in change.OriginalValues.PropertyNames)
{
var originalValue = change.OriginalValues[prop].ToString();
var currentValue = change.CurrentValues[prop].ToString();
if (originalValue != currentValue) //Only create a log if the value changes
{
//Create the Change Log
}
}
}
return base.SaveChanges();
}

Get List of Modified Objects within Entity Framework 7

You can use DbContext.ChangeTracker

var modifiedEntries = context.ChangeTracker
.Entries()
.Where(x => x.State == EntityState.Modified)
.Select(x =>x.Entity)
.ToList();

How can I log all entities change, during .SaveChanges() using EF code first?

You can get the before and after values for all changed entities by going through DbContext.ChangeTracker. Unfortunately the API is a little verbose:

var changeInfo = context.ChangeTracker.Entries()
.Where (t => t.State == EntityState.Modified)
.Select (t => new {
Original = t.OriginalValues.PropertyNames.ToDictionary (pn => pn, pn => t.OriginalValues[pn]),
Current = t.CurrentValues.PropertyNames.ToDictionary (pn => pn, pn => t.CurrentValues[pn]),
});

You can modify that to include things like the type of the entity if you need that for your logging. There is also a ToObject() method on the DbPropertyValues (the type of OriginalValues and CurrentValues) you could call if you already have a way to log whole objects, although the objects returned from that method will not have their navigation properties populated.

You can also modify that code to get all entities in the context by taking out the Where clause, if that makes more sense given your requirements.

How to get list of modified objects in Entity Framework 5

List<Object> modifiedOrAddedEntities = context.ChangeTracker.Entries()
.Where(x => x.State == System.Data.Entity.EntityState.Modified
|| x.State == System.Data.Entity.EntityState.Added)
.Select(x=>x.Entity).ToList();

When binding EF entities to a DataGridView it is often preferable to create an IBindingList from the DbSet.Local ObservableCollection. This way you get two way databinding and your new entities are automatically added to the context when adding via BindingSource.Add() or IBindingList.Add(). The simplest way to get this working, once properly bound, is to set DataGridView.AllowUserToAddRows to true and new rows the users enter will be new entities Added to the context.

context.Orders.Load();
BindingList<Order> bindingList = context.Orders.Local.ToBindingList();
BindingSource ordersBindingSource = new BindingSource();
ordersBindingSource.DataSource = bindingList;
dataGridView1.DataSource = ordersBindingSource ;

System.Data.Entity must be referenced to use .ToBindingList() and you must be using EF4.1 or greater.

Track changes to collections using Entity Framework change tracker

What you're looking for is relationship change tracking. You can find it in ObjectStateManager of the underlying ObjectContext, here is how you get all added relationships:

//you need to call DetectChanges
((IObjectContextAdapter)context).ObjectContext.DetectChanges();

var addedRelations = ((IObjectContextAdapter)context).ObjectContext
.ObjectStateManager.GetObjectStateEntries(EntityState.Added)
.Where(e=>e.IsRelationship).ToList();

Entity changes made manually are not being identified by EntityFramework

Let look at the first two statements:

var entityId = 13;
...

// This is "false"
var hasEntityWithValue = db.MyEntity.Any(p => p.Id == entityId && p.MyProperty1 != null);

// The "myEntity" is null
var myEntity = db.MyEntity.FirstOrDefault(p => p.Id == entityId && p.MyProperty1 != null);

Both of these sends the same query to the database:

SELECT * FROM MyEntities WHERE ID = 13 AND MyProperty1 IS NOT NULL

This returns no records from the database because the database does not yet have the new data - there are no records saved in the database with an ID of 13 where MyProperty1 IS NOT NULL. This is because you have not yet called db.SaveChanges(). The first statement turns the result of that SQL statement into a value of false, whereas the second statement turns it into a value of null.

Moving on to the next statement:

// Gets the entity only by Id
myEntity = db.MyEntity.FirstOrDefault(p => p.Id == entityId);

This sends a query to the database like this:

SELECT * FROM MyEntities WHERE ID = 13

The database does have a MyEntitiy with an ID of 13, and it returns that MyEntity to EF. However, before EF returns the MyEntity to you, EF checks to see if it has a MyEntity with an ID of 13 in its cache. It does have a cached MyEntity with an ID of 13, so it sends the cached MyEntity. And the cached MyEntity just so happens to be the one you updated in your call to your custom ApplyChanges method.

// And when I compare the "MyProperty1" it's "true". Why?????
hasEntityWithValue = myEntity.MyProperty1 != null;

The reason this is true is because the entity returned to you is the one that is in the EF cache.

When you make a query with EF, it will send the query to the database, and if records are returned from the database, EF will check it's cache to see if records with the same key are in the cache. If they already exist in the cache, the cached records will be returned in place of the records that were found in the database, even if the cached records are different from the database records. (for more information on how to get around this caching, see http://codethug.com/2016/02/19/Entity-Framework-Cache-Busting/)

The cache checking is done at the point the query is run. So you could make this call and you would see your updated data:

var data = db.MyEntity
.Where(p => p.Id == entityId)
.ToList()
.Where(p => p.MyProperty1 != null);

The first Where function is processed by the database. The second is processed in memory wherever your C# code is running. The ToList call forces the query that has been built so far to be sent to the database and run before any more filtering or sorting is done.


You can also use a transaction for this, but as you mention, this will lock up resources for the duration of the transaction. Assuming you are working with EF6, you can do this:

using (var transaction = db.Database.BeginTransaction()) 
{
// Applies the changes
this.ApplyChanges(db, entityType, entityId, propertyValues);

db.SaveChanges();

// Should be true
var hasEntityWithValue = db.MyEntity.Any(p => p.Id == entityId && p.MyProperty1!=null);

// At this point, queries to the database will see the updates you made
// in the ApplyChanges method
var isValid = ValidateSave();

if (isValid)
{
// Assuming no more changes were made since you called db.SaveChanges()
transaction .Commit();
}
else
{
transaction .Rollback();
}
}

Undo changes in entity framework entities

There is no revert or cancel changes operation in EF. Each entity has ObjectStateEntry in ObjectStateManager. State entry contains original and actual values so you can use original values to overwrite current values but you must do it manually for each entity. It will not reveret changes in navigation properties / relations.

The common way to "revert changes" is disposing context and reload entities. If you want to avoid reloading you must create clones of entities and modify those clones in new object context. If user cancel changes you will still have original entities.



Related Topics



Leave a reply



Submit