Entities deep diff
$ dotnet add package DeepDiffConfigureEntity<TEntity>() method instead of Entity<TEntity>()First configure DeepDiff to register what types you want to compare, in the startup of your application
var diffConfiguration = new DiffConfiguration();
diffConfiguration.ConfigureEntity<Entity>() // configure Entity type
.HasKey(x => new { x.StartsOn, x.Name }) // business key
.HasValues(x => new { x.Price, x.Volume }) // properties to compare for update (and copy on update)
.HasMany(x => x.SubEntities) // one-to-many relation
.OnInsert(cfg => cfg // operations to perform on insert
.SetValue(x => x.PersistChange, PersistChange.Insert)) // change PersistChange property to Insert
.OnUpdate(cfg => cfg // operations to perform on update
.SetValue(x => x.PersistChange, PersistChange.Update)) // change PersistChange property to Update
.OnDelete(cfg => cfg // operations to perform on delete
.SetValue(x => x.PersistChange, PersistChange.Delete)); // change PersistChange property to Delete
diffConfiguration.ConfigureEntity<SubEntity>() // configure SubEntity type
.HasKey(x => x.SubName) // business key
.HasValues(x => x.Energy) // property to compare for update (and copy on update)
.OnInsert(cfg => cfg // operations to perform on insert
.SetValue(x => x.PersistChange, PersistChange.Insert)) // change PersistChange property to Insert
.OnUpdate(cfg => cfg // operations to perform on update
.SetValue(x => x.PersistChange, PersistChange.Update) // change PersistChange property to Update
.CopyValues(x => x.Description) // copy Description property from new to existing entity
.OnDelete(cfg => cfg // operations to perform on delete
.SetValue(x => x.PersistChange, PersistChange.Delete)) // change PersistChange property to Delete
.Ignore(x => new { x.Entity, x.EntityId} ); // foreign key and navigation are not relevant for diff
var deepDiff = diffConfiguration.CreateDeepDiff(); // finalize configuration
Then in your application code, this will detect insert/update/delete between existing and new entities. In case of update, properties will be copied from new to existing entity. PersistChange property will be set accordingly, so you can persist changes in your database and in case of update Description property will also be copied from new to existing sub entity.
var resultEntities = deepDiff.MergeMany(existingEntities, newEntities); // resultEntities will contain 'merged' entities
Example entities definition
public class Entity
{
public Guid Id { get; set; } // DB Key
public DateTime StartsOn { get; set; } // Business Key
public string Name { get; set; } // Business Key
public decimal Price { get; set; }
public int Volume { get; set; }
public PersistChange PersistChange { get; set; }
public List<SubEntity> SubEntities { get; set; }
}
public class SubEntity
{
public Guid Id { get; set; } // DB Key
public string SubName { get; set; } // Business Key
public int Energy { get; set; }
public string Description { get; set; } // Not part of key or values, will not be used in diff
public PersistChange PersistChange { get; set; }
public Guid EntityId { get; set; } // Foreign Key
public Entity Entity { get; set; } // Navigation property
}
public enum PersistChange
{
None,
Insert,
Update,
Delete
}
Defines properties used to compare and detect an insert and a delete. Mandatory unless NoKey has been defined
IEntityConfiguration<TEntity> HasKey<TKey>(Expression<Func<TEntity, TKey>> keyExpression)
IEntityConfiguration<TEntity> HasKey<TKey>(Expression<Func<TEntity, TKey>> keyExpression, Action<IKeyConfiguration<TEntity>> keyConfigurationAction)
Defines properties used to detect an update in case keys are identical
IEntityConfiguration<TEntity> HasValues<TValue>(Expression<Func<TEntity, TValue>> valuesExpression)
IEntityConfiguration<TEntity> HasValues<TValue>(Expression<Func<TEntity, TValue>> valuesExpression, Action<IValuesConfiguration<TEntity>> valuesConfigurationAction)
Defines property to navigate to a single child
IEntityConfiguration<TEntity> HasOne<TChildEntity>(Expression<Func<TEntity, TChildEntity>> navigationPropertyExpression)
IEntityConfiguration<TEntity> HasOne<TChildEntity>(Expression<Func<TEntity, TChildEntity>> navigationPropertyExpression, Action<INavigationOneConfiguration<TEntity, TChildEntity>> navigationOneConfigurationAction)
Defines property to navigate to multiple children
IEntityConfiguration<TEntity> HasMany<TChildEntity>(Expression<Func<TEntity, List<TChildEntity>>> navigationPropertyExpression)
IEntityConfiguration<TEntity> HasMany<TChildEntity>(Expression<Func<TEntity, List<TChildEntity>>> navigationPropertyExpression, Action<INavigationManyConfiguration<TEntity, TChildEntity>> navigationManyConfigurationAction)
When set to true, engine will use force comparison by type if children are inherited and children collection is not abstract
INavigationManyConfiguration<TEntity, TChildEntity> UseDerivedTypes(bool use = false)
Defines operations to perform when an insert is detected
IEntityConfiguration<TEntity> OnInsert(Action<IInsertConfiguration<TEntity>> insertConfigurationAction)
When an insert is detected, overwrite a property with a specific value
IInsertConfiguration<TEntity> SetValue<TMember>(Expression<Func<TEntity, TMember>> destinationMember, TMember value)
Defines operations to perform when an insert is detected. Properties specified in Values(...) will automatically be copied from new entity to existing one
IEntityConfiguration<TEntity> OnUpdate(Action<IUpdateConfiguration<TEntity>> updateConfigurationAction)
When an update is detected, overwrite a property with a specific value
IUpdateConfiguration<TEntity> SetValue<TMember>(Expression<Func<TEntity, TMember>> destinationMember, TMember value)
When an update is detected, specify additional properties to copy from new entity to existing one
IUpdateConfiguration<TEntity> CopyValues<TValue>(Expression<Func<TEntity, TValue>> copyValuesExpression)
Defines operations to perform when a delete is detected.
IEntityConfiguration<TEntity> OnDelete(Action<IDeleteConfiguration<TEntity>> deleteConfigurationAction)
When a delete is detected, overwrite property with a specific value
IDeleteConfiguration<TEntity> SetValue<TMember>(Expression<Func<TEntity, TMember>> destinationMember, TMember value)
Defines the IEqualityComparer to use when comparing entity of that type
IEntityConfiguration<TEntity> WithComparer<T>(IEqualityComparer<T> equalityComparer)
Defines additional criteria to force an update even if no update is detected using entity value(s).
IEntityConfiguration<TEntity> ForceUpdateIf(Action<IForceUpdateIfConfiguration<TEntity>> forceUpdateIfConfigurationAction)
Trigger an update when a nested entity is modified
IForceUpdateIfConfiguration<TEntity> NestedEntitiesModified()
Trigger an update when an equality condition is met
IForceUpdateIfConfiguration<TEntity> Equals<TMember>(Expression<Func<TEntity, TMember>> compareToMember, TMember compareToValue)
Defines entity properties which will not be found anywhere in the entity diff configuration. This will be used by ValidateIfEveryPropertiesAreReferenced
IEntityConfiguration<TEntity> Ignore<TIgnore>(Expression<Func<TEntity, TIgnore>> ignoreExpression)
Defines a no key entity, only update will be deteted for this kind of entity. Mandatory if no HasKey has been defined
IEntityConfiguration<TEntity> NoKey()
TEntity MergeSingle<TEntity>(TEntity existingEntity, TEntity newEntity)
TEntity MergeSingle<TEntity>(TEntity existingEntity, TEntity newEntity, IOperationListener operationListener)
TEntity MergeSingle<TEntity>(TEntity existingEntity, TEntity newEntity, Action<IMergeSingleConfiguration> mergeSingleConfigurationAction)
TEntity MergeSingle<TEntity>(TEntity existingEntity, TEntity newEntity, IOperationListener operationListener, Action<IMergeSingleConfiguration> mergeSingleConfigurationAction)
IEnumerable<TEntity> MergeMany<TEntity>(IEnumerable<TEntity> existingEntities, IEnumerable<TEntity> newEntities)
IEnumerable<TEntity> MergeMany<TEntity>(IEnumerable<TEntity> existingEntities, IEnumerable<TEntity> newEntities, IOperationListener operationListener)
IEnumerable<TEntity> MergeMany<TEntity>(IEnumerable<TEntity> existingEntities, IEnumerable<TEntity> newEntities, Action<IMergeManyConfiguration> mergeManyConfigurationAction)
IEnumerable<TEntity> MergeMany<TEntity>(IEnumerable<TEntity> existingEntities, IEnumerable<TEntity> newEntities, IOperationListener operationListener, Action<IMergeManyConfiguration> mergeManyConfigurationAction)
void CompareSingle<TEntity>(TEntity existingEntity, TEntity newEntity, IOperationListener operationListener);
void CompareSingle<TEntity>(TEntity existingEntity, TEntity newEntity, IOperationListener operationListener, Action<ICompareSingleConfiguration> compareSingleConfigurationAction);
void CompareMany<TEntity>(IEnumerable<TEntity> existingEntities, IEnumerable<TEntity> newEntities, IOperationListener operationListener);
void CompareMany<TEntity>(IEnumerable<TEntity> existingEntities, IEnumerable<TEntity> newEntities, IOperationListener operationListener, Action<ICompareManyConfiguration> compareManyConfigurationAction);
Defines minimum number of entries in collection to use hashtable (15 by default)
IMergeSingleConfiguration HashtableThreshold(int threshold = 15)
Force OnUpdate to be triggered if a nested entity has been modified even if current entity is not modified (false by default))
IMergeSingleConfiguration ForceOnUpdateWhenModificationsDetectedOnlyInNestedLevel(bool force = false)
Indicates whether to check for duplicate keys in entities or nested entities (true by default).
IMergeSingleConfiguration SetCheckDuplicateKeys(bool checkDuplicateKeys = true)
Defines minimum number of entries in collection to use hashtable (15 by default)
IMergeManyConfiguration HashtableThreshold(int threshold = 15)
Force OnUpdate to be triggered if a nested entity has been modified even if current entity is not modified
IMergeManyConfiguration ForceOnUpdateWhenModificationsDetectedOnlyInNestedLevel(bool force = false)
Indicates whether to check for duplicate keys in entities or nested entities (true by default).
IMergeManyConfiguration SetCheckDuplicateKeys(bool checkDuplicateKeys = true)
Defines minimum number of entries in collection to use hashtable (15 by default)
ICompareSingleConfiguration HashtableThreshold(int threshold = 15);
Indicates whether to check for duplicate keys in entities or nested entities (true by default).
ICompareSingleConfiguration SetCheckDuplicateKeys(bool checkDuplicateKeys = true)
Defines minimum number of entries in collection to use hashtable (15 by default)
ICompareManyConfiguration HashtableThreshold(int threshold = 15);
Indicates whether to check for duplicate keys in entities or nested entities (true by default).
ICompareManyConfiguration SetCheckDuplicateKeys(bool checkDuplicateKeys = true)
Defines a listener which will be called on every insert/update/delete detection
void OnDelete(string entityTypeName, Func<Dictionary<string, object>> getKeysFunc, Func<Dictionary<string, Dictionary<string, object>>> getNavigationParentKeysFunc);
void OnInsert(string entityTypeName, Func<Dictionary<string, object>> getKeysFunc, Func<Dictionary<string, Dictionary<string, object>>> getNavigationParentKeysFunc);
void OnUpdate(string entityTypeName, string propertyName, Func<Dictionary<string, object>> getKeysFunc, Func<object> getOriginalValueFunc, Func<Dictionary<string, Dictionary<string, object>>> getNavigationParentKeysFunc);
Create an entity configuration
IEntityConfiguration<TEntity> ConfigureEntity<TEntity>()
Add diff profile
IDeepDiffConfiguration AddProfile<TProfile>()
IDeepDiffConfiguration AddProfile(DiffProfile diffProfile)
Scan assemblies and add diff profile found in those assemblies
IDeepDiffConfiguration AddProfiles(params Assembly[] assembliesToScan)
Create DeepDiff engine (also validate configuration)
IDeepDiff CreateDeepDiff()
Validate entity configurations
void ValidateConfiguration()
Check if every properties found in configured entities are used in configuration, use Ignore to add properties to ignore in this validation
void ValidateIfEveryPropertiesAreReferenced()