Sample: Bulk delete records that match common criteria
Applies To: Dynamics CRM 2013
This sample code is for Microsoft Dynamics CRM 2013 and Microsoft Dynamics CRM Online. Download the Microsoft Dynamics CRM SDK package. It can be found in the following location in the download package:
SampleCode\CS\DataManagement\BulkDelete\BulkDeleteOperations.cs
SampleCode\VB\DataManagement\BulkDelete\BulkDeleteOperations.vb
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 delete records, in bulk, that match common criteria.
Example
using System;
using System.Linq;
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 request to remove the records in
/// Microsoft Dynamics CRM that match certain criteria.
/// </summary>
public class BulkDeleteOperations
{
#region Class Level Members
private OrganizationServiceProxy _serviceProxy;
private ServiceContext _context;
private Guid _bulkDeleteOperationId;
private Guid _asyncOperationId;
#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 = ServerConnection.GetOrganizationProxy(serverConfig))
{
using (_context = new ServiceContext(_serviceProxy))
{
// This statement is required to enable early-bound type support.
_serviceProxy.EnableProxyTypes();
// This statments checks whether Standard Email templates are present
var emailTemplateId = (
from emailTemplate in _context.TemplateSet
where emailTemplate.Title == "Contact Reconnect"
select emailTemplate.Id
).FirstOrDefault();
if (emailTemplateId != Guid.Empty)
{
CreateRequiredRecords();
// Perform the bulk delete. If you want to perform a recurring delete
// operation, then leave this as it is. Otherwise, pass in false as the
// first parameter.
PerformBulkDelete(true, promptToDelete);
}
else
{
throw new ArgumentException("Standard Email Templates are missing");
}
}
}
}
/// <summary>
/// Create an account that will be deleted in the main portion of the sample.
/// </summary>
private void CreateRequiredRecords()
{
var account = new Account
{
Name = "Fourth Coffee",
WebSiteURL = "https://www.fourthcoffee.com/"
};
_serviceProxy.Create(account);
}
/// <summary>
/// Perform the main action of the sample - issuing a BulkDeleteRequest.
/// </summary>
/// <param name="useRecurrence">
/// whether or not to create a recurring BulkDeleteRequest.
/// </param>
private void PerformBulkDelete(bool useRecurrence, bool promptToDelete)
{
try
{
Console.WriteLine("Performing Bulk Delete Operation");
// 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;
Console.WriteLine(" Requesting user retrieved.");
// Create a condition for a bulk delete request.
// NOTE: If no records are found that match this condition, the bulk delete
// will not fail. It will succeed with 0 successes and 0 failures.
var deleteCondition = new ConditionExpression(
"name", ConditionOperator.Equal, "Fourth Coffee");
// Create a fiter expression for the bulk delete request.
var deleteFilter = new FilterExpression();
deleteFilter.Conditions.Add(deleteCondition);
// Create the bulk delete query set.
var bulkDeleteQuery = new QueryExpression
{
EntityName = Account.EntityLogicalName,
Distinct = false,
Criteria = deleteFilter
};
// Create the bulk delete request.
var bulkDeleteRequest = new BulkDeleteRequest
{
JobName = "Sample Bulk Delete",
QuerySet = new[] { bulkDeleteQuery },
StartDateTime = DateTime.Now,
ToRecipients = new[] { currentUserId },
CCRecipients = new Guid[] {},
SendEmailNotification = true,
RecurrencePattern = String.Empty
};
// Create a recurring BulkDeleteOperation.
if (useRecurrence)
{
bulkDeleteRequest.RecurrencePattern = "FREQ=DAILY;INTERVAL=1;";
}
// Submit the bulk delete job.
// NOTE: Because this is an asynchronous operation, the response will be
// immediate.
var bulkDeleteResponse =
(BulkDeleteResponse)_serviceProxy.Execute(bulkDeleteRequest);
_asyncOperationId = bulkDeleteResponse.JobId;
Console.WriteLine(" The Bulk Delete Request was made and the Bulk\n" +
" Delete Operation should be created.");
// To monitor the asynchronous operation, retrieve the
// bulkdeleteoperation object.
// NOTE: There will be a period of time from when the async operation
// request was submitted to the time when a successful query for that
// async operation can be made. When using plug-ins, events can be
// subscribed to that will fire when the async operation status changes.
var bulkQuery = new QueryByAttribute();
bulkQuery.ColumnSet = new ColumnSet(true);
bulkQuery.EntityName = BulkDeleteOperation.EntityLogicalName;
// NOTE: When the bulk delete operation was submitted, the GUID that was
// returned was the asyncoperationid, not the bulkdeleteoperationid.
bulkQuery.Attributes.Add("asyncoperationid");
bulkQuery.Values.Add(bulkDeleteResponse.JobId);
// With only the asyncoperationid at this point, a RetrieveMultiple is
// required to get the bulk delete operation created above.
var entityCollection =
_serviceProxy.RetrieveMultiple(bulkQuery);
BulkDeleteOperation createdBulkDeleteOperation = null;
// When creating a recurring BulkDeleteOperation, the BulkDeleteOperation
// will be in suspended status after the current instance has completed.
// When creating a non-recurring BulkDeleteOperation, it will be in
// Completed status when it is finished.
var bulkOperationEnded = useRecurrence
? BulkDeleteOperationState.Suspended
: BulkDeleteOperationState.Completed;
createdBulkDeleteOperation = RetrieveBulkDeleteOperation(
bulkQuery, entityCollection, bulkOperationEnded);
_bulkDeleteOperationId = createdBulkDeleteOperation.Id;
if (createdBulkDeleteOperation != null)
{
// If the BulkDeleteOperation is recurring, the status will be
// "Waiting" after the operation completes this instance. If it is
// non-recurring, the status will be "Succeeded".
var bulkOperationSuccess = useRecurrence
? bulkdeleteoperation_statuscode.Waiting
: bulkdeleteoperation_statuscode.Succeeded;
InspectBulkDeleteOperation(createdBulkDeleteOperation,
bulkOperationEnded, bulkOperationSuccess, useRecurrence);
DeleteRecords(promptToDelete);
}
else
{
Console.WriteLine(" The Bulk Delete Operation could not be retrieved.");
}
}
catch (System.Web.Services.Protocols.SoapException)
{
// Perform error handling here.
throw;
}
}
/// <summary>
/// Inspect and display information about the created BulkDeleteOperation.
/// </summary>
/// <param name="createdBulkDeleteOperation">
/// the BulkDeleteOperation to inspect.
/// </param>
/// <param name="bulkOperationEnded">
/// the statecode that will tell us if the BulkDeleteOperation is ended.
/// </param>
/// <param name="bulkOperationSuccess">
/// the statuscode that will tell us if the BulkDeleteOperation was successful.
/// </param>
/// <param name="useRecurrence">
/// whether or not the BulkDeleteOperation is a recurring operation.
/// </param>
private void InspectBulkDeleteOperation(
BulkDeleteOperation createdBulkDeleteOperation,
BulkDeleteOperationState bulkOperationEnded,
bulkdeleteoperation_statuscode bulkOperationSuccess,
bool useRecurrence)
{
// Validate that the operation was completed.
if (createdBulkDeleteOperation.StateCode != bulkOperationEnded)
{
// This will happen if it took longer than the polling time allowed
// for this operation to complete.
Console.WriteLine(" Completion of the Bulk Delete took longer\n" +
" than the polling time allotted.");
}
else if (createdBulkDeleteOperation.StatusCode.Value
!= (int)bulkOperationSuccess)
{
Console.WriteLine(" The Bulk Delete operation failed.");
}
else if (!useRecurrence)
{
// Check for the number of successful deletes.
var successfulDeletes = createdBulkDeleteOperation.SuccessCount ?? 0;
Console.WriteLine(" {0} records were successfully deleted",
successfulDeletes);
// Check for any failures that may have occurred during the bulk
// delete operation.
if (createdBulkDeleteOperation.FailureCount > 0)
{
Console.WriteLine(" {0} records failed to be deleted:",
createdBulkDeleteOperation.FailureCount);
// Query for all the failures.
var failureQuery = new QueryByAttribute();
failureQuery.ColumnSet = new ColumnSet(true);
failureQuery.EntityName = BulkDeleteFailure.EntityLogicalName;
failureQuery.Attributes.Add("bulkdeleteoperationid");
var bulkDeleteOperationId =
createdBulkDeleteOperation.BulkDeleteOperationId ?? Guid.Empty;
failureQuery.Values.Add(bulkDeleteOperationId);
// Retrieve the bulkdeletefailure objects.
EntityCollection entityCollection = _serviceProxy.RetrieveMultiple(
failureQuery);
// Examine each failure for information regarding the failure.
foreach (BulkDeleteFailure failureOperation in
entityCollection.Entities)
{
// Process failure information.
Console.WriteLine(String.Format(
" {0}, {1}",
failureOperation.RegardingObjectId.Name,
failureOperation.RegardingObjectId.Id));
}
}
}
else
{
// NOTE: If recurrence is used, we cannot reliably retrieve data
// about the records that were deleted, since a sub-BulkDeleteOperation
// is created by Microsoft Dynamics CRM that does not have any fields tying it back to the
// Asynchronous operation or the BulkDeleteOperation. This makes it
// unreliable to know which subprocess to retrieve.
Console.WriteLine(" The recurring Bulk Delete Operation was created successfully.");
}
}
/// <summary>
/// Retrieves the BulkDeleteOperation, but it's not necessarily created
/// immediately, so this method uses polling.
/// </summary>
/// <param name="bulkQuery">the query to find the BulkDeleteOperation.</param>
/// <param name="entityCollection">the initial results of the query.</param>
/// <param name="operationEndedStatus">
/// the statecode that will indicate that the operation has ended.
/// </param>
private BulkDeleteOperation RetrieveBulkDeleteOperation(
QueryByAttribute bulkQuery, EntityCollection entityCollection,
BulkDeleteOperationState operationEndedStatus)
{
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.
// NOTE: If a recurrence for the BulkDeleteOperation was
// specified, the state of the operation will be Suspended,
// not Completed, since the operation will run again in the
// future.
if (createdBulkDeleteOperation.StateCode !=
operationEndedStatus)
{
// 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;
Console.WriteLine(
" The BulkDeleteOperation record has been retrieved.");
}
}
else
{
// Wait a second for async operation to activate.
System.Threading.Thread.Sleep(1000);
secondsTicker--;
// Retrieve the entity again.
entityCollection = _serviceProxy.RetrieveMultiple(bulkQuery);
}
}
return createdBulkDeleteOperation;
}
/// <summary>
/// Deletes records that were created in the sample.
/// </summary>
/// <param name="prompt">whether or not to prompt the user for deletion.</param>
private void DeleteRecords(bool prompt)
{
var toBeDeleted = true;
if (prompt)
{
// 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.
_serviceProxy.Delete(
BulkDeleteOperation.EntityLogicalName, _bulkDeleteOperationId);
var asyncOperationEntity = _serviceProxy.Retrieve(
AsyncOperation.EntityLogicalName,
_asyncOperationId,
new ColumnSet("statecode", "asyncoperationid"));
var asyncOperation = asyncOperationEntity.ToEntity<AsyncOperation>();
if (asyncOperation.StateCode != AsyncOperationState.Completed)
{
// We have to update the AsyncOperation to be in a Completed state
// before we can delete it.
asyncOperation.StateCode = AsyncOperationState.Completed;
_serviceProxy.Update(asyncOperation);
}
_serviceProxy.Delete(
AsyncOperation.EntityLogicalName, _asyncOperationId);
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 BulkDeleteOperations();
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
}
}
Imports Microsoft.VisualBasic
Imports System
Imports System.ServiceModel
Imports Microsoft.Crm.Sdk.Messages
Imports Microsoft.Xrm.Sdk
Imports Microsoft.Xrm.Sdk.Client
Imports Microsoft.Xrm.Sdk.Query
Namespace Microsoft.Crm.Sdk.Samples
''' <summary>
''' This sample shows how to perform a bulk delete request to remove the records in
''' Microsoft Dynamics CRM that match certain criteria.
''' </summary>
Public Class BulkDeleteOperations
#Region "Class Level Members"
Private _serviceProxy As OrganizationServiceProxy
Private _context As ServiceContext
Private _bulkDeleteOperationId As Guid
Private _asyncOperationId As Guid
#End Region
#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 Sub Run(ByVal serverConfig As ServerConnection.Configuration,
ByVal promptToDelete As Boolean)
_serviceProxy = ServerConnection.GetOrganizationProxy(serverConfig)
Using _serviceProxy
_context = New ServiceContext(_serviceProxy)
Using _context
' This statement is required to enable early-bound type support.
_serviceProxy.EnableProxyTypes()
' This statments checks whether Standard Email templates are present
Dim emailTemplateId = ( _
From emailTemplate In _context.TemplateSet _
Where emailTemplate.Title.Equals("Contact Reconnect") _
Select emailTemplate.Id).FirstOrDefault()
If Not emailTemplateId.Equals(Guid.Empty) Then
CreateRequiredRecords()
' Perform the bulk delete. If you want to perform a recurring delete
' operation, leave this as it is. Otherwise, pass in false as the
' first parameter.
PerformBulkDelete(True, promptToDelete)
Else
Throw New ArgumentException("Standard Email Templates are missing")
End If
End Using
End Using
End Sub
''' <summary>
''' Create an account that will be deleted in the main portion of the sample.
''' </summary>
Private Sub CreateRequiredRecords()
Dim account = New Account With {.Name = "Fourth Coffee",
.WebSiteURL = "https://www.fourthcoffee.com/"}
_serviceProxy.Create(account)
End Sub
''' <summary>
''' Perform the main action of the sample - issuing a BulkDeleteRequest.
''' </summary>
''' <param name="useRecurrence">
''' whether or not to create a recurring BulkDeleteRequest.
''' </param>
Private Sub PerformBulkDelete(ByVal useRecurrence As Boolean,
ByVal promptToDelete As Boolean)
Try
Console.WriteLine("Performing Bulk Delete Operation")
' Query for a system user to send an email to after the bulk delete
' operation completes.
Dim userRequest = New WhoAmIRequest()
Dim userResponse = CType(_serviceProxy.Execute(userRequest),
WhoAmIResponse)
Dim currentUserId As Guid = userResponse.UserId
Console.WriteLine(" Requesting user retrieved.")
' Create a condition for a bulk delete request.
' NOTE: If no records are found matching this condition, the bulk delete
' will not fail. It will succeed with 0 successes and 0 failures.
Dim deleteCondition =
New ConditionExpression("name",
ConditionOperator.Equal,
"Fourth Coffee")
' Create a fiter expression for the bulk delete request.
Dim deleteFilter = New FilterExpression()
deleteFilter.Conditions.Add(deleteCondition)
' Create the bulk delete query set.
Dim bulkDeleteQuery = New QueryExpression With
{
.EntityName = Account.EntityLogicalName,
.Distinct = False,
.Criteria = deleteFilter
}
' Create the bulk delete request.
Dim bulkDeleteRequest = New BulkDeleteRequest With
{
.JobName = "Sample Bulk Delete",
.QuerySet = {bulkDeleteQuery},
.StartDateTime = Date.Now,
.ToRecipients = {currentUserId},
.CCRecipients = New Guid() {},
.SendEmailNotification = True,
.RecurrencePattern = String.Empty
}
' Create a recurring BulkDeleteOperation.
If useRecurrence Then
bulkDeleteRequest.RecurrencePattern = "FREQ=DAILY;INTERVAL=1;"
End If
' Submit the bulk delete job.
' NOTE: Because this is an asynchronous operation, the response will be
' immediate.
Dim bulkDeleteResponse = CType(_serviceProxy.Execute(bulkDeleteRequest),
BulkDeleteResponse)
_asyncOperationId = bulkDeleteResponse.JobId
Console.WriteLine(" The Bulk Delete Request was made and the Bulk" _
& vbLf & " Delete Operation should be created.")
' To monitor the asynchronous operation, we must retrieve the
' bulkdeleteoperation object.
' NOTE: There will be a period of time from when the async operation
' request was sumbitted to the time when a successful query for that
' async operation can be made. When using plug-ins, events can be
' subscribed to that will fire when the async operation status changes.
Dim bulkQuery = New QueryByAttribute()
bulkQuery.ColumnSet = New ColumnSet(True)
bulkQuery.EntityName = BulkDeleteOperation.EntityLogicalName
' NOTE: When the bulk delete operation was submitted, the GUID that was
' returned was the asyncoperationid, not the bulkdeleteoperationid.
bulkQuery.Attributes.Add("asyncoperationid")
bulkQuery.Values.Add(bulkDeleteResponse.JobId)
' With only the asyncoperationid at this point, a RetrieveMultiple is
' required to get the bulk delete operation created above.
Dim entityCollection = _serviceProxy.RetrieveMultiple(bulkQuery)
Dim createdBulkDeleteOperation As BulkDeleteOperation = Nothing
' When creating a recurring BulkDeleteOperation, the BulkDeleteOperation
' will be in suspended status after the current instance has completed.
' When creating a non-recurring BulkDeleteOperation, it will be in
' Completed status when it is finished.
Dim bulkOperationEnded = If(useRecurrence,
BulkDeleteOperationState.Suspended,
BulkDeleteOperationState.Completed)
createdBulkDeleteOperation = RetrieveBulkDeleteOperation(bulkQuery,
entityCollection,
bulkOperationEnded)
_bulkDeleteOperationId = createdBulkDeleteOperation.Id
If createdBulkDeleteOperation IsNot Nothing Then
' If the BulkDeleteOperation is recurring, the status will be
' "Waiting" after the operation completes this instance. If it is
' non-recurring, the status will be "Succeeded".
Dim bulkOperationSuccess = If(useRecurrence,
bulkdeleteoperation_statuscode.Waiting,
bulkdeleteoperation_statuscode.Succeeded)
InspectBulkDeleteOperation(createdBulkDeleteOperation,
bulkOperationEnded,
bulkOperationSuccess,
useRecurrence)
DeleteRecords(promptToDelete)
Else
Console.WriteLine(" The Bulk Delete Operation could not be retrieved.")
End If
Catch e1 As System.Web.Services.Protocols.SoapException
' Perform error handling here.
Throw
End Try
End Sub
''' <summary>
''' Inspect and display information about the created BulkDeleteOperation.
''' </summary>
''' <param name="createdBulkDeleteOperation">
''' the BulkDeleteOperation to inspect.
''' </param>
''' <param name="bulkOperationEnded">
''' the statecode that will tell us if the BulkDeleteOperation is ended.
''' </param>
''' <param name="bulkOperationSuccess">
''' the statuscode that will tell us if the BulkDeleteOperation was successful.
''' </param>
''' <param name="useRecurrence">
''' whether or not the BulkDeleteOperation is a recurring operation.
''' </param>
Private Sub InspectBulkDeleteOperation(ByVal createdBulkDeleteOperation As BulkDeleteOperation,
ByVal bulkOperationEnded As BulkDeleteOperationState,
ByVal bulkOperationSuccess As bulkdeleteoperation_statuscode,
ByVal useRecurrence As Boolean)
' Validate that the operation was completed.
If createdBulkDeleteOperation.StateCode <> bulkOperationEnded Then
' This will happen if it took longer than the polling time allowed
' for this operation to complete.
Console.WriteLine(" Completion of the Bulk Delete took longer" _
& vbLf & " than the polling time allotted.")
ElseIf createdBulkDeleteOperation.StatusCode.Value <> CInt(Fix(bulkOperationSuccess)) Then
Console.WriteLine(" The Bulk Delete operation failed.")
ElseIf Not useRecurrence Then
' Check for number of successful deletes.
Dim successfulDeletes = If(createdBulkDeleteOperation.SuccessCount, 0)
Console.WriteLine(" {0} records were successfully deleted",
successfulDeletes)
' Check for any failures that may have occurred during the bulk
' delete operation.
If createdBulkDeleteOperation.FailureCount > 0 Then
Console.WriteLine(" {0} records failed to be deleted:",
createdBulkDeleteOperation.FailureCount)
' Query for all the failures.
Dim failureQuery = New QueryByAttribute()
failureQuery.ColumnSet = New ColumnSet(True)
failureQuery.EntityName = BulkDeleteFailure.EntityLogicalName
failureQuery.Attributes.Add("bulkdeleteoperationid")
Dim bulkDeleteOperationId = If(createdBulkDeleteOperation.BulkDeleteOperationId,
Guid.Empty)
failureQuery.Values.Add(bulkDeleteOperationId)
' Retrieve the bulkdeletefailure objects.
Dim entityCollection As EntityCollection = _serviceProxy.RetrieveMultiple(failureQuery)
' Examine each failure for information regarding the failure.
For Each failureOperation As BulkDeleteFailure In entityCollection.Entities
' Process failure information.
Console.WriteLine(String.Format(" {0}, {1}",
failureOperation.RegardingObjectId.Name,
failureOperation.RegardingObjectId.Id))
Next failureOperation
End If
Else
' NOTE: If recurrence is used, we cannot reliably retrieve data
' about the records that were deleted, since a sub-BulkDeleteOperation
' is created by Microsoft Dynamics CRM that does not have any fields tying it back to the
' Asynchronous operation or the BulkDeleteOperation. This makes it
' unreliable to know which subprocess to retrieve.
Console.WriteLine(" The recurring Bulk Delete Operation was created successfully.")
End If
End Sub
''' <summary>
''' Retrieves the BulkDeleteOperation, but it's not necessarily created
''' immediately, so this method uses polling.
''' </summary>
''' <param name="bulkQuery">the query to find the BulkDeleteOperation.</param>
''' <param name="entityCollection">the initial results of the query.</param>
''' <param name="operationEndedStatus">
''' the statecode that will indicate that the operation has ended.
''' </param>
Private Function RetrieveBulkDeleteOperation(ByVal bulkQuery As QueryByAttribute,
ByVal entityCollection As EntityCollection,
ByVal operationEndedStatus As BulkDeleteOperationState) As BulkDeleteOperation
Dim createdBulkDeleteOperation As BulkDeleteOperation = Nothing
' Monitor the async operation via polling until it is complete or max
' polling time expires.
Const ARBITRARY_MAX_POLLING_TIME As Integer = 60
Dim secondsTicker As Integer = ARBITRARY_MAX_POLLING_TIME
Do While secondsTicker > 0
' Make sure the async operation was retrieved
If entityCollection.Entities.Count > 0 Then
' Grab the one bulk operation that has been created
createdBulkDeleteOperation = CType(entityCollection.Entities(0),
BulkDeleteOperation)
' Check the operation's state
' NOTE: If a recurrence for the BulkDeleteOperation was
' specified, then the state of the operation will be Suspended,
' not Completed, since the operation will run again in the
' future.
If createdBulkDeleteOperation.StateCode <> operationEndedStatus Then
' The operation has not yet completed. Wait a second for
' the status to change.
System.Threading.Thread.Sleep(1000)
secondsTicker -= 1
' 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
Console.WriteLine(" The BulkDeleteOperation record has been retrieved.")
End If
Else
' Wait a second for async operation to activate.
System.Threading.Thread.Sleep(1000)
secondsTicker -= 1
' Retrieve the entity again.
entityCollection = _serviceProxy.RetrieveMultiple(bulkQuery)
End If
Loop
Return createdBulkDeleteOperation
End Function
''' <summary>
''' Deletes records that were created in the sample.
''' </summary>
''' <param name="prompt">whether or not to prompt the user for deletion.</param>
Private Sub DeleteRecords(ByVal prompt As Boolean)
Dim toBeDeleted = True
If prompt Then
' Ask the user if the created entities should be deleted.
Console.Write(vbLf & "Do you want these entity records deleted? (y/n) [y]: ")
Dim answer As String = Console.ReadLine()
If answer.StartsWith("y") OrElse
answer.StartsWith("Y") OrElse
answer = String.Empty Then
toBeDeleted = True
Else
toBeDeleted = False
End If
End If
If toBeDeleted Then
' Delete the bulk delete operation so that it won't clutter the
' database.
_serviceProxy.Delete(BulkDeleteOperation.EntityLogicalName,
_bulkDeleteOperationId)
Dim asyncOperationEntity =
_serviceProxy.Retrieve(AsyncOperation.EntityLogicalName,
_asyncOperationId,
New ColumnSet("statecode", "asyncoperationid"))
Dim asyncOperation_renamed =
asyncOperationEntity.ToEntity(Of AsyncOperation)()
If asyncOperation_renamed.StateCode <> AsyncOperationState.Completed Then
' We have to update the AsyncOperation to be in a Completed state
' before we can delete it.
asyncOperation_renamed.StateCode = AsyncOperationState.Completed
_serviceProxy.Update(asyncOperation_renamed)
End If
_serviceProxy.Delete(AsyncOperation.EntityLogicalName, _asyncOperationId)
Console.WriteLine(" The AsyncOperation and BulkDeleteOperation have been deleted.")
End If
End Sub
#End Region ' How To Sample Code
#Region "Main method"
''' <summary>
''' Standard Main() method used by most SDK samples.
''' </summary>
''' <param name="args"></param>
Public Shared Sub Main(ByVal args() As String)
Try
' Obtain the target organization's web address and client logon
' credentials from the user.
Dim serverConnect As New ServerConnection()
Dim config As ServerConnection.Configuration =
serverConnect.GetServerConfiguration()
Dim app = New BulkDeleteOperations()
app.Run(config, True)
Catch ex As FaultException(Of Microsoft.Xrm.Sdk.OrganizationServiceFault)
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}",
If(Nothing Is ex.Detail.InnerFault, "No Inner Fault", "Has Inner Fault"))
Catch ex As System.TimeoutException
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}",
If(Nothing Is ex.InnerException.Message, "No Inner Fault", ex.InnerException.Message))
Catch ex As System.Exception
Console.WriteLine("The application terminated with an error.")
Console.WriteLine(ex.Message)
' Display the details of the inner exception.
If ex.InnerException IsNot Nothing Then
Console.WriteLine(ex.InnerException.Message)
Dim fe As FaultException(Of Microsoft.Xrm.Sdk.OrganizationServiceFault) =
TryCast(ex.InnerException,
FaultException(Of Microsoft.Xrm.Sdk.OrganizationServiceFault))
If fe IsNot Nothing Then
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}",
If(Nothing Is fe.Detail.InnerFault, "No Inner Fault", "Has Inner Fault"))
End If
End If
' Additional exceptions to catch: SecurityTokenValidationException, ExpiredSecurityTokenException,
' SecurityAccessDeniedException, MessageSecurityException, and SecurityNegotiationException.
Finally
Console.WriteLine("Press <Enter> to exit.")
Console.ReadLine()
End Try
End Sub
#End Region ' Main method
End Class
End Namespace
See Also
BulkDeleteRequest
Delete data in bulk
Run bulk delete
Recurrence pattern in asynchronous job execution