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
- HeaderedItemsControl.cs
- AnnotationAuthorChangedEventArgs.cs
- TextureBrush.cs
- OleDbSchemaGuid.cs
- CriticalFinalizerObject.cs
- Frame.cs
- PageThemeCodeDomTreeGenerator.cs
- DomainUpDown.cs
- Margins.cs
- GridEntry.cs
- MailMessageEventArgs.cs
- DataTableCollection.cs
- SignatureResourcePool.cs
- Stream.cs
- XmlSiteMapProvider.cs
- CollectionViewGroupRoot.cs
- VideoDrawing.cs
- ListBoxItemAutomationPeer.cs
- Camera.cs
- Control.cs
- XmlQueryContext.cs
- IdentityManager.cs
- Membership.cs
- MethodBuilder.cs
- ToolBar.cs
- NativeActivityMetadata.cs
- ICollection.cs
- CapabilitiesUse.cs
- FrameworkElementAutomationPeer.cs
- RightsManagementEncryptionTransform.cs
- DataGridViewCellValueEventArgs.cs
- StrokeNode.cs
- DataGridComponentEditor.cs
- XmlUtil.cs
- ToolStripProfessionalLowResolutionRenderer.cs
- IgnorePropertiesAttribute.cs
- TrackingServices.cs
- BamlLocalizer.cs
- Table.cs
- VariantWrapper.cs
- PersistChildrenAttribute.cs
- DesignerCategoryAttribute.cs
- hresults.cs
- SystemDiagnosticsSection.cs
- DataViewManager.cs
- SourceSwitch.cs
- XPathBuilder.cs
- SwitchAttribute.cs
- HostedTcpTransportManager.cs
- SetterBaseCollection.cs
- RouteTable.cs
- BamlResourceSerializer.cs
- OleAutBinder.cs
- SolidColorBrush.cs
- OdbcInfoMessageEvent.cs
- CompilationAssemblyInstallComponent.cs
- Color.cs
- ContextStaticAttribute.cs
- Matrix.cs
- fixedPageContentExtractor.cs
- XmlNullResolver.cs
- RulePatternOps.cs
- HelpEvent.cs
- ParseElementCollection.cs
- SqlCharStream.cs
- ThreadSafeList.cs
- DataGridViewCellStyle.cs
- DataSourceComponent.cs
- TriggerBase.cs
- Operators.cs
- ExpressionList.cs
- FileDialog_Vista_Interop.cs
- returneventsaver.cs
- ButtonStandardAdapter.cs
- ListViewEditEventArgs.cs
- WasHttpHandlersInstallComponent.cs
- HMACSHA256.cs
- BoundField.cs
- QilChoice.cs
- MessageQueuePermissionEntryCollection.cs
- AuthenticationService.cs
- CurrencyManager.cs
- ListViewItemSelectionChangedEvent.cs
- TextComposition.cs
- SchemaObjectWriter.cs
- IList.cs
- iisPickupDirectory.cs
- AdjustableArrowCap.cs
- PropertyEmitterBase.cs
- LightweightEntityWrapper.cs
- SoapHeaderAttribute.cs
- TypeNameConverter.cs
- ServiceContractAttribute.cs
- DrawListViewSubItemEventArgs.cs
- InternalConfigSettingsFactory.cs
- DbModificationClause.cs
- EnumerableValidator.cs
- Component.cs
- PropertyMapper.cs
- CommandEventArgs.cs