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
- ImageAttributes.cs
- TagMapCollection.cs
- mda.cs
- ZipIOCentralDirectoryDigitalSignature.cs
- ObjectQueryExecutionPlan.cs
- MediaElement.cs
- HideDisabledControlAdapter.cs
- ResourceDictionaryCollection.cs
- TabPanel.cs
- TableCellAutomationPeer.cs
- MarkupObject.cs
- LayoutManager.cs
- ErrorEventArgs.cs
- AttachedPropertyBrowsableForTypeAttribute.cs
- NewItemsContextMenuStrip.cs
- DataGridItem.cs
- PropertyNames.cs
- ElementUtil.cs
- TextMetrics.cs
- BlobPersonalizationState.cs
- PageSettings.cs
- CryptoStream.cs
- SqlXml.cs
- EditorZoneDesigner.cs
- ComboBox.cs
- RegexGroup.cs
- ApplicationContext.cs
- TransactionBridge.cs
- DateTimePicker.cs
- ReturnEventArgs.cs
- ObjectConverter.cs
- Filter.cs
- RuleCache.cs
- recordstatescratchpad.cs
- EntityCollectionChangedParams.cs
- HttpAsyncResult.cs
- MessageRpc.cs
- SecureUICommand.cs
- ThicknessConverter.cs
- DoubleLinkListEnumerator.cs
- Win32PrintDialog.cs
- MSG.cs
- DropShadowBitmapEffect.cs
- XmlException.cs
- Converter.cs
- COM2TypeInfoProcessor.cs
- Events.cs
- SweepDirectionValidation.cs
- PriorityBindingExpression.cs
- PartialCachingAttribute.cs
- RegistrySecurity.cs
- ProjectionPruner.cs
- ButtonChrome.cs
- BamlRecordWriter.cs
- VectorKeyFrameCollection.cs
- TableDetailsRow.cs
- KeyEventArgs.cs
- LinkConverter.cs
- EventWaitHandleSecurity.cs
- PropertyGridEditorPart.cs
- InteropAutomationProvider.cs
- SQLDouble.cs
- CodeConditionStatement.cs
- DocumentGridPage.cs
- StreamResourceInfo.cs
- UrlMappingsSection.cs
- CellTreeNodeVisitors.cs
- StrongNamePublicKeyBlob.cs
- ProvidersHelper.cs
- InheritanceContextHelper.cs
- XmlReaderSettings.cs
- TimestampInformation.cs
- DataFormats.cs
- ChangePasswordAutoFormat.cs
- XmlSchemaAppInfo.cs
- SqlNotificationRequest.cs
- ProviderException.cs
- XmlSerializationWriter.cs
- ResourceContainer.cs
- altserialization.cs
- SelectedGridItemChangedEvent.cs
- SecurityTokenValidationException.cs
- RectangleGeometry.cs
- XPathNavigatorKeyComparer.cs
- AdapterDictionary.cs
- Interop.cs
- PerformanceCounterPermission.cs
- TimeSpan.cs
- DataGridViewCellValidatingEventArgs.cs
- FontFamily.cs
- ListBoxItemAutomationPeer.cs
- WorkflowMarkupSerializationManager.cs
- XPathDocumentNavigator.cs
- ConnectionManagementSection.cs
- EpmCustomContentDeSerializer.cs
- ListControl.cs
- EditorOptionAttribute.cs
- SqlCacheDependencyDatabase.cs
- ReadOnlyTernaryTree.cs
- Int32CollectionConverter.cs