private void AssertCoreHistoryCommon(CoreHistory hist, IWithHistory ent, HistoryActionType action, DateTime dtLower, DateTime dtUpper)
 {
     Assert.AreEqual(InformationCenter.States.TransactionInfo.ApplicationTransactionIdentifier.Value, hist.ApplicationTransactionID);
     Assert.AreEqual(ent.EntityTypeID, hist.EntityTypeID);
     Assert.AreEqual(ent.EntityID, hist.EntityID);
     Assert.AreEqual(action, hist.ActionType);
     Assert.Greater(TimeSpan.FromSeconds(1), dtLower.Subtract(hist.Timestamp));
     Assert.Greater(TimeSpan.FromSeconds(1), hist.Timestamp.Subtract(dtUpper));
 }
        public ChangeMonitorBase(IWithHistory entity, int propertyID, RelatedEnd collection)
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }
            if (collection == null)
            {
                throw new ArgumentNullException("collection");
            }

            this.Entity     = entity;
            this.PropertyID = propertyID;
            this.TargetEnd  = collection;

            this.TargetEnd.AssociationChanged += new CollectionChangeEventHandler(this.CollectionAssociationChanged);
        }
        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.
                    }
                }
            }
        }