Skip to content

Commit

Permalink
Major improvements, more generic code. #195 etc
Browse files Browse the repository at this point in the history
  • Loading branch information
rappen committed Feb 4, 2024
1 parent 4db5e81 commit e80e1ee
Show file tree
Hide file tree
Showing 13 changed files with 867 additions and 970 deletions.
143 changes: 52 additions & 91 deletions BulkDataUpdater/AppCode/BDUAssign.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.ServiceModel;
using System.Windows.Forms;
using XrmToolBox.Extensibility;

Expand Down Expand Up @@ -41,7 +40,8 @@ private void AssignRecords()
{
return;
}
if (MessageBox.Show($"All selected records will unconditionally be reassigned to {txtAssignEntity.Text} {xrmAssignText.Text}.\n" +
var includedrecords = GetIncludedRecords();
if (MessageBox.Show($"{includedrecords.Count()} records will unconditionally be reassigned to {txtAssignEntity.Text} {xrmAssignText.Text}.\n" +
"UI defined rules will NOT be enforced.\n" +
"Plugins and workflows WILL trigger.\n" +
"User privileges WILL be respected.\n\n" +
Expand All @@ -50,10 +50,8 @@ private void AssignRecords()
{
return;
}
tsbCancel.Enabled = true;
splitContainer1.Enabled = false;
working = true;
var includedrecords = GetIncludedRecords();
EnableControls(false, true);
var executeoptions = GetExecuteOptions();
if (job != null && job.Assign != null)
{
Expand All @@ -63,102 +61,65 @@ private void AssignRecords()
{
Message = "Assigning records",
IsCancelable = true,
Work = (bgworker, workargs) =>
Work = (bgworker, workargs) => { AssignRecordsWork(bgworker, workargs, owner, includedrecords, executeoptions); },
PostWorkCallBack = (completedargs) => { BulkRecordsCallback(completedargs, "Assign"); },
ProgressChanged = (changeargs) => { SetWorkingMessage(changeargs.UserState.ToString()); }
});
}

private void AssignRecordsWork(System.ComponentModel.BackgroundWorker bgworker, System.ComponentModel.DoWorkEventArgs workargs, Entity owner, System.Collections.Generic.IEnumerable<Entity> includedrecords, JobExecuteOptions executeoptions)
{
var sw = Stopwatch.StartNew();
var progress = "Starting...";
var total = includedrecords.Count();
var current = 0;
var assigned = 0;
var failed = 0;
var entities = new EntityCollection
{
EntityName = includedrecords.FirstOrDefault().LogicalName
};
foreach (var record in includedrecords)
{
if (bgworker.CancellationPending)
{
var sw = Stopwatch.StartNew();
var total = includedrecords.Count();
var current = 0;
var assigned = 0;
var failed = 0;
var batch = new ExecuteMultipleRequest
{
Settings = new ExecuteMultipleSettings { ContinueOnError = executeoptions.IgnoreErrors },
Requests = new OrganizationRequestCollection()
};
foreach (var record in includedrecords)
{
if (bgworker.CancellationPending)
{
workargs.Cancel = true;
break;
}
current++;
var pct = 100 * current / total;
try
{
var clone = new Entity(record.LogicalName, record.Id);
clone.Attributes.Add("ownerid", owner.ToEntityReference());
var request = new UpdateRequest { Target = clone };
SetBypassPlugins(request, executeoptions.BypassCustom);
if (executeoptions.BatchSize == 1)
{
bgworker.ReportProgress(pct, $"Assigning record {current} of {total}");
Service.Execute(request);
assigned++;
}
else
{
batch.Requests.Add(request);
if (batch.Requests.Count == executeoptions.BatchSize || current == total)
{
bgworker.ReportProgress(pct, $"Assigning records {current - batch.Requests.Count + 1}-{current} of {total}");
var response = Service.Execute(batch) as ExecuteMultipleResponse;
if (!executeoptions.IgnoreErrors && response?.IsFaulted == true)
{
throw new FaultException<OrganizationServiceFault>(response.Responses.FirstOrDefault(r => r.Fault != null).Fault);
}
assigned += response.Responses.Count;
batch.Requests.Clear();
}
}
}
catch (Exception ex)
{
failed++;
if (!executeoptions.IgnoreErrors)
{
throw ex;
}
}
}
sw.Stop();
workargs.Result = new Tuple<int, int, long>(assigned, failed, sw.ElapsedMilliseconds);
},
PostWorkCallBack = (completedargs) =>
workargs.Cancel = true;
break;
}
current++;
var clone = new Entity(record.LogicalName, record.Id);
clone.Attributes.Add("ownerid", owner.ToEntityReference());
entities.Entities.Add(clone);
if (entities.Entities.Count >= executeoptions.BatchSize || current == total)
{
working = false;
tsbCancel.Enabled = false;
if (completedargs.Error != null)
progress = GetProgressDetails(sw, total, current, entities.Entities.Count, failed);
WaitingExecution(bgworker, workargs, executeoptions, progress);
PushProgress(bgworker, progress);
if (entities.Entities.Count == 1)
{
ShowErrorDialog(completedargs.Error, "Assign");
failed += ExecuteRequest(new UpdateRequest { Target = entities.Entities.FirstOrDefault() }, executeoptions);
}
else if (completedargs.Cancelled)
else if (executeoptions.MultipleRequest)
{
if (MessageBox.Show("Operation cancelled!\nRun query to get records again, to verify record information.", "Cancel", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK)
{
RetrieveRecords();
}
failed += ExecuteRequest(new UpdateMultipleRequest { Targets = entities }, executeoptions);
}
else if (completedargs.Result is Tuple<int, int, long> result)
else
{
lblUpdateStatus.Text = $"{result.Item1} records reassigned, {result.Item2} records failed.";
LogUse("Assigned", result.Item1, result.Item3);
if (result.Item2 > 0)
{
LogUse("Failed", result.Item2);
}
if (MessageBox.Show("Assign completed!\nRun query to get records again?", "Bulk Data Updater", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes)
var batch = new ExecuteMultipleRequest
{
RetrieveRecords();
}
Settings = new ExecuteMultipleSettings { ContinueOnError = executeoptions.IgnoreErrors },
Requests = new OrganizationRequestCollection()
};
batch.Requests.AddRange(entities.Entities.Select(e => new UpdateRequest { Target = e }));
batch.Requests.ToList().ForEach(r => SetBypassPlugins(r, executeoptions.BypassCustom));
failed += ExecuteRequest(batch, executeoptions);
}
splitContainer1.Enabled = true;
},
ProgressChanged = (changeargs) =>
{
SetWorkingMessage(changeargs.UserState.ToString());
assigned += entities.Entities.Count;
entities.Entities.Clear();
}
});
}
sw.Stop();
workargs.Result = new Tuple<int, int, long>(assigned, failed, sw.ElapsedMilliseconds);
}

private void SetAssignFromJob(JobAssign job)
Expand Down
147 changes: 53 additions & 94 deletions BulkDataUpdater/AppCode/BDUDelete.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.ServiceModel;
using System.Windows.Forms;
using XrmToolBox.Extensibility;

Expand All @@ -18,7 +17,8 @@ private void DeleteRecords()
{
return;
}
if (MessageBox.Show("All selected records will unconditionally be deleted.\n" +
var includedrecords = GetIncludedRecords();
if (MessageBox.Show($"{includedrecords.Count()} records will unconditionally be deleted.\n" +
"UI defined rules will NOT be enforced.\n" +
"Plugins and workflows WILL trigger.\n" +
"User privileges WILL be respected.\n\n" +
Expand All @@ -27,11 +27,16 @@ private void DeleteRecords()
{
return;
}
tsbCancel.Enabled = true;
splitContainer1.Enabled = false;
working = true;
var includedrecords = GetIncludedRecords();
var executeoptions = GetExecuteOptions();
if (executeoptions.BatchSize > 1 && executeoptions.MultipleRequest && includedrecords.Count() > 1)
{
if (MessageBox.Show("Note that the new feature DeleteMultiple from Microsoft is not yet available.\nWe will use ExecuteMultiple instead.", "Batch & Multi", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation) == DialogResult.Cancel)
{
return;
}
}
working = true;
EnableControls(false, true);
if (job != null && job.Delete != null)
{
job.Delete.ExecuteOptions = executeoptions;
Expand All @@ -40,101 +45,55 @@ private void DeleteRecords()
{
Message = "Deleting records",
IsCancelable = true,
Work = (bgworker, workargs) =>
Work = (bgworker, workargs) => { DeleteRecordsWork(bgworker, workargs, includedrecords, executeoptions); },
PostWorkCallBack = (completedargs) => { BulkRecordsCallback(completedargs, "Delete"); },
ProgressChanged = (changeargs) => { SetWorkingMessage(changeargs.UserState.ToString()); }
});
}

private void DeleteRecordsWork(System.ComponentModel.BackgroundWorker bgworker, System.ComponentModel.DoWorkEventArgs workargs, System.Collections.Generic.IEnumerable<Entity> includedrecords, JobExecuteOptions executeoptions)
{
var sw = Stopwatch.StartNew();
var progress = "Starting...";
var total = includedrecords.Count();
var current = 0;
var deleted = 0;
var failed = 0;
var batch = new ExecuteMultipleRequest
{
Settings = new ExecuteMultipleSettings { ContinueOnError = executeoptions.IgnoreErrors },
Requests = new OrganizationRequestCollection()
};
foreach (var record in includedrecords)
{
if (bgworker.CancellationPending)
{
var sw = Stopwatch.StartNew();
var total = includedrecords.Count();
var current = 0;
var deleted = 0;
var failed = 0;
var batch = new ExecuteMultipleRequest
{
Settings = new ExecuteMultipleSettings { ContinueOnError = executeoptions.IgnoreErrors },
Requests = new OrganizationRequestCollection()
};
foreach (var record in includedrecords)
{
if (bgworker.CancellationPending)
{
workargs.Cancel = true;
break;
}
current++;
var pct = 100 * current / total;
try
{
var request = new DeleteRequest { Target = record.ToEntityReference() };
SetBypassPlugins(request, executeoptions.BypassCustom);
if (executeoptions.BatchSize == 1)
{
bgworker.ReportProgress(pct, $"Deleting record {current} of {total}");
Service.Execute(request);
deleted++;
}
else
{
batch.Requests.Add(request);
if (batch.Requests.Count == executeoptions.BatchSize || current == total)
{
bgworker.ReportProgress(pct, $"Deleting records {current - batch.Requests.Count + 1}-{current} of {total}");
var response = Service.Execute(batch) as ExecuteMultipleResponse;
if (!executeoptions.IgnoreErrors && response?.IsFaulted == true)
{
throw new FaultException<OrganizationServiceFault>(response.Responses.FirstOrDefault(r => r.Fault != null).Fault);
}
deleted += batch.Requests.Count - response.Responses.Count(r => r.Fault != null);
batch.Requests.Clear();
}
}
}
catch (Exception ex)
{
failed++;
batch.Requests.Clear();
if (!executeoptions.IgnoreErrors)
{
throw ex;
}
}
}
sw.Stop();
workargs.Result = new Tuple<int, int, long>(deleted, failed, sw.ElapsedMilliseconds);
},
PostWorkCallBack = (completedargs) =>
workargs.Cancel = true;
break;
}
current++;
var request = new DeleteRequest { Target = record.ToEntityReference() };
SetBypassPlugins(request, executeoptions.BypassCustom);
batch.Requests.Add(request);
if (batch.Requests.Count >= executeoptions.BatchSize || current == total)
{
working = false;
tsbCancel.Enabled = false;
if (completedargs.Error != null)
progress = GetProgressDetails(sw, total, current, batch.Requests.Count, failed);
WaitingExecution(bgworker, workargs, executeoptions, progress);
PushProgress(bgworker, progress);
if (batch.Requests.Count == 1)
{
ShowErrorDialog(completedargs.Error, "Delete");
failed += ExecuteRequest(batch.Requests.FirstOrDefault(), executeoptions);
}
else if (completedargs.Cancelled)
else
{
if (MessageBox.Show("Operation cancelled!\nRun query to get records again, to verify remaining records.", "Cancel", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK)
{
RetrieveRecords();
}
failed += ExecuteRequest(batch, executeoptions);
}
else if (completedargs.Result is Tuple<int, int, long> result)
{
lblUpdateStatus.Text = $"{result.Item1} records deleted, {result.Item2} records failed.";
LogUse("Deleted", result.Item1, result.Item3);
if (result.Item2 > 0)
{
LogUse("Failed", result.Item2);
}
if (MessageBox.Show("Delete completed!\nRun query to get records again?", "Bulk Data Updater", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes)
{
RetrieveRecords();
}
}
splitContainer1.Enabled = true;
},
ProgressChanged = (changeargs) =>
{
SetWorkingMessage(changeargs.UserState.ToString());
deleted += batch.Requests.Count; // - response.Responses.Count(r => r.Fault != null);
batch.Requests.Clear();
}
});
}
sw.Stop();
workargs.Result = new Tuple<int, int, long>(deleted, failed, sw.ElapsedMilliseconds);
}

private void SetDeleteFromJob(JobDelete job)
Expand Down
Loading

0 comments on commit e80e1ee

Please sign in to comment.