2016-08-03 10:59:49 +00:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								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 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    /// <summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    /// Describes the type of change the entity will undergo when submitted to the database. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    /// </summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    public  enum  ChangeAction  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /// <summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /// The entity will not be submitted. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /// </summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        None  =  0 , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /// <summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /// The entity will be deleted. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /// </summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        Delete , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /// <summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /// The entity will be inserted. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /// </summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        Insert , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /// <summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /// The entity will be updated. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /// </summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        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 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            List < ObjectChangeConflict >  conflicts  =  new  List < ObjectChangeConflict > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            List < TrackedObject >  deletedItems  =  new  List < TrackedObject > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            List < TrackedObject >  insertedItems  =  new  List < TrackedObject > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            List < TrackedObject >  syncDependentItems  =  new  List < TrackedObject > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            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 )  { 
							 
						 
					
						
							
								
									
										
										
										
											2017-08-21 15:34:15 +00:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                // First we need to rollback any value that have already been auto-sync'd, since the values are no longer valid on the server 
							 
						 
					
						
							
								
									
										
										
										
											2016-08-03 10:59:49 +00:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								                changeDirector . RollbackAutoSync ( ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2017-08-21 15:34:15 +00:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                // Also rollback any dependent items that were sync'd, since their parent values may have been rolled back 
							 
						 
					
						
							
								
									
										
										
										
											2016-08-03 10:59:49 +00:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								                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 < TrackedObject >  insertedItems ,  List < TrackedObject >  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 ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /// <summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /// 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 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /// </summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        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 ( IEnumerable < TrackedObject >  list )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            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 < object >  newEntities  =  new  List < object > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            List < object >  deletedEntities  =  new  List < object > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            List < object >  changedEntities  =  new  List < object > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            this . ObserveUntrackedObjects ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // Must apply inferred deletions only after any untracked objects 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // are tracked 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            this . ApplyInferredDeletions ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            foreach  ( TrackedObject  item  in  this . tracker . GetInterestingObjects ( ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  ( item . IsNew )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    item . SynchDependentData ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    newEntities . Add ( item . Current ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                else  if  ( item . IsDeleted )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    deletedEntities . Add ( item . Current ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                else  if  ( item . IsPossiblyModified )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    item . SynchDependentData ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    if  ( item . IsModified )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        changedEntities . Add ( item . Current ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            return  new  ChangeSet ( newEntities . AsReadOnly ( ) ,  deletedEntities . AsReadOnly ( ) ,  changedEntities . AsReadOnly ( ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        // verify that primary key and db-generated values have not changed 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        private  static  void  CheckForInvalidChanges ( TrackedObject  tracked )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            foreach  ( MetaDataMember  mem  in  tracked . Type . PersistentDataMembers )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  ( mem . IsPrimaryKey  | |  mem . IsDbGenerated  | |  mem . IsVersion )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    if  ( tracked . HasChangedValue ( mem ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        if  ( mem . IsPrimaryKey )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            throw  Error . IdentityChangeNotAllowed ( mem . Name ,  tracked . Type . Name ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            throw  Error . DbGeneratedChangeNotAllowed ( mem . Name ,  tracked . Type . Name ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /// <summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /// Create an ChangeConflictException with the best message 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /// </summary>        
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        static  private  ChangeConflictException  CreateChangeConflictException ( int  totalUpdatesAttempted ,  int  failedUpdates )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            string  msg  =  Strings . RowNotFoundOrChanged ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( totalUpdatesAttempted  >  1 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                msg  =  Strings . UpdatesFailedMessage ( failedUpdates ,  totalUpdatesAttempted ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            return  new  ChangeConflictException ( msg ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        internal  void  TrackUntrackedObjects ( )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            Dictionary < object ,  object >  visited  =  new  Dictionary < object ,  object > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // search for untracked new objects 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            List < TrackedObject >  items  =  new  List < TrackedObject > ( this . tracker . GetInterestingObjects ( ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            foreach  ( TrackedObject  item  in  items )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                this . TrackUntrackedObjects ( item . Type ,  item . Current ,  visited ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        internal  void  ApplyInferredDeletions ( )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            foreach  ( TrackedObject  item  in  this . tracker . GetInterestingObjects ( ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  ( item . CanInferDelete ( ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    // based on DeleteOnNull specifications on the item's associations, 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    // a deletion can be inferred for this item.  The actual state transition 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    // is dependent on the current item state. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    if  ( item . IsNew )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        item . ConvertToRemoved ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    else  if  ( item . IsPossiblyModified  | |  item . IsModified )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        item . ConvertToDeleted ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        private  void  TrackUntrackedObjects ( MetaType  type ,  object  item ,  Dictionary < object ,  object >  visited )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( ! visited . ContainsKey ( item ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                visited . Add ( item ,  item ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                TrackedObject  tracked  =  this . tracker . GetTrackedObject ( item ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  ( tracked  = =  null )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    tracked  =  this . tracker . Track ( item ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    tracked . ConvertToNew ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                else  if  ( tracked . IsDead  | |  tracked . IsRemoved )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    // ignore 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    return ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                // search parents (objects we are dependent on) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                foreach  ( RelatedItem  parent  in  this . services . GetParents ( type ,  item ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    this . TrackUntrackedObjects ( parent . Type ,  parent . Item ,  visited ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                // synch up primary key 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  ( tracked . IsNew )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    tracked . InitializeDeferredLoaders ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    if  ( ! tracked . IsPendingGeneration ( tracked . Type . IdentityMembers ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        tracked . SynchDependentData ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        object  cached  =  this . services . InsertLookupCachedObject ( tracked . Type ,  item ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        if  ( cached  ! =  item )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            TrackedObject  cachedTracked  =  this . tracker . GetTrackedObject ( cached ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            Debug . Assert ( cachedTracked  ! =  null ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            if  ( cachedTracked . IsDeleted  | |  cachedTracked . CanInferDelete ( ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                // adding new object with same ID as object being deleted.. turn into modified 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                tracked . ConvertToPossiblyModified ( cachedTracked . Original ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                // turn deleted to dead... 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                cachedTracked . ConvertToDead ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                this . services . RemoveCachedObjectLike ( tracked . Type ,  item ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                this . services . InsertLookupCachedObject ( tracked . Type ,  item ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            else  if  ( ! cachedTracked . IsDead )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                throw  new  DuplicateKeyException ( item ,  Strings . CantAddAlreadyExistingKey ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        // we may have a generated PK, however we set the PK on this new item to  
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        // match a deleted item 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        object  cached  =  this . services . GetCachedObjectLike ( tracked . Type ,  item ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        if  ( cached  ! =  null )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            TrackedObject  cachedTracked  =  this . tracker . GetTrackedObject ( cached ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            Debug . Assert ( cachedTracked  ! =  null ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            if  ( cachedTracked . IsDeleted  | |  cachedTracked . CanInferDelete ( ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                // adding new object with same ID as object being deleted.. turn into modified 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                tracked . ConvertToPossiblyModified ( cachedTracked . Original ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                // turn deleted to dead... 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                cachedTracked . ConvertToDead ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                this . services . RemoveCachedObjectLike ( tracked . Type ,  item ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                this . services . InsertLookupCachedObject ( tracked . Type ,  item ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                // search children (objects that are dependent on us) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                foreach  ( RelatedItem  child  in  this . services . GetChildren ( type ,  item ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    this . TrackUntrackedObjects ( child . Type ,  child . Item ,  visited ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        internal  void  ObserveUntrackedObjects ( )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            Dictionary < object ,  object >  visited  =  new  Dictionary < object ,  object > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            List < TrackedObject >  items  =  new  List < TrackedObject > ( this . tracker . GetInterestingObjects ( ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            foreach  ( TrackedObject  item  in  items )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                this . ObserveUntrackedObjects ( item . Type ,  item . Current ,  visited ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        private  void  ObserveUntrackedObjects ( MetaType  type ,  object  item ,  Dictionary < object ,  object >  visited )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( ! visited . ContainsKey ( item ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                visited . Add ( item ,  item ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                TrackedObject  tracked  =  this . tracker . GetTrackedObject ( item ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  ( tracked  = =  null )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    tracked  =  this . tracker . Track ( item ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    tracked . ConvertToNew ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                }  else  if  ( tracked . IsDead  | |  tracked . IsRemoved )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    // ignore 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    return ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                // search parents (objects we are dependent on) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                foreach  ( RelatedItem  parent  in  this . services . GetParents ( type ,  item ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    this . ObserveUntrackedObjects ( parent . Type ,  parent . Item ,  visited ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                // synch up primary key unless its generated. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  ( tracked . IsNew )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                     if  ( ! tracked . IsPendingGeneration ( tracked . Type . IdentityMembers ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        tracked . SynchDependentData ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                     }  
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                // search children (objects that are dependent on us) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                foreach  ( RelatedItem  child  in  this . services . GetChildren ( type ,  item ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    this . ObserveUntrackedObjects ( child . Type ,  child . Item ,  visited ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        private  TrackedObject  GetOtherItem ( MetaAssociation  assoc ,  object  instance )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( instance  = =  null ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                return  null ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            object  other  =  null ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // Don't load unloaded references 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( assoc . ThisMember . StorageAccessor . HasAssignedValue ( instance )  | | 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                assoc . ThisMember . StorageAccessor . HasLoadedValue ( instance ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                other  =  assoc . ThisMember . MemberAccessor . GetBoxedValue ( instance ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            else  if  ( assoc . OtherKeyIsPrimaryKey )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                // Maybe it's in the cache, but not yet attached through reference. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                object [ ]  foreignKeys  =  CommonDataServices . GetForeignKeyValues ( assoc ,  instance ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                other  =  this . services . GetCachedObject ( assoc . OtherType ,  foreignKeys ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // else the other key is not the primary key so there is no way to try to look it up 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            return  ( other  ! =  null )  ?  this . tracker . GetTrackedObject ( other )  :  null ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        private  bool  HasAssociationChanged ( MetaAssociation  assoc ,  TrackedObject  item )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( item . Original  ! =  null  & &  item . Current  ! =  null )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  ( assoc . ThisMember . StorageAccessor . HasAssignedValue ( item . Current )  | | 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    assoc . ThisMember . StorageAccessor . HasLoadedValue ( item . Current ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    return  this . GetOtherItem ( assoc ,  item . Current )  ! =  this . GetOtherItem ( assoc ,  item . Original ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    object [ ]  currentFKs  =  CommonDataServices . GetForeignKeyValues ( assoc ,  item . Current ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    object [ ]  originaFKs  =  CommonDataServices . GetForeignKeyValues ( assoc ,  item . Original ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    for  ( int  i  =  0 ,  n  =  currentFKs . Length ;  i  <  n ;  i + + )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        if  ( ! object . Equals ( currentFKs [ i ] ,  originaFKs [ i ] ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            return  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            return  false ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        private  void  BuildEdgeMaps ( )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            this . currentParentEdges . Clear ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            this . originalChildEdges . Clear ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            this . originalChildReferences . Clear ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            List < TrackedObject >  list  =  new  List < TrackedObject > ( this . tracker . GetInterestingObjects ( ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            foreach  ( TrackedObject  item  in  list )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                bool  isNew  =  item . IsNew ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                MetaType  mt  =  item . Type ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                foreach  ( MetaAssociation  assoc  in  mt . Associations )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    if  ( assoc . IsForeignKey )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        TrackedObject  otherItem  =  this . GetOtherItem ( assoc ,  item . Current ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        TrackedObject  dbOtherItem  =  this . GetOtherItem ( assoc ,  item . Original ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        bool  pointsToDeleted  =  ( otherItem  ! =  null  & &  otherItem . IsDeleted )  | |  ( dbOtherItem  ! =  null  & &  dbOtherItem . IsDeleted ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        bool  pointsToNew  =  ( otherItem  ! =  null  & &  otherItem . IsNew ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        if  ( isNew  | |  pointsToDeleted  | |  pointsToNew  | |  this . HasAssociationChanged ( assoc ,  item ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            if  ( otherItem  ! =  null )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                this . currentParentEdges . Add ( assoc ,  item ,  otherItem ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            if  ( dbOtherItem  ! =  null )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                if  ( assoc . IsUnique )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                    this . originalChildEdges . Add ( assoc ,  dbOtherItem ,  item ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                this . originalChildReferences . Add ( dbOtherItem ,  item ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        enum  VisitState  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            Before , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            After 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        private  List < TrackedObject >  GetOrderedList ( )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            var  objects  =  this . tracker . GetInterestingObjects ( ) . ToList ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // give list an initial order (most likely correct order) to avoid deadlocks in server 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            var  range  =  Enumerable . Range ( 0 ,  objects . Count ) . ToList ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            range . Sort ( ( int  x ,  int  y )  = >  Compare ( objects [ x ] ,  x ,  objects [ y ] ,  y ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            var  ordered  =  range . Select ( i  = >  objects [ i ] ) . ToList ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // permute order if constraint dependencies requires some changes to come before others 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            var  visited  =  new  Dictionary < TrackedObject ,  VisitState > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            var  list  =  new  List < TrackedObject > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            foreach  ( TrackedObject  item  in  ordered )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                this . BuildDependencyOrderedList ( item ,  list ,  visited ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            return  list ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        private  static  int  Compare ( TrackedObject  x ,  int  xOrdinal ,  TrackedObject  y ,  int  yOrdinal )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // deal with possible nulls 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( x  = =  y )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                return  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( x  = =  null )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                return  - 1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            else  if  ( y  = =  null )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                return  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // first order by action: Inserts first, Updates, Deletes last 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            int  xAction  =  x . IsNew  ?  0  :  x . IsDeleted  ?  2  :  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            int  yAction  =  y . IsNew  ?  0  :  y . IsDeleted  ?  2  :  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( xAction  <  yAction )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                return  - 1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            else  if  ( xAction  >  yAction )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                return  1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // no need to order inserts (PK's may not even exist) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( x . IsNew )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                // keep original order 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                return  xOrdinal . CompareTo ( yOrdinal ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // second order by type 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( x . Type  ! =  y . Type )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                return  string . CompareOrdinal ( x . Type . Type . FullName ,  y . Type . Type . FullName ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // lastly, order by PK values 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            int  result  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            foreach  ( MetaDataMember  mm  in  x . Type . IdentityMembers )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                object  xValue  =  mm . StorageAccessor . GetBoxedValue ( x . Current ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                object  yValue  =  mm . StorageAccessor . GetBoxedValue ( y . Current ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  ( xValue  = =  null )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    if  ( yValue  ! =  null )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        return  - 1 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    IComparable  xc  =  xValue  as  IComparable ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    if  ( xc  ! =  null )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        result  =  xc . CompareTo ( yValue ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        if  ( result  ! =  0 )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            return  result ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // they are the same? leave in original order 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            return  xOrdinal . CompareTo ( yOrdinal ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        private  void  BuildDependencyOrderedList ( TrackedObject  item ,  List < TrackedObject >  list ,  Dictionary < TrackedObject ,  VisitState >  visited )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            VisitState  state ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( visited . TryGetValue ( item ,  out  state ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  ( state  = =  VisitState . Before )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    throw  Error . CycleDetected ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                return ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            visited [ item ]  =  VisitState . Before ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( item . IsInteresting )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  ( item . IsDeleted )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    // if 'item' is deleted 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    //    all objects that used to refer to 'item' must be ordered before item 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    foreach  ( TrackedObject  other  in  this . originalChildReferences [ item ] )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        if  ( other  ! =  item )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            this . BuildDependencyOrderedList ( other ,  list ,  visited ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    // if 'item' is new or changed 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    //   for all objects 'other' that 'item' refers to along association 'assoc' 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    //      if 'other' is new then 'other' must be ordered before 'item' 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    //      if 'assoc' is pure one-to-one and some other item 'prevItem' used to refer to 'other' 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    //         then 'prevItem' must be ordered before 'item' 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    foreach  ( MetaAssociation  assoc  in  item . Type . Associations )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        if  ( assoc . IsForeignKey )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            TrackedObject  other  =  this . currentParentEdges [ assoc ,  item ] ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            if  ( other  ! =  null )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                if  ( other . IsNew )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                    // if other is new, visit other first (since item's FK depends on it) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                    if  ( other  ! =  item  | |  item . Type . DBGeneratedIdentityMember  ! =  null )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                        this . BuildDependencyOrderedList ( other ,  list ,  visited ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                else  if  ( ( assoc . IsUnique  | |  assoc . ThisKeyIsPrimaryKey ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                    TrackedObject  prevItem  =  this . originalChildEdges [ assoc ,  other ] ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                    if  ( prevItem  ! =  null  & &  other  ! =  item )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                        this . BuildDependencyOrderedList ( prevItem ,  list ,  visited ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                list . Add ( item ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            visited [ item ]  =  VisitState . After ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        class  EdgeMap  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            Dictionary < MetaAssociation ,  Dictionary < TrackedObject ,  TrackedObject > >  associations ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            internal  EdgeMap ( )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                this . associations  =  new  Dictionary < MetaAssociation ,  Dictionary < TrackedObject ,  TrackedObject > > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            internal  void  Add ( MetaAssociation  assoc ,  TrackedObject  from ,  TrackedObject  to )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                Dictionary < TrackedObject ,  TrackedObject >  pairs ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  ( ! associations . TryGetValue ( assoc ,  out  pairs ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    pairs  =  new  Dictionary < TrackedObject ,  TrackedObject > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    associations . Add ( assoc ,  pairs ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                pairs . Add ( from ,  to ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            internal  TrackedObject  this [ MetaAssociation  assoc ,  TrackedObject  from ]  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                get  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    Dictionary < TrackedObject ,  TrackedObject >  pairs ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    if  ( associations . TryGetValue ( assoc ,  out  pairs ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        TrackedObject  to ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        if  ( pairs . TryGetValue ( from ,  out  to ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            return  to ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    return  null ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            internal  void  Clear ( )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                this . associations . Clear ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        class  ReferenceMap  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            Dictionary < TrackedObject ,  List < TrackedObject > >  references ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            internal  ReferenceMap ( )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                this . references  =  new  Dictionary < TrackedObject ,  List < TrackedObject > > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            internal  void  Add ( TrackedObject  from ,  TrackedObject  to )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                List < TrackedObject >  refs ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  ( ! references . TryGetValue ( from ,  out  refs ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    refs  =  new  List < TrackedObject > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    references . Add ( from ,  refs ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  ( ! refs . Contains ( to ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    refs . Add ( to ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            internal  IEnumerable < TrackedObject >  this [ TrackedObject  from ]  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                get  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    List < TrackedObject >  refs ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    if  ( references . TryGetValue ( from ,  out  refs ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        return  refs ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    return  Empty ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            internal  void  Clear ( )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                this . references . Clear ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            private  static  TrackedObject [ ]  Empty  =  new  TrackedObject [ ]  {  } ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}