| Quintiq file version 2.0 | 
| #parent: #root | 
| StaticMethod CopyDatasetRobust (LibOpt_Task task, String namenewdataset, String componentpositionname,  | 
|   Key mdsid) as LibOpt_SnapshotReplannableCopyDataset | 
| { | 
|   Description: | 
|   [* | 
|     Copy the dataset in a slow (non-reactive) way that is robust against rollbacks and errors.  | 
|     An optimizer run has to wait until the dataset copy is created before the optmizer run can continue.  | 
|     Please override the `[YourOptimizationSubtype].GetMDSObject()` method before using this method. | 
|   *] | 
|   TextBody: | 
|   [* | 
|     // evr3 Mar-19-2020 (created) | 
|     component := task.Component(); | 
|     run := component.Run(); | 
|     snapshot := LibOpt_SnapshotReplannableCopyDataset::Create( run, | 
|                                                                task,  | 
|                                                                namenewdataset,  | 
|                                                                componentpositionname,                                               | 
|                                                                true // IsMemoryOnly  | 
|                                                                ); | 
|     // LibOpt_Optimization::GetMDSObject should be overridden. | 
|     mdsobject := LibOpt_DatasetCopyConditional::GetMDSObject( run.Optimization() ); | 
|     snapshot.IsGetMDSObjectOverridden( not isnull( mdsobject ) ); | 
|     snapshot.HasFailedToCreateDataset( isnull( mdsobject ) ); | 
|      | 
|     // Create the dataset after creating the snapshot, to ensure that the snapshot shows up in the 'snapshots' and 'replannable snapshots' forms.  | 
|     if( not isnull( mdsobject ) ) | 
|     { | 
|       parameters := MDSParameters::Create(); | 
|       // It is not possible to use StandAloneStorage. This results in empty datasets. | 
|       // The dataset is later converted to a StandAloneStorage dataset in LibOpt_Component::DatasetCopyChangeToStandAlone() | 
|       parameters.State( DatasetState::MemoryOnly().AsString() );   | 
|      | 
|       // An error can occur when mdsobject.Copy is called. | 
|       // This happens, for example, when a circularity is created in the current transaction (but not propagated). | 
|       // An error is also thrown if the name of the dataset copy is too long. | 
|       // When the error occurs, the following steps happen: | 
|       // - The onfailure block is entered | 
|       // - HandleQuillErrorFromOnFailure is called | 
|       // - If there is a dataset copy snapshot on the 'Handle error' component position, then the current method is called again. | 
|       // - The try block is entered again. | 
|       // - The dataset is copied succesfully (This dataset copy might be in a unpropagated or semi-propgated state) | 
|       // - The HandleQuillErrorFromOnFailure call will roll back the current iteration to resolve the propagation error | 
|        | 
|       // Note: If another error happens when mdsobject.Copy is called the second time,  | 
|       // then the if-block in the onfailure ensures that HandleQuillErrorFromOnFailure method won't be called again. This is to prevent an inescapable loop. | 
|       try | 
|       {  | 
|         // Using mdsobject.Copy() is less efficient than using 'MDSObject::Create'.  | 
|         // However, datasets created by using mdsobject.Copy() are not rolled back after an error or rollback.  | 
|         mdsobject.Copy( namenewdataset, parameters );   | 
|         snapshot.HasCreatedDataset( true ); | 
|       } | 
|       onfailure | 
|       { | 
|         snapshot.HasFailedToCreateDataset( true );     | 
|         numberofsnapshots := counter( task, | 
|                                       SnapshotComponent.Children.astype( LibOpt_SnapshotReplannableCopyDataset ), | 
|                                       snapshotreplannable, | 
|                                       snapshotreplannable.ComponentPositionName() = componentpositionname ); | 
|         // In the unlikely case that a failure occurs every time that mdsobject.Copy() is called, then we could end up in an endless dataset creation loop. | 
|         // Therefore, we attempt to create at most 1 additional dataset copy. | 
|         if( numberofsnapshots <= 1 ) | 
|         { | 
|           transaction := LibOpt_CurrentTransaction::GetCurrentTransaction( component ); | 
|           transaction.HandleQuillErrorFromOnFailure( e ); | 
|         } | 
|       } | 
|     } | 
|      | 
|     return snapshot; | 
|   *] | 
|   InterfaceProperties { Accessibility: 'Module' } | 
| } |