If you want to restore any records that have been deleted for any reason (a clumsy employee or a misunderstanding 😉), it’s possible to do so with C#. Let me also note that this feature is still in the preview stage while writing this post.
Retrieving Deleted Records
Before restoring a record, we need to find it first. Deleted records are stored in a special “bin” datasource. You can retrieve them using FetchXML or QueryExpression
FetchXML Example
This example retrieves up to three deleted contact records:
static EntityCollection GetDeletedContactRecordsFetchXml(IOrganizationService service) {
string queryString = @"<fetch datasource='bin'>
<entity name='contact'>
<attribute name='contactid' />
<attribute name='fullname' />
</entity>
</fetch>";
FetchExpression query = new(queryString);
return service.RetrieveMultiple(query);
}
QueryExpression Example
Same retrieval process but using QueryExpression:
static EntityCollection GetDeletedContactRecordsQueryExpression(IOrganizationService service) {
QueryExpression query = new("contact") {
ColumnSet = new ColumnSet("fullname", "contactid"),
DataSource = "bin",
TopCount = 50
};
return service.RetrieveMultiple(query);
}
Restoring a Deleted Record
Once we have the deleted record, we can restore it. Restoring works by creating a new instance of the record with its original values and passing it to the RestoreRequest<T>.
Example: Restoring a Contact Record
static Guid RestoreContactRecord(IOrganizationService service, Guid contactId, string originalFullName) {
Contact contactToRestore = new() {
Id = contactId,
FullName = originalFullName + " (Restored)"
};
RestoreRequest<Contact> request = new() {
Target = contactToRestore
};
var response = (RestoreResponse)service.Execute(request);
return response.id;
}
⚠️Note: At the moment, you can only restore records using their primary key (GUID). Alternate keys are not supported.
Checking if Recycle Bin is Enabled for a Table
Not all tables support the recycle bin. To check which tables have it enabled, query the RecycleBinConfig table:
<fetch>
<entity name='recyclebinconfig'>
<filter type='and'>
<condition attribute='statecode' operator='eq' value='0' />
<condition attribute='isreadyforrecyclebin' operator='eq' value='1' />
</filter>
<link-entity name='entity' from='entityid' to='extensionofrecordid' link-type='inner' alias='entity'>
<attribute name='logicalname' />
<order attribute='logicalname' />
</link-entity>
</entity>
</fetch>
Disabling Recycle Bin for a Table
Admins can disable the recycle bin for a specific table by updating the RecycleBinConfig record.
static void DisableRecycleBinForTable(IOrganizationService service, Guid tableEntityId) {
QueryExpression query = new("recyclebinconfig") {
ColumnSet = new ColumnSet("recyclebinconfigid")
};
LinkEntity entityLink = query.AddLink("entity", "extensionofrecordid", "entityid");
entityLink.LinkCriteria.AddCondition("extensionofrecordid", ConditionOperator.Equal, tableEntityId);
EntityCollection recyclebinconfigs = service.RetrieveMultiple(query);
if (recyclebinconfigs.Entities.Count.Equals(1)) {
var id = recyclebinconfigs.Entities[0].GetAttributeValue<Guid>("recyclebinconfigid");
Entity recyclebinconfig = new("recyclebinconfig", id) {
Attributes = {
{ "statecode", new OptionSetValue(1) },
{ "statuscode", new OptionSetValue(2) }
}
};
service.Update(recyclebinconfig);
} else {
throw new Exception($"Recycle bin configuration for table '{tableEntityId}' not found.");
}
}
Conclusion
The recycle bin feature in Dynamics 365 is a great safety net for accidental deletions. Ensure your tables support the recycle bin, and disable it where necessary. Happy coding!
Leave a comment