Sample: Bulk delete exported records

 

Applies To: Dynamics 365 (online), Dynamics 365 (on-premises), Dynamics CRM 2016, Dynamics CRM Online

This sample code is for Microsoft Dynamics 365 (online & on-premises). Download the Microsoft Dynamics CRM SDK package. It can be found in the following location in the download package:

SampleCode\CS\DataManagement\BulkDelete\BulkDeleteBackup.cs

Requirements

For more information about the requirements for running the sample code provided in this SDK, see Use the sample and helper code.

Demonstrates

This sample shows how to perform a bulk deletion of records that were previously exported from Microsoft Dynamics 365 by using the Export to Excel option.

Example


using System;
using System.ServiceModel;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;

namespace Microsoft.Crm.Sdk.Samples
{
    /// <summary>
    /// This sample shows how to perform a bulk delete asynchronous 
    /// operation on the records that have been manually exported 
    /// from Microsoft Dynamics CRM by using Export to Excel in a grid's view.
    /// </summary>
    public class BulkDeleteBackup
    {
        #region Class Level Members
        private OrganizationServiceProxy _serviceProxy;
        private BulkDeleteResponse _bulkDeleteResponse;
        private Guid? _asyncOperationId;
        private Guid? _bulkDeleteOperationId;
        #endregion

        #region How To Sample Code

        /// <summary>
        /// Run the sample.
        /// </summary>
        /// <param name="serverConfig">configuration for the server.</param>
        /// <param name="promptToDelete">
        /// whether or not to prompt the user to delete created records.
        /// </param>
        public void Run(ServerConnection.Configuration serverConfig, bool promptToDelete)
        {
            using (_serviceProxy = new OrganizationServiceProxy(serverConfig.OrganizationUri, serverConfig.HomeRealmUri,
                                                                     serverConfig.Credentials, serverConfig.DeviceCredentials))
            {
                // This statement is required to enable early-bound type support.
                _serviceProxy.EnableProxyTypes();

                PerformBulkDeleteBackup();
                DeleteRequiredRecords(promptToDelete);
            }
        }

        /// <summary>
        /// Performs the main operation of the sample - performs a bulk delete on inactive
        /// opportunities and activities to remove them from the system.
        /// </summary>
        private void PerformBulkDeleteBackup()
        {
            try
            {
                // Query for a system user to send an email to after the bulk delete
                // operation completes.
                var userRequest = new WhoAmIRequest();
                var userResponse = (WhoAmIResponse)_serviceProxy.Execute(userRequest);
                Guid currentUserId = userResponse.UserId;

                // Create a condition for a bulk delete request.
                // NOTE: This sample uses very specific queries for deleting records
                // that have been manually exported in order to free space.
                QueryExpression opportunitiesQuery = BuildOpportunityQuery();

                // Create the bulk delete request.
                BulkDeleteRequest bulkDeleteRequest = new BulkDeleteRequest();

                // Set the request properties.
                bulkDeleteRequest.JobName = "Backup Bulk Delete";

                // Querying activities
                bulkDeleteRequest.QuerySet = new QueryExpression[]
                {
                    opportunitiesQuery,
                    BuildActivityQuery(Task.EntityLogicalName),
                    BuildActivityQuery(Fax.EntityLogicalName),
                    BuildActivityQuery(PhoneCall.EntityLogicalName),
                    BuildActivityQuery(Email.EntityLogicalName),
                    BuildActivityQuery(Letter.EntityLogicalName),
                    BuildActivityQuery(Appointment.EntityLogicalName),
                    BuildActivityQuery(ServiceAppointment.EntityLogicalName),
                    BuildActivityQuery(CampaignResponse.EntityLogicalName),
                    BuildActivityQuery(RecurringAppointmentMaster.EntityLogicalName)
                };

                // Set the start time for the bulk delete.
                bulkDeleteRequest.StartDateTime = DateTime.Now;

                // Set the required recurrence pattern.
                bulkDeleteRequest.RecurrencePattern = String.Empty;

                // Set email activity properties.
                bulkDeleteRequest.SendEmailNotification = false;
                bulkDeleteRequest.ToRecipients = new Guid[] { currentUserId };
                bulkDeleteRequest.CCRecipients = new Guid[] { };

                // Submit the bulk delete job.
                // NOTE: Because this is an asynchronous operation, the response will be immediate.
                _bulkDeleteResponse =
                    (BulkDeleteResponse)_serviceProxy.Execute(bulkDeleteRequest);
                Console.WriteLine("The bulk delete operation has been requested.");

                CheckSuccess();
            }
            catch (System.Web.Services.Protocols.SoapException)
            {
                // Perform error handling here.
                throw;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// This method will query for the BulkDeleteOperation until it has been
        /// completed or until the designated time runs out.  It then checks to see if
        /// the operation was successful.
        /// </summary>
        private void CheckSuccess()
        {
            // Query for bulk delete operation and check for status.
            QueryByAttribute bulkQuery = new QueryByAttribute(
                BulkDeleteOperation.EntityLogicalName);
            bulkQuery.ColumnSet = new ColumnSet(true);

            // NOTE: When the bulk delete operation was submitted, the GUID that was
            // returned was the asyncoperationid, not the bulkdeleteoperationid.
            bulkQuery.Attributes.Add("asyncoperationid");
            _asyncOperationId = _bulkDeleteResponse.JobId;
            bulkQuery.Values.Add(_asyncOperationId);

            // With only the asyncoperationid at this point, a RetrieveMultiple is
            // required to get the
            // bulk delete operation created above.
            EntityCollection entityCollection = _serviceProxy.RetrieveMultiple(bulkQuery);
            BulkDeleteOperation createdBulkDeleteOperation = null;

            // Monitor the async operation via polling until it is complete or max
            // polling time expires.
            const int ARBITRARY_MAX_POLLING_TIME = 60;
            int secondsTicker = ARBITRARY_MAX_POLLING_TIME;
            while (secondsTicker > 0)
            {
                // Make sure the async operation was retrieved.
                if (entityCollection.Entities.Count > 0)
                {
                    // Grab the one bulk operation that has been created.
                    createdBulkDeleteOperation = (BulkDeleteOperation)entityCollection.Entities[0];

                    // Check the operation's state.
                    if (createdBulkDeleteOperation.StateCode.Value != BulkDeleteOperationState.Completed)
                    {
                        // The operation has not yet completed.  Wait a second for the
                        // status to change.
                        System.Threading.Thread.Sleep(1000);
                        secondsTicker--;

                        // Retrieve a fresh version of the bulk delete operation.
                        entityCollection = _serviceProxy.RetrieveMultiple(bulkQuery);
                    }
                    else
                    {
                        // Stop polling as the operation's state is now complete.
                        secondsTicker = 0;
                    }
                }
                else
                {
                    // Wait a second for async operation to activate.
                    System.Threading.Thread.Sleep(1000);
                    secondsTicker--;

                    // Retrieve the entity again.
                    entityCollection = _serviceProxy.RetrieveMultiple(bulkQuery);
                }
            }

            // Validate that the operation was completed.
            if (createdBulkDeleteOperation != null)
            {
                _bulkDeleteOperationId = createdBulkDeleteOperation.BulkDeleteOperationId;
                if (createdBulkDeleteOperation.StateCode.Value != BulkDeleteOperationState.Completed)
                {
                    Console.WriteLine(
                        "Polling for the BulkDeleteOperation took longer than allowed ({0} seconds).",
                        ARBITRARY_MAX_POLLING_TIME);
                }
                else
                {
                    Console.WriteLine("The BulkDeleteOperation succeeded.\r\n  Successes: {0}, Failures: {1}",
                        createdBulkDeleteOperation.SuccessCount,
                        createdBulkDeleteOperation.FailureCount);
                }
            }
            else
            {
                Console.WriteLine("The BulkDeleteOperation could not be retrieved.");
            }
        }

        /// <summary>
        /// Builds a query that matches all opportunities that are not in the open state.
        /// </summary>
        private static QueryExpression BuildOpportunityQuery()
        {
            // Create a query that will match all opportunities that do not have a state
            // of open.
            var closedCondition = new ConditionExpression(
                "statecode", ConditionOperator.NotEqual, (int)OpportunityState.Open);

            // Create a filter expression for a bulk delete request.
            var closedFilter = new FilterExpression();
            closedFilter.Conditions.Add(closedCondition);

            var queryExpression = new QueryExpression();

            queryExpression.EntityName = Opportunity.EntityLogicalName;
            queryExpression.Criteria = closedFilter;

            // Return all records
            queryExpression.Distinct = false;

            return queryExpression;
        }

        /// <summary>
        /// Builds a query which will match all activities that are in the canceled or
        /// completed state.
        /// </summary>
        private static QueryExpression BuildActivityQuery(String entityName)
        {
            var canceledCondition = new ConditionExpression(
                "statecode", ConditionOperator.Equal, (int)ActivityPointerState.Canceled);
            var completedCondition = new ConditionExpression(
                "statecode", ConditionOperator.Equal, (int)ActivityPointerState.Completed);

            var closedFilter = new FilterExpression(LogicalOperator.Or);
            closedFilter.Conditions.AddRange(canceledCondition, completedCondition);

            var queryExpression = new QueryExpression();

            queryExpression.EntityName = entityName;
            queryExpression.Criteria = closedFilter;

            queryExpression.Distinct = false;

            return queryExpression;
        }

        /// <summary>
        /// This method deletes the AsyncOperation and BulkDeleteOperation that were
        /// created in the database, if the user confirms that deleting these is
        /// desired.
        /// </summary>
        private void DeleteRequiredRecords(bool promptToDelete)
        {
            var toBeDeleted = true;
            if (promptToDelete)
            {
                // Ask the user if the created entities should be deleted.
                Console.Write("\nDo you want these entity records deleted? (y/n) [y]: ");
                String answer = Console.ReadLine();
                if (answer.StartsWith("y") ||
                    answer.StartsWith("Y") ||
                    answer == String.Empty)
                {
                    toBeDeleted = true;
                }
                else
                {
                    toBeDeleted = false;
                }
            }

            if (toBeDeleted)
            {
                // Delete the bulk delete operation so that it won't clutter the
                // database.
                if (_bulkDeleteOperationId.HasValue)
                {
                    _serviceProxy.Delete(
                        BulkDeleteOperation.EntityLogicalName,
                        _bulkDeleteOperationId.Value);
                }

                if (_asyncOperationId.HasValue)
                {
                    _serviceProxy.Delete(
                        AsyncOperation.EntityLogicalName, _asyncOperationId.Value);
                }

                Console.WriteLine("The AsyncOperation and BulkDeleteOperation have been deleted.");
            }
        }

        #endregion How To Sample Code

        #region Main method

        /// <summary>
        /// Standard Main() method used by most SDK samples.
        /// </summary>
        /// <param name="args"></param>
        static public void Main(string[] args)
        {
            try
            {
                // Obtain the target organization's Web address and client logon 
                // credentials from the user.
                ServerConnection serverConnect = new ServerConnection();
                ServerConnection.Configuration config = serverConnect.GetServerConfiguration();

                var app = new BulkDeleteBackup();
                app.Run(config, true);
            }
            catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> ex)
            {
                Console.WriteLine("The application terminated with an error.");
                Console.WriteLine("Timestamp: {0}", ex.Detail.Timestamp);
                Console.WriteLine("Code: {0}", ex.Detail.ErrorCode);
                Console.WriteLine("Message: {0}", ex.Detail.Message);
                Console.WriteLine("Plugin Trace: {0}", ex.Detail.TraceText);
                Console.WriteLine("Inner Fault: {0}",
                    null == ex.Detail.InnerFault ? "No Inner Fault" : "Has Inner Fault");
            }
            catch (System.TimeoutException ex)
            {
                Console.WriteLine("The application terminated with an error.");
                Console.WriteLine("Message: {0}", ex.Message);
                Console.WriteLine("Stack Trace: {0}", ex.StackTrace);
                Console.WriteLine("Inner Fault: {0}",
                    null == ex.InnerException.Message ? "No Inner Fault" : ex.InnerException.Message);
            }
            catch (System.Exception ex)
            {
                Console.WriteLine("The application terminated with an error.");
                Console.WriteLine(ex.Message);

                // Display the details of the inner exception.
                if (ex.InnerException != null)
                {
                    Console.WriteLine(ex.InnerException.Message);

                    FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> fe = ex.InnerException
                        as FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>;
                    if (fe != null)
                    {
                        Console.WriteLine("Timestamp: {0}", fe.Detail.Timestamp);
                        Console.WriteLine("Code: {0}", fe.Detail.ErrorCode);
                        Console.WriteLine("Message: {0}", fe.Detail.Message);
                        Console.WriteLine("Plugin Trace: {0}", fe.Detail.TraceText);
                        Console.WriteLine("Inner Fault: {0}",
                            null == fe.Detail.InnerFault ? "No Inner Fault" : "Has Inner Fault");
                    }
                }
            }
            // Additional exceptions to catch: SecurityTokenValidationException, ExpiredSecurityTokenException,
            // SecurityAccessDeniedException, MessageSecurityException, and SecurityNegotiationException.

            finally
            {
                Console.WriteLine("Press <Enter> to exit.");
                Console.ReadLine();
            }
        }
        #endregion Main method
    }
}

See Also

BulkDeleteRequest
Delete data in bulk
Run bulk delete
Recurrence pattern in asynchronous job execution
Sample: Bulk delete records that match common criteria

Microsoft Dynamics 365

© 2016 Microsoft. All rights reserved. Copyright