private void InferRelationHistories(ObjectContext ctx) { List <RelationWrapper> relations = new List <RelationWrapper>(this.Relations); // The first time scan for those have corresponding change action recorded. for (int i = 0; i < relations.Count; i++) { RelationWrapper relation = relations[i]; // Look into the relationship from two direction, which could provide a uniform way. foreach (RelationEndWrapper end in relation.GetRelationEnds()) { object entityRaw; if (ctx.TryGetObjectByKey(end.SourceKey, out entityRaw)) { IWithHistory entity = entityRaw as IWithHistory; if (entity != null) { ChangeEntry change = entity.CollectionChanges.ChangedEntries.Find(x => this.AssertChangeMatch(x, relation, end.TargetAssociationEnd.Name)); // Find out recorded change action matches current relation.. if (change != null) { // Remove from list. let the rest of list keep clean as not developed. relations.Remove(relation); i--; HistoryPreparation hp = new HistoryPreparation(change); // Prepare basic change history. RawChangeHistory rch = new RawChangeHistory(change.Source, change.Monitor.PropertyID, change.Target, change.Action); hp.Relations.Add(rch); // Start prepare extra history. if (end.TargetAssociationEnd.RelationshipMultiplicity == RelationshipMultiplicity.Many) { // This is n to n association. if (change.Target is IWithHistory) { // Whether this EntityType at opposite requires history and has corresponding property in metadata. EntityPropertyInfo pi = MetadataManager.GetEntityProperty(change.Monitor.PropertyID, true); if (pi.OppositePropertyReference.GetValueAutoLoaded() != null) { RawChangeHistory rch1 = new RawChangeHistory(change.Target, pi.OppositePropertyReference.GetValueAutoLoaded().ID, change.Source, change.Action); hp.Relations.Add(rch1); } } } else { // This is n to 0..1 association. if (change.Action == CollectionChangeAction.Add) { EntityPropertyInfo pi = MetadataManager.GetEntityProperty(change.Monitor.PropertyID, true); RelationEndWrapper end1 = null; // Find out a deleted relation which is possibly occur because of current added one. RelationWrapper relation1 = relations.Find(x => (x.State == EntityState.Deleted) && String.Equals(x.Definition.Name, relation.Definition.Name) && this.AssertRelationSameTarget(x, relation, EntityState.Deleted, end, out end1)); if (relation1 != null) { if (entity.CollectionChanges.ChangedEntries.Find(x => this.AssertChangeMatch(x, relation, end.TargetAssociationEnd.Name)) == null) { // No recorded change matches this relation. so it did really occur because of current added one. if (relations.IndexOf(relation1) <= i) { i--; } relations.Remove(relation1); RawChangeHistory rch1 = new RawChangeHistory((EntityObject)ctx.GetObjectByKey(end1.SourceKey), change.Monitor.PropertyID, change.Target, CollectionChangeAction.Remove); hp.Relations.Add(rch1); } } if ((change.Target is IWithHistory) && (pi.OppositePropertyReference.GetValueAutoLoaded() != null)) { hp.Entities.Add(change.Target); } } } entity.CollectionChanges.CachedHistories.Add(hp); } } } } } // Second time scan for those relation changes launched by assigning on entity. for (int i = 0; i < relations.Count; i++) { RelationWrapper relation = relations[i]; foreach (RelationEndWrapper end in relation.GetRelationEnds()) { if (end.TargetAssociationEnd.RelationshipMultiplicity != RelationshipMultiplicity.Many) { EntityWrapper er = this.Entities.Find(x => x.Key.Equals(end.SourceKey)); IWithHistory entity = null; if (er == null) { // Normal properties of this entity have not been changed. so it should be added to entity history list. entity = ctx.GetObjectByKey(end.SourceKey) as IWithHistory; if (entity != null) { this.AddChangedEntity(EntityState.Unchanged, (EntityObject)entity); } } else { entity = er.Entity as IWithHistory; } // Prepare relation histories. } } } }
private bool AssertRelationSameTarget(RelationWrapper relation, RelationWrapper targetRelation, EntityState state, RelationEndWrapper targetEnd, out RelationEndWrapper correspondingEnd) { correspondingEnd = null; bool ret = (relation.State == state) && String.Equals(relation.Definition.Name, targetRelation.Definition.Name); foreach (RelationEndWrapper end in relation.GetRelationEnds()) { ret = String.Equals(end.TargetAssociationEnd.Name, correspondingEnd.TargetAssociationEnd.Name) && end.TargetKey.Equals(targetEnd.TargetKey); if (ret) { correspondingEnd = end; break; } } return(ret); }