Reputation: 2861
With Entity Framework Core, I need to check the original values of a navigation property inside my entity but i can't find a way to do it.
I'm able to read the original and the current values of the actual entity and also his reference property but the OriginalValue property is missing when I read the navigation property.
Here's what I been able to do so far.
var entries = ChangeTracker.Entries<Book>()
.Where(x => x.State == EntityState.Modified)
.ToList();
foreach (var entry in entries)
{
// read the entity current & original values
var currentTitleValue = entry.Property(x => x.Title).CurrentValue;
var originalTitleValue = entry.Property(x => x.Title).OriginalValue;
// read the reference object current & original values
var promotionReferenceEntry = entry.Reference(x => x.Promotion);
var currentPromotionPriceValue = promotionReferenceEntry.TargetEntry.Property(x => x.Price).CurrentValue;
var originalPromotionPriceValue = promotionReferenceEntry.TargetEntry.Property(x => x.Price).OriginalValue;
// read the navigation object current & original values
var authorsCollectionEntry = entry.Collection(x => x.AuthorBooks);
var currentAuthorIds = authorsCollectionEntry.CurrentValue.Select(x => x.AuthorId).ToList();
var originalAuthorIds = ?????;
}
Upvotes: 7
Views: 2344
Reputation: 1353
https://github.com/dotnet/efcore/issues/9050
Original values are the values that were present in the database when the entity was queried. So for a relationship, the FK property will have an original value. Navigation properties are synthesized from the FK value rather than being directly mapped to anything in the database. This means that navigation properties (both reference and collections) do not have original values.
Upvotes: -1
Reputation: 9052
Here is a way to get the reference properties in the context as well as its original values:
var entries = ChangeTracker.Entries<Book>()
.Where(x => x.State == EntityState.Modified)
.ToList();
foreach (var entry in entries)
{
// If it is Added/Deleted, you can't get the OriginalValues/CurrentValues respectively.
// So make it Modified for the meanwhile.
var tempState = entry.State;
entry.State = EntityState.Modified;
// Clone the Entity values (the original ID, Current ID of the navigation
var currentValues = Entry(entry.Entity).CurrentValues.Clone();
var originalValues = Entry(entry.Entity).OriginalValues.Clone();
// Set the Entity values to the OriginalValues and load the reference
Entry(entry.Entity).CurrentValues.SetValues(originalValues);
Entry(entry.Entity).Reference(x => x.Promotion).Load();
// Store the Original Reference value in a variable
var promotionReferenceEntryOriginalValue = entry.Reference(x => x.Promotion).CurrentValue;
// Set the Entity values back to CurrentValues and load the reference
Entry(entry.Entity).CurrentValues.SetValues(currentValues);
Entry(entry.Entity).Reference(x => x.Promotion).Load();
// Store the Current Reference value in a variable
var promotionReferenceEntryCurrentValue = entry.Reference(x => x.Promotion).CurrentValue;
// Set the Entry State back to its original State Added/Modified/Deleted
entry.State = tempState;
// read the entity current & original values
var currentTitleValue = entry.Property(x => x.Title).CurrentValue;
var originalTitleValue = entry.Property(x => x.Title).OriginalValue;
// read the reference object current & original values
//var promotionReferenceEntry = entry.Reference(x => x.Promotion);
var currentPromotionPriceValue = promotionReferenceEntryCurrentValue.Price; // promotionReferenceEntry.TargetEntry.Property(x => x.Price).CurrentValue;
var originalPromotionPriceValue = promotionReferenceEntryOriginalValue.Price; // promotionReferenceEntry.TargetEntry.Property(x => x.Price).OriginalValue;
}
To make it dynamic, remove the type <Book>
from ChangeTracker.Entries<Book>()
and loop though the entry.Entity
properties inside the foreach (var entry in entries)
loop:
foreach (var propertyInfo in entry.Entity.GetType().GetProperties())
{
var propertyName = propertyInfo.Name;
//Do the loads here...
//Get the values
}
Check if it is a navigation reference or a collection reference by trying to access it using extra methods and check if it is null or not, not null means it is a valid property to try and load()
it:
private DbPropertyEntry GetProperty(DbEntityEntry entry, string propertyName)
{
try
{
return entry.Property(propertyName);
}
catch { return null; }
}
private DbReferenceEntry GetReference(DbEntityEntry entry, string propertyName)
{
try
{
return entry.Reference(propertyName);
}
catch { return null; }
}
private DbCollectionEntry GetCollection(DbEntityEntry entry, string propertyName)
{
try
{
return entry.Collection(propertyName);
}
catch { return null; }
}
Upvotes: 1