protected virtual void Merge(Object obj, Object clone, MergeHandle handle) { IEntityMetaDataProvider entityMetaDataProvider = this.EntityMetaDataProvider; IEntityMetaData metaData = ((IEntityMetaDataHolder)obj).Get__EntityMetaData(); bool fieldBasedMergeActive = handle.FieldBasedMergeActive; bool oneChangeOccured = false; try { RelationMember[] relationMembers = metaData.RelationMembers; if (relationMembers.Length > 0) { IObjRefContainer vhc = (IObjRefContainer)obj; for (int relationIndex = relationMembers.Length; relationIndex-- > 0;) { RelationMember relationMember = relationMembers[relationIndex]; if (!metaData.IsMergeRelevant(relationMember)) { continue; } if (ValueHolderState.INIT != vhc.Get__State(relationIndex)) { // v2 valueholder is not initialized. so a change is impossible continue; } Object objMember = relationMember.GetValue(obj, false); Object cloneMember = relationMember.GetValue(clone, false); if (objMember is IDataObject && !((IDataObject)objMember).HasPendingChanges) { IEntityMetaData relationMetaData = entityMetaDataProvider.GetMetaData(relationMember.RealType); if (EqualsReferenceOrId(objMember, cloneMember, handle, relationMetaData)) { continue; } } IEntityMetaData childMetaData = entityMetaDataProvider.GetMetaData(relationMember.ElementType); if (IsMemberModified(objMember, cloneMember, handle, childMetaData)) { oneChangeOccured = true; AddOriModification(obj, relationMember.Name, objMember, cloneMember, handle); } } } if (fieldBasedMergeActive) { MergePrimitivesFieldBased(metaData, obj, clone, handle); return; } bool additionalRound; do { additionalRound = !oneChangeOccured; foreach (PrimitiveMember primitiveMember in metaData.PrimitiveMembers) { if (!metaData.IsMergeRelevant(primitiveMember)) { continue; } Object objValue = primitiveMember.GetValue(obj, true); if (oneChangeOccured) { AddModification(obj, primitiveMember.Name, primitiveMember.ElementType, objValue, null, handle); continue; } Object cloneValue = primitiveMember.GetValue(clone, true); if (!ArePrimitivesEqual(metaData, primitiveMember, objValue, cloneValue, handle)) { oneChangeOccured = true; break; } } }while (additionalRound && oneChangeOccured); } finally { PrimitiveMember versionMember = metaData.VersionMember; if (oneChangeOccured && versionMember != null) { // Check for early optimistic locking (Another, later level is directly on persistence layer) Object versionToMerge = versionMember.GetValue(obj, true); Object currentVersion = versionMember.GetValue(clone, true); int compareResult = ((IComparable)versionToMerge).CompareTo(currentVersion); if (ExactVersionForOptimisticLockingRequired ? compareResult != 0 : compareResult < 0) { throw new OptimisticLockException(currentVersion, versionToMerge, obj); } } } }
public virtual void DataChanged(IDataChange dataChange, DateTime dispatchTime, long sequenceId) { dataChange = dataChange.Derive(InterestedEntityTypes); if (dataChange.IsEmpty) { return; } ISet <Object> directObjectsToDelete = null; ISet <Type> requestedTypes = new HashSet <Type>(); IDictionary <Type, IEntityMetaData> typeToMetaDataDict = new Dictionary <Type, IEntityMetaData>(); GuiThreadHelper.InvokeInGuiAndWait(delegate() { IList <T> entities = Model.Objects; for (int i = entities.Count; i-- > 0;) { Object entity = entities[i]; requestedTypes.Add(entity.GetType()); } }); IList <IDataChangeEntry> dataChangeEntries = dataChange.Inserts; for (int a = dataChangeEntries.Count; a-- > 0;) { requestedTypes.Add(dataChangeEntries[a].EntityType); } dataChangeEntries = dataChange.Updates; for (int a = dataChangeEntries.Count; a-- > 0;) { requestedTypes.Add(dataChangeEntries[a].EntityType); } dataChangeEntries = dataChange.Deletes; for (int a = dataChangeEntries.Count; a-- > 0;) { requestedTypes.Add(dataChangeEntries[a].EntityType); } IList <IEntityMetaData> metaDatas = EntityMetaDataProvider.GetMetaData(ListUtil.ToList(requestedTypes)); foreach (IEntityMetaData metaData in metaDatas) { typeToMetaDataDict[metaData.EntityType] = metaData; } bool consistsOnlyOfDirectDeletes = false; if (dataChange.Deletes.Count > 0) { consistsOnlyOfDirectDeletes = true; foreach (IDataChangeEntry deleteEntry in dataChange.Deletes) { if (deleteEntry is DirectDataChangeEntry) { if (directObjectsToDelete == null) { directObjectsToDelete = new IdentityHashSet <Object>(); } directObjectsToDelete.Add(((DirectDataChangeEntry)deleteEntry).Entry); } else { consistsOnlyOfDirectDeletes = false; } } } IList <T> interestingEntities = null; Object[] contextInformation = GetContextInformation(); IFilterDescriptor filterDescriptor = GetFilterDescriptor(); IList <ISortDescriptor> sortDescriptors = GetSortDescriptors(); IPagingRequest pagingRequest = GetPagingRequest(); IPagingResponse pagingResponse = null; List <IDataChangeEntry> modifiedEntries = new List <IDataChangeEntry>(); modifiedEntries.AddRange(dataChange.All); if (!consistsOnlyOfDirectDeletes) { interestingEntities = CacheContext.ExecuteWithCache(CacheProvider.GetCurrentCache(), delegate() { ConfigureCacheWithEagerLoads(Cache); if (Refresher is IPagingRefresher <T> ) { interestingEntities = new List <T>(); pagingResponse = ((IPagingRefresher <T>)Refresher).Refresh(modifiedEntries, filterDescriptor, sortDescriptors, pagingRequest, contextInformation); foreach (Object obj in pagingResponse.Result) { interestingEntities.Add((T)obj); } return(interestingEntities); } else { if (filterDescriptor != null || sortDescriptors != null) { contextInformation = new Object[2]; contextInformation[0] = filterDescriptor; contextInformation[1] = sortDescriptors; } return(((IRefresher <T>)Refresher).Refresh(modifiedEntries, contextInformation)); } }); } GuiThreadHelper.InvokeInGuiAndWait(delegate() { IList <T> entities = Model.Objects; ISet <T> entitiesToAdd = null; ISet <T> entitiesToRemove = null; IDictionary <T, T> entitiesToReplace = null; IDictionary <IObjRef, T> oldObjRefToOldEntityMap = null; bool mergeModel = false; if (interestingEntities != null && interestingEntities.Count > 0) { entitiesToAdd = new IdentityHashSet <T>(interestingEntities); entitiesToRemove = new IdentityHashSet <T>(entities); entitiesToReplace = new IdentityDictionary <T, T>(); oldObjRefToOldEntityMap = new Dictionary <IObjRef, T>(); mergeModel = true; } for (int i = entities.Count; i-- > 0;) { T oldEntity = entities[i]; if (directObjectsToDelete != null && directObjectsToDelete.Contains(oldEntity)) { if (entitiesToRemove != null) { entitiesToRemove.Remove(oldEntity); } Model.RemoveAt(i); continue; } Type oldEntityType = ProxyHelper.GetRealType(oldEntity.GetType()); PrimitiveMember idMember = typeToMetaDataDict[oldEntityType].IdMember; Object oldEntityId = idMember.GetValue(oldEntity, false); if (oldEntityId == null) { if (entitiesToRemove != null) { entitiesToRemove.Remove(oldEntity); } // Unpersisted object. This object should not be removed // only because of a background DCE continue; } bool entryRemoved = false; foreach (IDataChangeEntry deleteEntry in dataChange.Deletes) { if (deleteEntry is DirectDataChangeEntry) { continue; } Object id = deleteEntry.Id; if (!EqualsItems(oldEntityType, oldEntityId, deleteEntry.EntityType, id)) { continue; } if (entitiesToRemove != null) { entitiesToRemove.Remove(oldEntity); } Model.RemoveAt(i); entryRemoved = true; break; } if (entryRemoved) { continue; } if (mergeModel) { IObjRef oldObjRef = new ObjRef(oldEntityType, ObjRef.PRIMARY_KEY_INDEX, oldEntityId, null); T existingOldEntity = DictionaryExtension.ValueOrDefault(oldObjRefToOldEntityMap, oldObjRef); if (existingOldEntity == null) { oldObjRefToOldEntityMap.Add(oldObjRef, oldEntity); } else if (!Object.ReferenceEquals(existingOldEntity, oldEntity)) { // Force duplicate key exception oldObjRefToOldEntityMap.Add(oldObjRef, oldEntity); } } } if (oldObjRefToOldEntityMap != null && oldObjRefToOldEntityMap.Count > 0) { IDictionary <IObjRef, T> newObjRefToNewEntityMap = new Dictionary <IObjRef, T>(); for (int a = interestingEntities.Count; a-- > 0;) { T newEntity = interestingEntities[a]; Type newEntityType = ProxyHelper.GetRealType(newEntity.GetType()); PrimitiveMember idMember = typeToMetaDataDict[newEntityType].IdMember; Object newEntityId = idMember.GetValue(newEntity, false); IObjRef newObjRef = new ObjRef(newEntityType, ObjRef.PRIMARY_KEY_INDEX, newEntityId, null); newObjRefToNewEntityMap.Add(newObjRef, newEntity); } DictionaryExtension.Loop(oldObjRefToOldEntityMap, delegate(IObjRef objRef, T oldEntity) { T newEntity = DictionaryExtension.ValueOrDefault(newObjRefToNewEntityMap, objRef); if (newEntity == null) { // Nothing to do if current oldEntity has no corresponding newEntity return; } entitiesToAdd.Remove(newEntity); if (!Object.ReferenceEquals(oldEntity, newEntity) && (dataChange.IsLocalSource || !(oldEntity is IDataObject) || !((IDataObject)oldEntity).ToBeUpdated)) { entitiesToReplace[oldEntity] = newEntity; } entitiesToRemove.Remove(oldEntity); }); } if (mergeModel) { for (int a = entities.Count; a-- > 0;) { T item = entities[a]; if (entitiesToRemove.Contains(item)) { Model.RemoveAt(a); continue; } T replacingItem = DictionaryExtension.ValueOrDefault(entitiesToReplace, item); if (replacingItem != null) { Model.Replace(a, replacingItem); continue; } } IEnumerator <T> enumerator = entitiesToAdd.GetEnumerator(); while (enumerator.MoveNext()) { T entityToAdd = enumerator.Current; Model.Add(entityToAdd); } if (hasPagedViewModel) { UpdatePagingInformation(pagingResponse); } UpdateAfterDCE(); } }); }
protected void ApplyChangesToOriginalsIntern(ICUDResult cudResult, IOriCollection oriCollection, ICache cache) { ICacheModification cacheModification = this.CacheModification; IConversionHelper conversionHelper = this.ConversionHelper; IEntityMetaDataProvider entityMetaDataProvider = this.EntityMetaDataProvider; IList <Object> originalRefs = cudResult.GetOriginalRefs(); IList <IObjRef> allChangeORIs = oriCollection.AllChangeORIs; String[] allChangedBy = oriCollection.AllChangedBy; long[] allChangedOn = oriCollection.AllChangedOn; String singleChangedBy = oriCollection.ChangedBy; long? singleChangedOn = oriCollection.ChangedOn; bool newInstanceOnCall = CacheProvider.IsNewInstanceOnCall; IList <Object> validObjects = new List <Object>(originalRefs.Count); bool oldCacheModificationValue = CacheModification.Active; CacheModification.Active = true; try { for (int a = originalRefs.Count; a-- > 0;) { Object originalRef = originalRefs[a]; IObjRef ori = allChangeORIs[a]; if (originalRef == null) { // Object has been deleted by cascade delete contraints on server merge or simply a "not specified" original ref continue; } if (originalRef is IObjRef) { continue; } long? changedOn = allChangedOn != null ? allChangedOn[a] : singleChangedOn; String changedBy = allChangedBy != null ? allChangedBy[a] : singleChangedBy; IEntityMetaData metaData = ((IEntityMetaDataHolder)originalRef).Get__EntityMetaData(); PrimitiveMember versionMember = metaData.VersionMember; PrimitiveMember keyMember = metaData.IdMember; PrimitiveMember onMember, byMember; if (keyMember.GetValue(originalRef, false) == null) { onMember = metaData.CreatedOnMember; byMember = metaData.CreatedByMember; } else { onMember = metaData.UpdatedOnMember; byMember = metaData.UpdatedByMember; } if (onMember != null && changedOn != null) { Object createdOn = ConversionHelper.ConvertValueToType(onMember.ElementType, changedOn); onMember.SetValue(originalRef, createdOn); } if (byMember != null && changedBy != null) { Object createdBy = ConversionHelper.ConvertValueToType(byMember.ElementType, changedBy); byMember.SetValue(originalRef, createdBy); } if (ori == null) { keyMember.SetValue(originalRef, null); if (versionMember != null) { versionMember.SetValue(originalRef, null); } if (originalRef is IDataObject) { ((IDataObject)originalRef).ToBeUpdated = false; ((IDataObject)originalRef).ToBeDeleted = false; } continue; // Object has been deleted directly } keyMember.SetValue(originalRef, ConversionHelper.ConvertValueToType(keyMember.RealType, ori.Id)); if (versionMember != null) { if (AlwaysUpdateVersionInChangedEntities) { versionMember.SetValue(originalRef, ConversionHelper.ConvertValueToType(versionMember.RealType, ori.Version)); } else { // We INTENTIONALLY do NOT set the version and let it on its old value, to force the following DCE to refresh the cached object with 'real' data // If we set the version here to the ori.getVersion(), the DCE will 'see' a already valid object - but is IS NOT valid // because it may not contain bi-directional information which can only be resolved by reloading the object from persistence layer //versionMember.SetValue(originalRef, null); } } if (originalRef is IDataObject) { ((IDataObject)originalRef).ToBeUpdated = false; ((IDataObject)originalRef).ToBeDeleted = false; } validObjects.Add(originalRef); } PutInstancesToCurrentCache(validObjects, cache); } finally { CacheModification.Active = oldCacheModificationValue; } }
public ICollection <T> ExtractTargetEntities <T, S>(IEnumerable <S> sourceEntities, String sourceToTargetEntityPropertyPath) { // Einen Accessor ermitteln, der die gesamte Hierachie aus dem propertyPath („A.B.C“) selbstständig traversiert Member member = MemberTypeProvider.GetMember(typeof(S), sourceToTargetEntityPropertyPath); // MetaDaten der Ziel-Entity ermitteln, da wir (generisch) den PK brauchen, um damit ein DISTINCT-Behavior durch eine Map als Zwischenstruktur zu // erreichen IEntityMetaData targetMetaData = EntityMetaDataProvider.GetMetaData(member.ElementType); PrimitiveMember targetIdMember = targetMetaData.IdMember; // Damit bei der Traversion keine Initialisierungen mit DB-Roundtrips entstehen, machen wir vorher eine Prefetch passend zum PropertyPath auf allen // übergebenen Quell-Entities // Dadurch entstehen maximal 2 gebatchte SELECTs, egal wie groß die Liste ist IPrefetchHandle prefetch = CreatePrefetch().Add(typeof(S), sourceToTargetEntityPropertyPath).Build(); // Speichere das State-Result unbenutzt - wichtig für concurrent GC Aktivitäten, um Verluste an Entity-Referenzen zu verhindern IPrefetchState state = prefetch.Prefetch(sourceEntities); IDictionary <Object, T> targetDistinctMap = new Dictionary <Object, T>(); // Danach traversieren, wobei wir jetzt wissen, dass uns das keine Roundtrips kostet foreach (S sourceEntity in sourceEntities) { if (sourceEntity == null) { continue; } Object targetEntities = member.GetValue(sourceEntity); if (targetEntities == null) { continue; } // Ergebnismenge flexibel (bei *-To-Many) verarbeiten oder so lassen (bei *-To-One) if (targetEntities is IEnumerable) { foreach (Object targetEntity in (IEnumerable)targetEntities) { if (targetEntity == null) { continue; } Object targetId = targetIdMember.GetValue(targetEntity); if (targetId == null) { // Falls die Entity keine ID hat, speichern wir sie ausnahmsweise selbst als Key targetId = targetEntity; } targetDistinctMap[targetId] = (T)targetEntity; } } else { Object targetId = targetIdMember.GetValue(targetEntities); if (targetId == null) { // Falls die Entity keine ID hat, speichern wir sie ausnahmsweise selbst als Key targetId = targetEntities; } targetDistinctMap[targetId] = (T)targetEntities; } } // Alle values sind unsere eindeutigen Target Entities ohne Duplikate return(targetDistinctMap.Values); }