Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DLinq / Dlinq / ChangeProcessor.cs / 1305376 / ChangeProcessor.cs
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Diagnostics; namespace System.Data.Linq { using System.Data.Linq.Mapping; using System.Data.Linq.Provider; ////// Describes the type of change the entity will undergo when submitted to the database. /// public enum ChangeAction { ////// The entity will not be submitted. /// None = 0, ////// The entity will be deleted. /// Delete, ////// The entity will be inserted. /// Insert, ////// The entity will be updated. /// Update } internal class ChangeProcessor { CommonDataServices services; DataContext context; ChangeTracker tracker; ChangeDirector changeDirector; EdgeMap currentParentEdges; EdgeMap originalChildEdges; ReferenceMap originalChildReferences; internal ChangeProcessor(CommonDataServices services, DataContext context) { this.services = services; this.context = context; this.tracker = services.ChangeTracker; this.changeDirector = services.ChangeDirector; this.currentParentEdges = new EdgeMap(); this.originalChildEdges = new EdgeMap(); this.originalChildReferences = new ReferenceMap(); } internal void SubmitChanges(ConflictMode failureMode) { this.TrackUntrackedObjects(); // Must apply inferred deletions only after any untracked objects // are tracked this.ApplyInferredDeletions(); this.BuildEdgeMaps(); var list = this.GetOrderedList(); ValidateAll(list); int numUpdatesAttempted = 0; ChangeConflictSession conflictSession = new ChangeConflictSession(this.context); Listconflicts = new List (); List deletedItems = new List (); List insertedItems = new List (); List syncDependentItems = new List (); foreach (TrackedObject item in list) { try { if (item.IsNew) { if (item.SynchDependentData()) { syncDependentItems.Add(item); } changeDirector.Insert(item); // store all inserted items for post processing insertedItems.Add(item); } else if (item.IsDeleted) { // Delete returns 1 if the delete was successfull, 0 if the row exists // but wasn't deleted due to an OC conflict, or -1 if the row was // deleted by another context (no OC conflict in this case) numUpdatesAttempted++; int ret = changeDirector.Delete(item); if (ret == 0) { conflicts.Add(new ObjectChangeConflict(conflictSession, item, false)); } else { // store all deleted items for post processing deletedItems.Add(item); } } else if (item.IsPossiblyModified) { if (item.SynchDependentData()) { syncDependentItems.Add(item); } if (item.IsModified) { CheckForInvalidChanges(item); numUpdatesAttempted++; if (changeDirector.Update(item) <= 0) { conflicts.Add(new ObjectChangeConflict(conflictSession, item)); } } } } catch (ChangeConflictException) { conflicts.Add(new ObjectChangeConflict(conflictSession, item)); } if (conflicts.Count > 0 && failureMode == ConflictMode.FailOnFirstConflict) { break; } } // if we have accumulated any failed updates, throw the exception now if (conflicts.Count > 0) { // First we need to rollback any value that have already been auto-[....]'d, since the values are no longer valid on the server changeDirector.RollbackAutoSync(); // Also rollback any dependent items that were [....]'d, since their parent values may have been rolled back foreach (TrackedObject syncDependentItem in syncDependentItems) { Debug.Assert(syncDependentItem.IsNew || syncDependentItem.IsPossiblyModified, "SynchDependent data should only be rolled back for new and modified objects."); syncDependentItem.SynchDependentData(); } this.context.ChangeConflicts.Fill(conflicts); throw CreateChangeConflictException(numUpdatesAttempted, conflicts.Count); } else { // No conflicts occurred, so we don't need to save the rollback values anymore changeDirector.ClearAutoSyncRollback(); } // Only after all updates have been sucessfully processed do we want to make // post processing modifications to the objects and/or cache state. PostProcessUpdates(insertedItems, deletedItems); } private void PostProcessUpdates(List insertedItems, List deletedItems) { // perform post delete processing foreach (TrackedObject deletedItem in deletedItems) { // remove deleted item from identity cache this.services.RemoveCachedObjectLike(deletedItem.Type, deletedItem.Original); ClearForeignKeyReferences(deletedItem); } // perform post insert processing foreach (TrackedObject insertedItem in insertedItems) { object lookup = this.services.InsertLookupCachedObject(insertedItem.Type, insertedItem.Current); if (lookup != insertedItem.Current) { throw new DuplicateKeyException(insertedItem.Current, Strings.DatabaseGeneratedAlreadyExistingKey); } insertedItem.InitializeDeferredLoaders(); } } /// /// Clears out the foreign key values and parent object references for deleted objects on the child side of a relationship. /// For bi-directional relationships, also performs the following fixup: /// - for 1:N we remove the deleted entity from the opposite EntitySet or collection /// - for 1:1 we null out the back reference /// private void ClearForeignKeyReferences(TrackedObject to) { Debug.Assert(to.IsDeleted, "Foreign key reference cleanup should only happen on Deleted objects."); foreach (MetaAssociation assoc in to.Type.Associations) { if (assoc.IsForeignKey) { // If there is a member on the other side referring back to us (i.e. this is a bi-directional relationship), // we want to do a cache lookup to find the other side, then will remove ourselves from that collection. // This cache lookup is only possible if the other key is the primary key, since that is the only way items can be found in the cache. if (assoc.OtherMember != null && assoc.OtherKeyIsPrimaryKey) { Debug.Assert(assoc.OtherMember.IsAssociation, "OtherMember of the association is expected to also be an association."); // Search the cache for the target of the association, since // it might not be loaded on the object being deleted, and we // don't want to force a load. object[] keyValues = CommonDataServices.GetForeignKeyValues(assoc, to.Current); object cached = this.services.IdentityManager.Find(assoc.OtherType, keyValues); if (cached != null) { if (assoc.OtherMember.Association.IsMany) { // Note that going through the IList interface handles // EntitySet as well as POCO collections that implement IList // and are not FixedSize. System.Collections.IList collection = assoc.OtherMember.MemberAccessor.GetBoxedValue(cached) as System.Collections.IList; if (collection != null && !collection.IsFixedSize) { collection.Remove(to.Current); // Explicitly clear the foreign key values and parent object reference ClearForeignKeysHelper(assoc, to.Current); } } else { // Null out the other association. Since this is a 1:1 association, // we're not concerned here with causing a deferred load, since the // target is already cached (since we're deleting it). assoc.OtherMember.MemberAccessor.SetBoxedValue(ref cached, null); // Explicitly clear the foreign key values and parent object reference ClearForeignKeysHelper(assoc, to.Current); } } // else the item was not found in the cache, so there is no fixup that has to be done // We are explicitly not calling ClearForeignKeysHelper because it breaks existing shipped behavior and we want to maintain backward compatibility } else { // This is a unidirectional relationship or we have no way to look up the other side in the cache, so just clear our own side ClearForeignKeysHelper(assoc, to.Current); } } // else this is not the 1-side (foreign key) of the relationship, so there is nothing for us to do } } // Ensure the the member and foreign keys are nulled so that after trackedInstance is deleted, // the object does not appear to be associated with the other side anymore. This prevents the deleted object // from referencing objects still in the cache, but also will prevent the related object from being implicitly loaded private static void ClearForeignKeysHelper(MetaAssociation assoc, object trackedInstance) { Debug.Assert(assoc.IsForeignKey, "Foreign key clearing should only happen on foreign key side of the association."); Debug.Assert(assoc.ThisMember.IsAssociation, "Expected ThisMember of an association to always be an association."); // If this member is one of our deferred loaders, and it does not already have a value, explicitly set the deferred source to // null so that when we set the association member itself to null later, it doesn't trigger an implicit load. // This is only necessary if the value has not already been assigned or set, because otherwise we won't implicitly load anyway when the member is accessed. MetaDataMember thisMember = assoc.ThisMember; if (thisMember.IsDeferred && !(thisMember.StorageAccessor.HasAssignedValue(trackedInstance) || thisMember.StorageAccessor.HasLoadedValue(trackedInstance))) { // If this is a deferred member, set the value directly in the deferred accessor instead of going // through the normal member accessor, so that we don't trigger an implicit load. thisMember.DeferredSourceAccessor.SetBoxedValue(ref trackedInstance, null); } // Notify the object that the relationship should be considered deleted. // This allows the object to do its own fixup even when we can't do it automatically. thisMember.MemberAccessor.SetBoxedValue(ref trackedInstance, null); // Also set the foreign key values to null if possible for (int i = 0, n = assoc.ThisKey.Count; i < n; i++) { MetaDataMember thisKey = assoc.ThisKey[i]; if (thisKey.CanBeNull) { thisKey.StorageAccessor.SetBoxedValue(ref trackedInstance, null); } } } private static void ValidateAll(IEnumerablelist) { foreach (var item in list) { if (item.IsNew) { item.SynchDependentData(); if (item.Type.HasAnyValidateMethod) { SendOnValidate(item.Type, item, ChangeAction.Insert); } } else if (item.IsDeleted) { if (item.Type.HasAnyValidateMethod) { SendOnValidate(item.Type, item, ChangeAction.Delete); } } else if (item.IsPossiblyModified) { item.SynchDependentData(); if (item.IsModified && item.Type.HasAnyValidateMethod) { SendOnValidate(item.Type, item, ChangeAction.Update); } } } } private static void SendOnValidate(MetaType type, TrackedObject item, ChangeAction changeAction) { if (type != null) { SendOnValidate(type.InheritanceBase, item, changeAction); if (type.OnValidateMethod != null) { try { type.OnValidateMethod.Invoke(item.Current, new object[] { changeAction }); } catch (TargetInvocationException tie) { if (tie.InnerException != null) { throw tie.InnerException; } throw; } } } } internal string GetChangeText() { this.ObserveUntrackedObjects(); // Must apply inferred deletions only after any untracked objects // are tracked this.ApplyInferredDeletions(); this.BuildEdgeMaps(); // append change text only StringBuilder changeText = new StringBuilder(); foreach (TrackedObject item in this.GetOrderedList()) { if (item.IsNew) { item.SynchDependentData(); changeDirector.AppendInsertText(item, changeText); } else if (item.IsDeleted) { changeDirector.AppendDeleteText(item, changeText); } else if (item.IsPossiblyModified) { item.SynchDependentData(); if (item.IsModified) { changeDirector.AppendUpdateText(item, changeText); } } } return changeText.ToString(); } internal ChangeSet GetChangeSet() { List
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- HtmlShim.cs
- AppDomainAttributes.cs
- MaskedTextBoxDesignerActionList.cs
- ArrayConverter.cs
- PageContentCollection.cs
- Byte.cs
- ContainerVisual.cs
- odbcmetadatafactory.cs
- HostExecutionContextManager.cs
- CharacterHit.cs
- StorageScalarPropertyMapping.cs
- X509CertificateCollection.cs
- ProviderCommandInfoUtils.cs
- ObjectAnimationBase.cs
- MessageQueuePermissionEntry.cs
- PointLightBase.cs
- LineUtil.cs
- FixedSOMImage.cs
- HtmlInputFile.cs
- FlowDocumentReaderAutomationPeer.cs
- ObjectStateEntryBaseUpdatableDataRecord.cs
- HTTPNotFoundHandler.cs
- FileNameEditor.cs
- URLAttribute.cs
- DocumentPageViewAutomationPeer.cs
- SpnEndpointIdentityExtension.cs
- XPathScanner.cs
- QueryContinueDragEventArgs.cs
- AssemblyInfo.cs
- SyndicationDeserializer.cs
- HelpProvider.cs
- Activity.cs
- RightsManagementEncryptedStream.cs
- Rect3D.cs
- SqlAliaser.cs
- SimpleWorkerRequest.cs
- InternalMappingException.cs
- MergeEnumerator.cs
- Base64Decoder.cs
- XmlDataLoader.cs
- WSSecurityOneDotOneReceiveSecurityHeader.cs
- StatusBarAutomationPeer.cs
- DigitalSignature.cs
- QueueProcessor.cs
- ChtmlSelectionListAdapter.cs
- TemplateBindingExtensionConverter.cs
- MemberAccessException.cs
- DataGridTextBoxColumn.cs
- SpellerStatusTable.cs
- TreeViewDesigner.cs
- Typeface.cs
- DynamicDataManager.cs
- XmlCompatibilityReader.cs
- ParameterElement.cs
- PartialCachingControl.cs
- EventLogEntryCollection.cs
- Deflater.cs
- Base64Decoder.cs
- OleDbConnection.cs
- CallbackHandler.cs
- SiteIdentityPermission.cs
- ExtendedProtectionPolicyTypeConverter.cs
- HideDisabledControlAdapter.cs
- ProcessThreadDesigner.cs
- XmlReader.cs
- PeerNameRegistration.cs
- SHA384CryptoServiceProvider.cs
- DataServiceQueryProvider.cs
- ProfileService.cs
- ToolZoneDesigner.cs
- BindingOperations.cs
- SmiGettersStream.cs
- Comparer.cs
- SqlTriggerAttribute.cs
- SqlRemoveConstantOrderBy.cs
- _SSPIWrapper.cs
- ClientRuntimeConfig.cs
- QilGeneratorEnv.cs
- TraceSwitch.cs
- MapPathBasedVirtualPathProvider.cs
- Rect.cs
- GZipDecoder.cs
- WsdlBuildProvider.cs
- PointConverter.cs
- InlineUIContainer.cs
- EdmEntityTypeAttribute.cs
- ClientRuntimeConfig.cs
- SoapExtension.cs
- RequestSecurityToken.cs
- GenericTypeParameterBuilder.cs
- SubtreeProcessor.cs
- RelatedView.cs
- GridLengthConverter.cs
- SchemaMerger.cs
- Transform.cs
- NumericExpr.cs
- ImageMap.cs
- DataGridParentRows.cs
- ViewPort3D.cs
- PathData.cs