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
- ZipIOCentralDirectoryFileHeader.cs
- RightsManagementSuppressedStream.cs
- TextEditorSelection.cs
- PointHitTestParameters.cs
- TableLayoutPanelCellPosition.cs
- ListViewHitTestInfo.cs
- ClientTargetCollection.cs
- XmlSerializerSection.cs
- OdbcStatementHandle.cs
- XmlTextReader.cs
- MouseButton.cs
- SendActivity.cs
- DataGridViewCell.cs
- DataKey.cs
- SpellerError.cs
- ServiceMemoryGates.cs
- ServerIdentity.cs
- CurrentChangedEventManager.cs
- DataGridItem.cs
- Brush.cs
- X509Chain.cs
- DebugController.cs
- FixedSOMImage.cs
- BasicHttpMessageCredentialType.cs
- TextOutput.cs
- PolicyLevel.cs
- Events.cs
- TextWriterTraceListener.cs
- CompilerScope.cs
- FactoryId.cs
- UiaCoreApi.cs
- ItemContainerGenerator.cs
- ActivitySurrogateSelector.cs
- UIntPtr.cs
- XmlBindingWorker.cs
- MgmtConfigurationRecord.cs
- StylusLogic.cs
- HtmlEmptyTagControlBuilder.cs
- SafeHandles.cs
- XmlReaderDelegator.cs
- SimpleHandlerFactory.cs
- ItemsPresenter.cs
- ThreadAbortException.cs
- GetLedgerRequest.cs
- ImmutableObjectAttribute.cs
- SqlParameter.cs
- SoapEnumAttribute.cs
- OdbcError.cs
- SelectionWordBreaker.cs
- DataGridRow.cs
- XmlDocumentSurrogate.cs
- FilteredAttributeCollection.cs
- XpsS0ValidatingLoader.cs
- DataServiceClientException.cs
- Window.cs
- MouseActionValueSerializer.cs
- ToolStripLabel.cs
- Visual3DCollection.cs
- TextEditorContextMenu.cs
- FixedPageAutomationPeer.cs
- Misc.cs
- SqlCommandSet.cs
- Substitution.cs
- InternalMappingException.cs
- SmtpNegotiateAuthenticationModule.cs
- PolicyValidationException.cs
- DataControlImageButton.cs
- DbFunctionCommandTree.cs
- StylusLogic.cs
- Vector3D.cs
- CodeTypeOfExpression.cs
- RadioButtonList.cs
- MimeXmlImporter.cs
- ListBindableAttribute.cs
- TextClipboardData.cs
- WpfXamlMember.cs
- InputReport.cs
- HwndHostAutomationPeer.cs
- TypeDependencyAttribute.cs
- MsmqSecureHashAlgorithm.cs
- InArgumentConverter.cs
- ProxyGenerationError.cs
- EntitySqlQueryState.cs
- LineSegment.cs
- Keyboard.cs
- ActivityScheduledQuery.cs
- SmtpNtlmAuthenticationModule.cs
- CodeTypeConstructor.cs
- TextEffectCollection.cs
- SizeConverter.cs
- BulletedListEventArgs.cs
- WebBrowserSiteBase.cs
- WebBrowsableAttribute.cs
- SocketPermission.cs
- Char.cs
- Int64AnimationBase.cs
- BinaryCommonClasses.cs
- Encoder.cs
- WeakEventTable.cs
- ListenerAdaptersInstallComponent.cs