长期数据保留可自动将数据从Microsoft Dataverse事务数据库传输到托管数据湖,以实现经济高效的存档存储。 首先 配置表以便长期保留。 然后,创建用于定义要存档的数据的保留策略。 计划的保留期运行与条件匹配的传输行。
Important
若要使用所有长期数据保留功能,必须满足 Dataverse 长期数据保留概述中所述的两项要求。
检索保留的数据
可以使用 FetchXml 和 QueryExpression 检索已保留的数据。
通过使用 FetchXml,将 fetch 元素datasource 属性值设置为 "retained".
<fetch datasource="retained">
<entity name="account">
<attribute name="accountId" />
</entity>
</fetch>
通过使用 QueryExpression ,将 QueryExpression.DataSource 属性 设置为 retained。
Note
目前无法通过具有 OData 样式查询的 Dataverse Web API 检索保留的数据。 可以将 FetchXml 与 Dataverse Web API 配合使用。
设置保留策略
若要创建保留策略,请使用 API、创建者门户或解决方案安装。 下面的代码示例演示如何使用 API 创建保留策略。
以下代码使用 组织服务 和 IOrganizationService.Create(Entity) 方法 创建一个保留策略,用于保留所有已关闭的商机,并且每年运行一次。 有效的重复参数是DAILY,WEEKLYMONTHLY和YEARLY。 若要仅运行保留一次,请将重复周期值设置为空。
public void CreateRetentionConfig(IOrganizationService orgService)
{
Entity retentionConfig = new Entity("retentionconfig");
retentionConfig["retentionconfigid"] = Guid.NewGuid();
retentionConfig["entitylogicalname"] = "incident";
retentionConfig["name"] = "Retain all closed opportunities";
retentionConfig["uniquename"] = "ui_RetainAllClosedOpportunities";
retentionConfig["statecode"] = new OptionSetValue(0);
retentionConfig["statuscode"] = new OptionSetValue(10);
retentionConfig["criteria"] = "<fetch> " +
"<entity name=\"opportunity\"> " +
"<attribute name=\"name\" /> " +
"<attribute name=\"statecode\" />" +
"<attribute name=\"actualvalue\" />" +
"<attribute name=\"actualclosedate\" />" +
"<attribute name=\"customerid\" />" +
"<attribute name=\"opportunityid\" />" +
"<order attribute=\"actualclosedate\" descending=\"true\" />" +
"<filter type=\"and\">" +
"<filter type=\"or\">" +
"<condition attribute=\"statecode\" operator=\"eq\" value=\"1\" />" +
"<condition attribute=\"statecode\" operator=\"eq\" value=\"2\" />" +
"</filter>" +
"</filter>" +
"</entity></fetch>";
retentionConfig["starttime"] = DateTime.Parse("2024-05-01T00:00:00");
retentionConfig["recurrence"] = "FREQ=YEARLY;INTERVAL=1";
try
{
var retentionConfigId = orgService.Create(retentionConfig);
Console.WriteLine($"Retention policy created with Id : {retentionConfigId}");
}
catch (Exception ex)
{
throw new Exception($"Create retention policy failed: {ex.Message})", ex);
}
}
此代码的输出是“使用 Id 创建的保留策略:c1a9e932-89f6-4f17-859c-bd2101683263”。
验证保留策略
长期保留过程将数据从 Dataverse 事务存储移动到托管数据湖。 移动到 Data Lake 后,不能再对数据运行事务操作。 请务必确保保留策略正确。 您可以通过在 消息上可选地注册自定义 ValidateRetentionConfig 来添加自己的验证。
class SampleValidateRetentionConfigPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var pluginContext = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
var entityName = pluginContext.PrimaryEntityName;
if( pluginContext.InputParameters.TryGetValue("FetchXml", out var fetchXml) )
{
// Add custom validation against the Fetch XML.
}
else
{
throw new Exception("No critiera provided.");
}
}
}
执行保留操作时的自定义逻辑
长期保留是一个异步过程,每当你设置保留策略时都会运行。 它执行以下操作:
- 标记准备保留的行(记录)。
- 将标记的行复制到 Data Lake。
- 清除源数据库中的各行数据。
- 如果清除失败,则回滚已标记的行。
您可以选择注册自定义插件,以便在行被标记为保留时、行在源端被清除时,或标记为保留的行被回滚时执行。 编写插件代码仅适用于用于 .NET 编程的 SDK。 Web API 不支持插件开发。
行被标记为保留时的自定义逻辑
作为标记行以供保留的一部分,Dataverse 调用 BulkRetain 和 Retain 消息。 可以通过在这些消息执行时注册插件来添加自定义逻辑。 自定义逻辑的示例包括标记更多行以供保留或执行验证,然后再将行标记为保留。
此代码示例演示在保留单个表行期间执行的自定义插件。
class SampleRetainPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var pluginContext = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
var entityName = pluginContext.PrimaryEntityName;
if( pluginContext.InputParameters.TryGetValue("Target", out var _target) )
{
EntityReference target = (EntityReference)_target;
Console.WriteLine($"Request came for table : {target.Name} with id : {target.Id}");
// Add your logic for validation or additional operation.
// For example - you can call Retain on Additional row of another table.
}
else
{
throw new Exception("No target present.");
}
}
}
对于回滚保留操作,请参照前面的示例编写插件,只是要将其注册到 RollbackRetain 消息上。
批量保留中的自定义逻辑
此代码示例演示了在 BulkRetain 消息操作最后一页执行时的自定义逻辑。
class SampleBulkRetainPlugin : IPlugin
{
// Send notification when bulk retain execution is done.
public void Execute(IServiceProvider serviceProvider)
{
var pluginContext = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
var entityName = pluginContext.PrimaryEntityName;
if(pluginContext.OutputParameters != null
&& pluginContext.OutputParameters.TryGetValue("HasMoreRecords", out var _hasMoreRecords) )
{
if(!(bool)_hasMoreRecords)
{
Console.WriteLine("This is a last execution of this request.");
// SendNotifcation that retention for an entity is completed.
}
}
}
}
因保留而删除行时的自定义逻辑
Dataverse 执行 PurgeRetainedContent 消息以删除已成功移动到 Data Lake 的事务数据行。
PurgeRetainedContent 消息在内部执行 Delete 消息操作,以删除其已成功移动的表行。
如果您需要在表级清除操作期间实现自定义逻辑,可以在 PurgeRetainedContent 消息上注册自定义插件。 或者,如果您需要在某一行因保留策略而被删除时调用代码,则可以在 Delete 消息上注册自定义插件。 可以通过检查插件的 ParentContext 属性来确定删除是否由于保留而发生。 由于保留原因触发的 Delete 消息操作的 ParentContext 属性值为“PurgeRetainedContent”。
当行尚未准备好清除时,此代码示例会阻止对表进行清除。
class SamplePurgeRetainedContentPlugin : IPlugin
{
// Block purge if all the rows are not validatd.
public void Execute(IServiceProvider serviceProvider)
{
var pluginContext = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
var entityName = pluginContext.PrimaryEntityName;
if( pluginContext.InputParameters.TryGetValue("MaxVersionToPurge", out var _maxVersiontoPurge) )
{
long MaxVersionToPurge = (long)_maxVersiontoPurge;
var rowsToBePurged = GetToBePurgedRows(entityName, MaxVersionToPurge);
// Add custom validation to process rowsToBePurged.
}
}
public EntityCollection GetToBePurgedRows(string entityName, long maxVersionToPurge)
{
IOrganizationService organizationService; // Create OrgService.
QueryExpression queryExpression = new QueryExpression()
{
EntityName = entityName,
ColumnSet = new ColumnSet(new string[] { "versionnumber", "msft_datastate" })
};
queryExpression.Criteria.AddCondition("msft_datastate", ConditionOperator.Equal, 1);
queryExpression.Criteria.AddCondition("versionnumber", ConditionOperator.LessEqual, maxVersionToPurge);
var response = organizationService.RetrieveMultiple(queryExpression);
return response;
}
}
此代码示例适用于由于保留而导致的删除操作。
class SampleDeletePlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
if (IsDeleteDueToRetention(serviceProvider))
{
// Write your code to handle delete during retention
}
else
{
// Write your code to handle normal delete without retention
}
}
private bool IsDeleteDueToRetention(IServiceProvider serviceProvider)
{
var currentContext = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
while (currentContext != null)
{
if (string.Equals(currentContext.MessageName, "PurgeRetainedContent"))
{
return true;
}
else
{
currentContext = currentContext.ParentContext;
}
}
return false;
}
}
查询保留策略和执行详细信息
系统将保留策略详细信息 RetentionConfig 存储在表中。 它将保留操作的详细信息存储在 RetentionOperation 和 RetentionOperationDetail 表中。 可以查询这些表以获取保留策略和执行详细信息。
下面的代码提供了一些 FetchXML 示例,可用于查询日期保留详细信息表行。 FetchXML 是基于 XML 的专有查询语言。 你可以在基于 SDK 的查询中使用 FetchExpression,也可以在 Web API 中通过 fetchXml 查询字符串使用它。
此代码示例演示了一个简单的查询,用于按名称返回电子邮件订单的所有活动保留策略。
public EntityCollection GetActivePolicies(IOrganizationService orgService)
{
string fetchXml = @"
<fetch>
<entity name='retentionconfig'>
<attribute name='retentionconfigid' />
<attribute name='name' />
<attribute name='createdon' />
<attribute name='starttime' />
<attribute name='recurrence' />
<attribute name='entitylogicalname' />
<attribute name='criteria' />
<order attribute='name' descending='false' />
<filter type='and'>
<condition attribute='entitylogicalname' operator='eq' value='email' />
<condition attribute='statuscode' operator='eq' value='10' />
</filter>
</entity>
</fetch>";
var query = new FetchExpression(fetchXml);
EntityCollection results = orgService.RetrieveMultiple(query);
results.Entities.ToList().ForEach(x => {
Console.WriteLine(x.Attributes["name"]);
});
return(results);
}
FetchXML 查询字符串的更多示例
此代码示例演示如何使用 FetchXML 语句检索电子邮件的所有暂停保留策略。
<fetch>
<entity name="retentionconfig">
<attribute name="retentionconfigid" />
<attribute name="name" />
<attribute name="createdon" />
<attribute name="starttime" />
<attribute name="recurrence" />
<attribute name="entitylogicalname" />
<attribute name="criteria" />
<order attribute="name" descending="false" />
<filter type="and">
<condition attribute="entitylogicalname" operator="eq" value="email" />
<condition attribute="statuscode" operator="eq" value="20" />
</filter>
</entity>
</fetch>
此代码示例演示如何使用 FetchXML 语句检索保留策略的所有保留操作。
<fetch>
<entity name="retentionoperation">
<attribute name="retentionoperationid" />
<attribute name="name" />
<attribute name="statuscode" />
<attribute name="statecode" />
<attribute name="starttime" />
<attribute name="rootentitylogicalname" />
<attribute name="endtime" />
<attribute name="criteria" />
<order attribute="name" descending="false" />
<filter type="and">
<condition
attribute="retentionconfigid"
operator="eq"
value="{35CC1317-20B7-4F4F-829D-5D9D5D77F763}" />
</filter>
</entity>
</fetch>
此代码示例显示一个 FetchXML 语句,该语句检索保留操作的详细信息。
<fetch>
<entity name="retentionoperationdetail">
<attribute name="retentionoperationdetailid" />
<attribute name="name" />
<attribute name="createdon" />
<attribute name="retentionoperationid" />
<attribute name="retentioncount" />
<attribute name="isrootentity" />
<attribute name="failedcount" />
<attribute name="entitylogicalname" />
<order attribute="name" descending="false" />
<filter type="and">
<condition attribute="retentionoperationid" operator="eq" value="{35CC1317-20B7-4F4F-829D-5D9D5D77F763}"/>
</filter>
</entity>
</fetch>
此代码示例演示 FetchXML 语句,该语句检索有关在保留操作期间发生的失败的详细信息。
<fetch>
<entity name="retentionfailuredetail">
<attribute name="retentionfailuredetailid" />
<attribute name="name" />
<attribute name="createdon" />
<attribute name="recordid" />
<attribute name="operation" />
<attribute name="message" />
<order attribute="name" descending="false" />
<filter type="and">
<condition attribute="operationid" operator="eq" value="35CC1317-20B7-4F4F-829D-5D9D5D77F763" />
</filter>
</entity>
</fetch>