/// <inheritdoc/> protected override object ExecuteInternal(IOrganizationService org, IAttributeMetadataCache metadata, IQueryExecutionOptions options) { // Check if the query is allowed if (options.Cancelled) { return(null); } if (options.BlockDeleteWithoutWhere && !FetchXml.Items.OfType <FetchEntityType>().Single().Items.OfType <filter>().Any()) { throw new InvalidOperationException("DELETE without WHERE is blocked by your settings"); } var meta = metadata[EntityName]; // If we are using a bulk delete job, start the job if (options.UseBulkDelete && Extensions.Count == 0 && meta.IsIntersect != true) { var query = ((FetchXmlToQueryExpressionResponse)org.Execute(new FetchXmlToQueryExpressionRequest { FetchXml = Serialize(FetchXml) })).Query; var bulkDelete = new BulkDeleteRequest { JobName = $"SQL 4 CDS {meta.DisplayCollectionName.UserLocalizedLabel.Label} Bulk Delete Job", QuerySet = new[] { query }, StartDateTime = DateTime.Now, RunNow = true, RecurrencePattern = String.Empty, SendEmailNotification = false, ToRecipients = new Guid[0], CCRecipients = new Guid[0] }; org.Execute(bulkDelete); return("Bulk delete job started"); } // Otherwise, get the records to delete var count = 0; var entities = RetrieveAll(org, metadata, options).Entities; if (entities == null) { return(null); } // Check again if the query is allowed if (!options.ConfirmDelete(entities.Count, meta)) { throw new OperationCanceledException("DELETE cancelled by user"); } ExecuteMultipleRequest multiple = null; // Delete hte records in batches foreach (var entity in entities) { if (options.Cancelled) { break; } if (options.BatchSize == 1) { options.Progress($"Deleting {meta.DisplayName.UserLocalizedLabel.Label} {count + 1:N0} of {entities.Count:N0}..."); org.Execute(CreateDeleteRequest(meta, entity)); count++; } else { if (multiple == null) { multiple = new ExecuteMultipleRequest { Requests = new OrganizationRequestCollection(), Settings = new ExecuteMultipleSettings { ContinueOnError = false, ReturnResponses = false } }; } multiple.Requests.Add(CreateDeleteRequest(meta, entity)); if (multiple.Requests.Count == options.BatchSize) { options.Progress($"Deleting {meta.DisplayCollectionName.UserLocalizedLabel.Label} {count + 1:N0} - {count + multiple.Requests.Count:N0} of {entities.Count:N0}..."); var resp = (ExecuteMultipleResponse)org.Execute(multiple); if (resp.IsFaulted) { throw new ApplicationException($"Error deleting {meta.DisplayCollectionName.UserLocalizedLabel.Label}"); } count += multiple.Requests.Count; multiple = null; } } } if (!options.Cancelled && multiple != null) { options.Progress($"Deleting {meta.DisplayCollectionName.UserLocalizedLabel.Label} {count + 1:N0} - {count + multiple.Requests.Count:N0}..."); var resp = (ExecuteMultipleResponse)org.Execute(multiple); if (resp.IsFaulted) { throw new ApplicationException($"Error deleting {meta.DisplayCollectionName.UserLocalizedLabel.Label}"); } count += multiple.Requests.Count; } return($"{count:N0} {meta.DisplayCollectionName.UserLocalizedLabel.Label} deleted"); }
public override string Execute(IDictionary <string, DataSource> dataSources, IQueryExecutionOptions options, IDictionary <string, Type> parameterTypes, IDictionary <string, object> parameterValues) { _executionCount++; try { if (!dataSources.TryGetValue(DataSource, out var dataSource)) { throw new QueryExecutionException("Missing datasource " + DataSource); } List <Entity> entities; EntityMetadata meta; Func <Entity, object> primaryIdAccessor; Func <Entity, object> secondaryIdAccessor = null; using (_timer.Run()) { entities = GetDmlSourceEntities(dataSources, options, parameterTypes, parameterValues, out var schema); // Precompile mappings with type conversions meta = dataSource.Metadata[LogicalName]; var attributes = meta.Attributes.ToDictionary(a => a.LogicalName); var dateTimeKind = options.UseLocalTimeZone ? DateTimeKind.Local : DateTimeKind.Utc; var primaryKey = meta.PrimaryIdAttribute; string secondaryKey = null; // Special cases for the keys used for intersect entities if (meta.LogicalName == "listmember") { primaryKey = "listid"; secondaryKey = "entityid"; } else if (meta.IsIntersect == true) { var relationship = meta.ManyToManyRelationships.Single(); primaryKey = relationship.Entity1IntersectAttribute; secondaryKey = relationship.Entity2IntersectAttribute; } var fullMappings = new Dictionary <string, string> { [primaryKey] = PrimaryIdSource }; if (secondaryKey != null) { fullMappings[secondaryKey] = SecondaryIdSource; } var attributeAccessors = CompileColumnMappings(meta, fullMappings, schema, attributes, dateTimeKind); primaryIdAccessor = attributeAccessors[primaryKey]; if (SecondaryIdSource != null) { secondaryIdAccessor = attributeAccessors[secondaryKey]; } } // Check again that the update is allowed. Don't count any UI interaction in the execution time if (options.Cancelled || !options.ConfirmDelete(entities.Count, meta)) { throw new OperationCanceledException("DELETE cancelled by user"); } using (_timer.Run()) { return(ExecuteDmlOperation( dataSource.Connection, options, entities, meta, entity => CreateDeleteRequest(meta, entity, primaryIdAccessor, secondaryIdAccessor), new OperationNames { InProgressUppercase = "Deleting", InProgressLowercase = "deleting", CompletedLowercase = "deleted" })); } } catch (QueryExecutionException ex) { if (ex.Node == null) { ex.Node = this; } throw; } catch (Exception ex) { throw new QueryExecutionException(ex.Message, ex) { Node = this }; } }
public bool ConfirmDelete(int count, EntityMetadata meta) { return(_options.ConfirmDelete(count, meta)); }