As part of the Relativity Services API (RSAPI) Deprecation, content on this page referring to the RSAPI and the Patient Tracker application is in the process of being deprecated and will no longer be supported. For more information and alternative APIs, see RSAPI deprecation process.

Update RDO properties and fields

Relativity Dynamic Objects (RDOs) are custom objects that extend the functionality of the system. You can create RDOs through the Relativity user interface. For more information, see Relativity Dynamic Objects. The Services API also includes a set of interfaces for creating custom RDO types (object types) and manipulating RDOs.

Starting with 9.5.133.118, you can programmatically interact with admin-level RDOs.

This page provides detailed explanation of the programmatic steps for the following RDO tasks:

For complete RDO and object type operations reference and code samples, see RDO and ObjectType in RSAPI reference for .NET.

Create object types

Before you can manipulate Relativity data through a custom RDO object, define the custom object as an object type. You can create object types using regular multi-artifact and single-artifact patterns. For more information about DTO artifact access patterns, see Single-artifact access.

To create an object type:

  1. Instantiate the client proxy object:
    Copy
    using (IRSAPIClient proxy = helper.GetServicesManager().CreateProxy<IRSAPIClient>(ExecutionIdentity.System))
    {
        //All operations on the Services API DTO objects must be enclosed in the using block
    }

    Note: In this example the proxy object instantiates using the IRSAPIClient helper class with System execution identity. For more information, see Create the proxy using the Relativity API Helpers. All subsequent operations with Relativity objects perform within the using block.

  2. Set the workspace ID in the APIOptions object. This specifies the Relativity workspace where the object type will be added:
    Copy
    proxy.APIOptions.WorkspaceID = this.SampleWorkspace_ID;
  3. Instantiate the ObjectType DTO:
    Copy
    Client.DTOs.ObjectType objectTypeDTO = new Client.DTOs.ObjectType();
  4. Set the fields values:
    1. Name
      Copy
      objectTypeDTO.Name = string.Format("API {0}", Guid.NewGuid());

      Note: In the example above the name is set to a GUID.

    2. ParentArtifactTypeID
      Copy
      objectTypeDTO.ParentArtifactTypeID = 8;
    3. SnapshotAuditingEnabledOnDelete
      Copy
      objectTypeDTO.SnapshotAuditingEnabledOnDelete = true;
    4. Pivot
      Copy
      objectTypeDTO.Pivot = true;
    5. CopyInstancesOnWorkspaceCreation
      Copy
      objectTypeDTO.CopyInstancesOnWorkspaceCreation = false;
    6. CopyInstancesOnParentCopy
      Copy
      objectTypeDTO.CopyInstancesOnParentCopy = false;

      Note: Use the field to set the ObjectType to automatically copy the instances of the object when its parent object is copied. You cannot set the CopyInstanceOnParentCopy to True if the parent artifact type is Workspace. If you attempt to do that, a validation error is thrown. That scenario is covered by the CopyInstancesOnWorkspaceCreation property.

    7. Sampling
      Copy
      objectTypeDTO.Sampling = true;
    8. RelativtyApplications
      Copy
      objectTypeDTO.RelativityApplications = new List<Client.DTOs.RelativityApplication> { new Client.DTOs.RelativityApplication(1037990), new Client.DTOs.RelativityApplication(1037639) };

      Note: The object type associates with the applications existing in the workspace by passing in a collection of RelativityApplication DTOs identified by Artifact ID.

  5. Call the CreateSingle() method of the ObjectTypeRepository object passing it the populated ObjectType DTO. Use a try/catch block in case the operation fails:
    Copy
    try
    {
        int objectTypeID = proxy.Repositories.ObjectType.CreateSingle(objectTypeDTO);
        Console.WriteLine("The new ObjectType Artifact ID is: {0}", objectTypeID);
    }
    catch (Exception ex)
    {
        Console.WriteLine("An error occurred: {0}", ex.Message);
        return false;
    }

    If the object type creates successfully, the integer value of its Artifact ID returns.

    If the operations fails, kCura.Relativity.Client.APIException returns. The Message property of the exception indicates the error.

Read object types

You can read Relativity object types with both regular multi-artifact and single-artifact patterns. For more information about DTO artifact access patterns, see Single-artifact access.

To read a single object type:

  1. Instantiate the client proxy and set the workspace ID in the APIOptions object as shown in Create object types.
  2. Call the ReadSingle() method of the ObjectTypeRepository object passing it the object type ID and read the object type. Use a try/catch block in case the operation fails.
    Copy
    try
    {
        Client.DTOs.ObjectType objectTypeDTO = proxy.Repositories.ObjectType.ReadSingle(this.SampleObjectType_ID);    
        string name = objectTypeDTO.Name;
    }
    catch (APIException ex)
    {
        //Exceptions are returned as an APIException
        Console.WriteLine("An error occurred: {0}" + ex.Message);
        return false;
    }
  3. Read the fields using the object properties after the Object Type DTO returns. For example, Name:
    Copy
    string name = objectTypeDTO.Name;

Create RDO instances with multiple field types

You can create RDOs with multiple field types using both the regular multi-artifact and single-artifact patterns. For more information about DTO artifact access patterns, see Single-artifact access.

Create a single RDO

To create a single RDO instance:

  1. Instantiate the client proxy and set the workspace ID in the APIOptions object as shown in Create object types.
  2. Read the ObjectType for the RDO you are creating:
    Copy
    try
    {
        Client.DTOs.ObjectType objectTypeDTO = proxy.Repositories.ObjectType.ReadSingle(this.SampleObjectType_ID);
        
        string name = objectTypeDTO.Name;
    }
    catch (APIException ex)
    {
        //Exceptions are returned as an APIException
        Console.WriteLine("An error occurred: {0}" + ex.Message);
        return false;
    }
  3. Instantiate the RDO object:
    Copy
    Client.DTOs.RDO rdoToCreate = new Client.DTOs.RDO();
  4. Associate the RDO with the previously returned object type by setting the ArtifactTypeID field value to the Artifact ID of the object type.
    Copy
    rdoToCreate.ArtifactTypeID = objectTypeToCreate.DescriptorArtifactTypeID;
  5. Set the required Name field value:
    Copy
    rdoToCreate.Fields.Add(new DTOs.FieldValue("Name", string.Format("API {0}", Guid.NewGuid())));

    Note: In this example the name is set to a GUID.

  6. Set the optional fields of various types:
    1. Currency
      Copy
      rdoToCreate.Fields.Add(new DTOs.FieldValue(this.SampleField_Currency_ID) { Value = new Random().Next(0, 10) });
    2. Date
      Copy
      rdoToCreate.Fields.Add(new DTOs.FieldValue(this.SampleField_Date_ID) { Value = DateTime.UtcNow });
    3. Decimal
      Copy
      rdoToCreate.Fields.Add(new DTOs.FieldValue(this.SampleField_Decimal_ID) { Value = new Random().Next(0, 10) });
    4. Fixed length text
      Copy
      rdoToCreate.Fields.Add(new DTOs.FieldValue(this.SampleField_FixedLengthText_ID) { Value = "SAMPLE TEXT VALUE" });
    5. Long text
      Copy
      rdoToCreate.Fields.Add(new DTOs.FieldValue(this.SampleField_LongText_ID) { Value = "SAMPLE LONG TEXT VALUE" });
    6. Whole number
      Copy
      rdoToCreate.Fields.Add(new DTOs.FieldValue(this.SampleField_WholeNumber_ID) { Value = new Random().Next(0, 10) });
    7. Yes/No
      Copy
      rdoToCreate.Fields.Add(new DTOs.FieldValue(this.SampleField_YesNo_ID) { Value = false });
  7. Call the CreateSingle() method of the RDORepository object, passing it the populated RDO object type. Use a try/catch block in case the operation fails.
    Copy
    try
    {
        int rdoID = proxy.Repositories.RDO.CreateSingle(rdoToCreate);
        Console.WriteLine("The new RDO Artifact ID is: {0}", rdoID);
    }
    catch (APIException ex)
    {
        //Exceptions are thrown as an APIException
        Console.WriteLine("An error occurred: {0}", ex.Message);
        return false;
    }
  8. If the RDO creates successfully, the integer value of its Artifact ID returns.

    If the operations fails, kCura.Relativity.Client.APIException returns. The Message property of the exception indicates the error.

Create multiple RDOs

To create multiple RDO instances:

  1. Instantiate the client proxy and set the workspace ID in the APIOptions object as shown in Create object types.
  2. Read the ObjectType for the RDO you are creating:
    Copy
    try
    {
        Client.DTOs.ObjectType objectTypeDTO = proxy.Repositories.ObjectType.ReadSingle(this.SampleObjectType_ID);
        
        string name = objectTypeDTO.Name;
    }
    catch (APIException ex)
    {
        //Exceptions are returned as an APIException
        Console.WriteLine("An error occurred: {0}" + ex.Message);
        return false;
    }<![CDATA[  ]]>
  3. Instantiate a list of RDO objects you will create:
    Copy
    List<Client.DTOs.RDO> rdosToCreate = new List<Client.DTOs.RDO>();
    for (int i = 0; i <= 10; i++)
    {
        //Create an RDO DTO
        Client.DTOs.RDO rdoToCreate = new Client.DTOs.RDO();

        //Set primary fields
        rdoToCreate.ArtifactTypeID = objectTypeToCreate.DescriptorArtifactTypeID;
        //The name of the sample data is being set to a random string so that sample data can be debugged
        //and never causes collisions. You can set this to any string that you want
        rdoToCreate.Fields.Add(new DTOs.FieldValue("Name", string.Format("API {0}", Guid.NewGuid())));

        //Set secondary fields
        rdoToCreate.Fields.Add(new DTOs.FieldValue(this.SampleField_Currency_ID) { Value = new Random().Next(0, 10) });
        rdoToCreate.Fields.Add(new DTOs.FieldValue(this.SampleField_Date_ID) { Value = DateTime.UtcNow });
        rdoToCreate.Fields.Add(new DTOs.FieldValue(this.SampleField_Decimal_ID) { Value = new Random().Next(0, 10) });
        rdoToCreate.Fields.Add(new DTOs.FieldValue(this.SampleField_FixedLengthText_ID) { Value = "SAMPLE TEXT VALUE" });
        rdoToCreate.Fields.Add(new DTOs.FieldValue(this.SampleField_LongText_ID) { Value = "SAMPLE LONG TEXT VALUE" });
        rdoToCreate.Fields.Add(new DTOs.FieldValue(this.SampleField_WholeNumber_ID) { Value = new Random().Next(0, 10) });
        rdoToCreate.Fields.Add(new DTOs.FieldValue(this.SampleField_YesNo_ID) { Value = false });

        rdosToCreate.Add(rdoToCreate);
    }
  4. Note: The field values for each RDO instance in the list are set in the same way as in the example in Create a single RDO.

  5. Call the Create() method of the RDORepository object and pass the RDO objects list. Use a try/catch block in case the operation fails.
    Copy
    try
    {
        resultSet = proxy.Repositories.RDO.Create(rdosToCreate);
    }
    catch (Exception ex)
    {
        Console.WriteLine("An error occurred: {0}", ex.Message);
        return false;
    }
  6. Check the result of the create operation by evaluating the Success property of the ResultSet object and iterating through the objects in the Results collection:
    Copy
    // Check for success
    if (resultSet.Success)
    {
        //Check the results
        int succeed = 0;
        foreach (Client.DTOs.Result<Client.DTOs.RDO> rdo in resultSet.Results)
        {
    if (rdo.Success)
    {
        succeed++;
    }
        }
        Console.WriteLine("{0} RDOs created", succeed);
    }
    else
    {
        Console.WriteLine("The create failed. {0}", resultSet.Message);
        return false;
    }

    If all RDOs create successfully, the Success property returns as True for every object in the collection.

Read RDOs with multiple field types

To read multiple RDO instances:

  1. Instantiate the client proxy and set the workspace ID in the APIOptions object as shown in Create object types.
  2. Read the ObjectType for the RDO you're returning:
    Copy
    Client.DTOs.ObjectType objectTypeToRead = proxy.Repositories.ObjectType.ReadSingle(this.SampleObjectType_ID);
  3. Instantiate a list of RDO objects that you will read in:
    Copy
    List<Client.DTOs.RDO> rdosToRead = new List<Client.DTOs.RDO>();
    foreach (int rdoID in this.SampleRDOList_IDs)
    {
        //Create the rdo DTO
        //NOTE: rdoID is sample data created for this example. 
        //You should replace this with the correct Artifact ID, GUID, or Name
        Client.DTOs.RDO rdoDTO = new Client.DTOs.RDO(rdoID);
        rdoDTO.ArtifactTypeID = objectTypeToRead.DescriptorArtifactTypeID;

        //Request the fields you want from RDO
        //NOTE: A best practice is to specify fields rather than using AllFields. This keeps
        //the data returned predicable in large environments with many fields 
        rdoDTO.Fields.Add(new DTOs.FieldValue("Name"));
        rdoDTO.Fields.Add(new FieldValue(Client.DTOs.ArtifactQueryFieldNames.ArtifactID));
        rdoDTO.Fields.Add(new FieldValue(Client.DTOs.ArtifactFieldNames.TextIdentifier));
        rdoDTO.Fields.Add(new FieldValue(this.SampleField_Currency_ID));
        rdoDTO.Fields.Add(new FieldValue(this.SampleField_Date_ID));
        rdoDTO.Fields.Add(new FieldValue(this.SampleField_Decimal_ID));
        rdoDTO.Fields.Add(new FieldValue(this.SampleField_FixedLengthText_ID));
        rdoDTO.Fields.Add(new FieldValue(this.SampleField_LongText_ID));
        rdoDTO.Fields.Add(new FieldValue(this.SampleField_WholeNumber_ID));
        rdoDTO.Fields.Add(new FieldValue(this.SampleField_YesNo_ID));
        rdoDTO.Fields.Add(new FieldValue(this.SampleField_SingleChoice_ID));
        rdoDTO.Fields.Add(new FieldValue(this.SampleField_MultiChoice_ID));

        rdosToRead.Add(rdoDTO);
    }

    Note: The field values for each RDO instance in the list are set in the same way as in the example in Create a single RDO.

  4. Instantiate the ResultSet object for the create read:
    Copy
    DTOs.ResultSet<DTOs.RDO> resultSet = new DTOs.ResultSet<DTOs.RDO>();
  5. Call the Read() method of the RDORepository object passing it the populated list of RDO objects. Use a try/catch block in case the operation fails.
    Copy
    try
    {
        resultSet = proxy.Repositories.RDO.Read(rdosToRead);
    }
    catch (Exception ex)
    {
        Console.WriteLine("An error occurred: {0}", ex.Message);
        return false;
    }
  6. Check for success:
  7. Copy
    //Check the success
    if (resultSet.Success)
    {
        //Save the RDO from the results
        Client.DTOs.RDO rdoDTO = resultSet.Results.FirstOrDefault().Artifact;

        //Read some basic Fields from the RDO
        int artifactID = rdoDTO.ArtifactID;
        int parentArtifactID = rdoDTO.ParentArtifact.ArtifactID;

        //Value fields
        decimal? currency = rdoDTO.Fields.Get(this.SampleField_Currency_ID).ValueAsCurrency;
        DateTime? date = rdoDTO.Fields.Get(this.SampleField_Date_ID).ValueAsDate;
        decimal? decimalField = rdoDTO.Fields.Get(this.SampleField_Decimal_ID).ValueAsDecimal;
        int? wholeNumber = rdoDTO.Fields.Get(this.SampleField_WholeNumber_ID).ValueAsWholeNumber;

        //Text fields
        string fixedLengthText = rdoDTO.Fields.Get(this.SampleField_FixedLengthText_ID).ValueAsFixedLengthText;
        string longTextField = rdoDTO.Fields.Get(this.SampleField_LongText_ID).ValueAsLongText;
        
        //Yes/no field
        bool? yesOrNo = rdoDTO.Fields.Get(this.SampleField_YesNo_ID).ValueAsYesNo;

        //Single-choice Field
        Client.DTOs.Choice singleChoice = rdoDTO.Fields.Get(this.SampleField_SingleChoice_ID).ValueAsSingleChoice;

        //Multi-choice field
        //List<Client.DTOs.Choice> multiChoice = rdoDTO.Fields.Get(this.SampleField_MultiChoice_ID).ValueAsMultipleChoice;
    }
    else
    {
        Console.WriteLine("The read failed. {0}", resultSet.Message);
        return false;
    }

Update RDOs with multiple field types

To update an instance of RDO:

  1. Instantiate the client proxy and set the workspace ID in the APIOptions object as shown in Create object types.
  2. Call the ReadSingle() method of the RDORepository object and passing it the RDO Artifact ID and read the updated RDO. Instantiate an RDO object to the results of the read operation. Use a try/catch block in case the operation fails.
    Copy
    Client.DTOs.RDO rdoToUpdate = proxy.Repositories.RDO.ReadSingle(this.SampleRDO_ID);

    Note: You can create the RDO object manually rather than read it.

  3. Change the Name field value. In this example it is set to a GUID, but you can set the name to any other string.
    Copy
    rdoToUpdate.Fields.Add(new Client.DTOs.FieldValue("Name") { Value = string.Format("API Updated {0}", Guid.NewGuid()) });
  4. Change the secondary field values:
    Copy
    rdoToUpdate.Fields.Add(new DTOs.FieldValue(this.SampleField_Currency_ID) { Value = new Random().Next(0, 10) });
    rdoToUpdate.Fields.Add(new DTOs.FieldValue(this.SampleField_Date_ID) { Value = DateTime.UtcNow });
    rdoToUpdate.Fields.Add(new DTOs.FieldValue(this.SampleField_Decimal_ID) { Value = new Random().Next(0, 10) });
    rdoToUpdate.Fields.Add(new DTOs.FieldValue(this.SampleField_FixedLengthText_ID) { Value = "SAMPLE TEXT VALUE UPDATED" });
    rdoToUpdate.Fields.Add(new DTOs.FieldValue(this.SampleField_LongText_ID) { Value = "SAMPLE LONG TEXT VALUE UPDATED" });
    rdoToUpdate.Fields.Add(new DTOs.FieldValue(this.SampleField_WholeNumber_ID) { Value = new Random().Next(0, 10) });
    rdoToUpdate.Fields.Add(new DTOs.FieldValue(this.SampleField_YesNo_ID) { Value = true });
  5. Call the UpdateSingle() method of the RDORepository object to update the RDO. Use a try/catch block in case the operation fails.
    Copy
    try
    {
        proxy.Repositories.RDO.Update(rdoToUpdate);
    }
    catch (Exception ex)
    {
        //Exceptions are returned as an APIException
        Console.WriteLine(string.Format("An error occurred: {0}", ex.Message));
        return false;
    }<br />

Find the download URL for an RDO file field

Sometimes it may be necessary to programmatically download the file contained in the RDO file field. Before you can download the file, find the file download URL.

To find the download URL for an RDO file field:

  1. Instantiate the client proxy and set the workspace ID in the APIOptions object as shown in Create object types.
  2. Instantiate a new DownloadURLRequest object with the APIOptions.
    Copy
    var downloadUrlRequest = new DownloadURLRequest(proxy.APIOptions);
  3. Set the URL property in the DownloadURLRequest:
    Copy
    downloadUrlRequest.BaseURI = new Uri("http://localhost");
  4. Set the ObjectArtifactID or ObjectArtifactGuid that identifies the target instance of the RDO:
    Copy
    downloadUrlRequest.Target.ObjectArtifactGuid = new Guid("B5FD94BB-0226-4CA5-B227-5F3F7BEE4D8A");

    Note: All GUIDs are specific to your workspace.

  5. Set the FieldID, FieldName, or FieldGuid of the file field:
    Copy
    downloadUrlRequest.Target.FieldId = 1042467;
  6. Instantiate a Response object.
    Copy
    DownloadURLResponse response;
  7. Call the GetFileFieldDownloadURL() method of the RDORepository object. Use a try/catch block in case the operation fails.
    Copy
    try
    {
        response = proxy.Repositories.RDO.GetFileFieldDownloadURL(downloadUrlRequest);
    }
    catch (Exception ex)
    {
        Console.WriteLine("An error occurred: {0}", ex.Message);
        return false;
    }<br />
  8. Check for success:
  9. Copy
    //Check for success
    if (!response.Success)
    {
        Console.WriteLine("GetFileFieldDownloadURLReceived returned: {0}", response.URL);
        return true;
    }
    else
    {
        Console.WriteLine("An GetFileFieldDownloadURL operation was not successful.{0}{1}", Environment.NewLine, response.Message);
        return false;
    }

    // NOTE: For an example of reading the file, see DocumentRepository.Download_Document_Native

    Note: For an example of how to download a file, see the sample code for downloading a native file in Download a native file.

Delete RDOs

To delete an RDO instance:

  1. Instantiate the client proxy and set the workspace ID in the APIOptions object as shown in Create object types.
  2. Call the DeleteSingle() method of the RDORepository object and passing it the RDO ID to delete the RDO. Use a try/catch block in case the operation fails.
    Copy
    try
    {
        proxy.Repositories.RDO.DeleteSingle(rdoToDeleteID.Value);
    }
    catch (APIException ex)
    {
     //Exceptions are returned as an APIException
         Console.WriteLine("An error occurred: {0}", ex.Message);
         return false;
    }

Admin-level RDOs

When working with admin-level RDO, you must specify -1 as the WorkspaceID in the APIOptions object:

Copy
proxy.APIOptions.WorkspaceID = -1;

The subsequent operations - create, read, update, delete, and query - use the same workflow as the workspace-level RDO.

The artifact type of the RDO is set by the DescriptorArtifactTypeID property. The following example demonstrates how to use the property when creating an admin-level RDO:

  • Create a parent object type.
  • Read the object type to get the ArtifactID of the object type.
  • Create the parent RDO with the object type set as the DescriptorArtifactTypeID.
  • Create the object type for the admin-level RDO with the parent object type.
  • Read the object type.
  • Create the RDO with the object type.
Copy
public bool Create_RDOWithParent_Workflow(Client.SamplesLibrary.Helper.IHelper helper, IRSAPIClient proxy)
{
    // Set the ForContext for the method.
    kCura.Relativity.Client.SamplesLibrary.Logging.ISampleLogger logger = _logger.ForContext("MethodName", new StackFrame(0).GetMethod().Name, false);

    try
    {
        // Set the workspace ID to -1 for admin level
        proxy.APIOptions.WorkspaceID = -1;

        // Step 1: Create parent object type
        Client.DTOs.ObjectType parentObjectTypeDTO = new Client.DTOs.ObjectType();
        parentObjectTypeDTO.Name = string.Format("API-ParentOT-{0}", Guid.NewGuid().ToString().Substring(0, 10));
        parentObjectTypeDTO.ParentArtifactTypeID = 1;
        parentObjectTypeDTO.SnapshotAuditingEnabledOnDelete = true;
        parentObjectTypeDTO.Pivot = true;
        parentObjectTypeDTO.Sampling = true;
        parentObjectTypeDTO.PersistentLists = false;
        parentObjectTypeDTO.CopyInstancesOnWorkspaceCreation = false;
        parentObjectTypeDTO.CopyInstancesOnParentCopy = false;

        int parentObjectTypeArtifactID = proxy.Repositories.ObjectType.CreateSingle(parentObjectTypeDTO);
        logger.LogDebug("Parent object type artifactID: {parentObjectTypeArtifactID}", parentObjectTypeArtifactID);

        // Read the object type back to get DescriptorArtifactTypeID
        parentObjectTypeDTO = proxy.Repositories.ObjectType.ReadSingle(parentObjectTypeArtifactID);

        // Step 2: Create parent RDO
        Client.DTOs.RDO parentRDO = new Client.DTOs.RDO();
        parentRDO.ArtifactTypeID = parentObjectTypeDTO.DescriptorArtifactTypeID;
        parentRDO.Fields.Add(new DTOs.FieldValue("Name", string.Format("API-ParentRDO-{0}", Guid.NewGuid().ToString().Substring(0, 10))));
        int parentRDOArtifactID = proxy.Repositories.RDO.CreateSingle(parentRDO);
        logger.LogDebug("Parent RDO artifactID: {parentRDOArtifactID}", parentRDOArtifactID);

        // Step 3: Create object type with parent object type
        Client.DTOs.ObjectType objectType = new Client.DTOs.ObjectType();
        objectType.Name = string.Format("API-OT-{0}", Guid.NewGuid().ToString().Substring(0, 10));
        objectType.ParentArtifactTypeID = parentObjectTypeDTO.DescriptorArtifactTypeID; // Set the parent artifact to the object type created above
        objectType.SnapshotAuditingEnabledOnDelete = true;
        objectType.Pivot = true;
        objectType.Sampling = true;
        objectType.PersistentLists = false;
        objectType.CopyInstancesOnWorkspaceCreation = false;
        objectType.CopyInstancesOnParentCopy = false;
        int objectTypeArtifactID = proxy.Repositories.ObjectType.CreateSingle(objectType);
        logger.LogDebug("Object type artifactID: {objectTypeArtifactID}", objectTypeArtifactID);

        // Read the object type back to get DescriptorArtifactTypeID
        objectType = proxy.Repositories.ObjectType.ReadSingle(objectTypeArtifactID);

        // Step 4: Create RDO with with parent as another RDO
        Client.DTOs.RDO rdo = new Client.DTOs.RDO();
        rdo.ArtifactTypeID = objectType.DescriptorArtifactTypeID;
        rdo.ParentArtifact = new DTOs.Artifact(parentRDOArtifactID); // Set the parent artifact to the RDO created above
        rdo.Fields.Add(new DTOs.FieldValue("Name", string.Format("API-RDO-{0}", Guid.NewGuid().ToString().Substring(0, 10))));
        int rdoArtifactID = proxy.Repositories.RDO.CreateSingle(parentRDO);
        logger.LogDebug("RDO artifactID: {rdoArtifactID}", rdoArtifactID);

        // Clean everything up
        proxy.Repositories.RDO.DeleteSingle(rdoArtifactID);
        proxy.Repositories.ObjectType.DeleteSingle(objectTypeArtifactID);
        proxy.Repositories.RDO.DeleteSingle(parentRDOArtifactID);
        proxy.Repositories.ObjectType.DeleteSingle(parentObjectTypeArtifactID);

        return true;
    }
    catch(Exception ex)
    {
        logger.LogError(ex, "Error creating RDO with parent workflow");
        return false;
    }
}