如何安全地忽略 Dynamics CRM 插件中的错误?

How to safely ignore an error in a Dynamics CRM plugin?

我在执行某些操作的自定义实体的创建(同步,post-操作)上注册了一个 CRM 插件,我希望创建操作能够成功,尽管插件中存在错误。出于性能原因,我还希望插件在创建记录时立即触发,因此不希望使插件异步。我通过执行以下操作实现了这一点:

public class FooPlugin : IPlugin
{
    public FooPlugin(string unsecureInfo, string secureInfo) { }

    public void Execute(IServiceProvider serviceProvider)
    {
        try
        {
            // Boilerplate
            var context = (IPluginExecutionContext) serviceProvider.GetService(typeof (IPluginExecutionContext));
            var serviceFactory = (IOrganizationServiceFactory) serviceProvider.GetService(typeof (IOrganizationServiceFactory));
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            // Additional validation omitted
            var targetEntity = (Entity) context.InputParameters["Target"];

            UpdateFrobber(service, (EntityReference)targetEntity["new_frobberid"]);
            CreateFollowUpFlibber(service, targetEntity);
            CloseTheEntity(service, targetEntity);
        }
        catch (Exception ex)
        {
            // Send an email but do not re-throw the exception 
            // because we don't want a failure to roll-back the transaction.
            try
            {
                SendEmailForException(ex, context);
            }
            catch { }
        }
    }
}

但是,当发生错误时(例如在UpdateFrobber(...)中),服务客户端会收到此异常:

System.ServiceModel.FaultException`1[Microsoft.Xrm.Sdk.OrganizationServiceFault]: 
There is no active transaction. This error is usually caused by custom plug-ins
that ignore errors from service calls and continue processing.

Server stack trace: 
   at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ref ProxyRpc rpc)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]: 
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(ref MessageData msgData, Int32 type)
   at Microsoft.Xrm.Sdk.IOrganizationService.Create(Entity entity)
   at Microsoft.Xrm.Sdk.Client.OrganizationServiceProxy.CreateCore(Entity entity)
   at Microsoft.Xrm.Sdk.Client.OrganizationServiceProxy.Create(Entity entity)
   at Microsoft.Xrm.Client.Services.OrganizationService.<>c__DisplayClassd.<Create>b__c(IOrganizationService s)
   at Microsoft.Xrm.Client.Services.OrganizationService.InnerOrganizationService.UsingService(Func`2 action)
   at Microsoft.Xrm.Client.Services.OrganizationService.Create(Entity entity)
   at MyClientCode() in MyClientCode.cs: line 100

我的猜测是,发生这种情况是因为 UpdateFrobber(...) 使用从插件派生的 IOrganizationService 实例,因此它进行的任何 CRM 服务调用都参与与插件相同的事务,如果那些 "child" 操作失败,会导致整个事务回滚。这个对吗?是否有 "safe" 方法可以忽略同步插件中 "child" 操作的错误?也许是一种不重复使用插件上下文的实例化 IOrganizationService 实例的方法?

如果相关,我们是 运行 CRM 2013,本地。

当您的插件参与数据库事务时,您不能忽略来自子插件的未处理异常。

但是,当您的插件在部分受信任模式下运行 On Premise 时,您实际上可以创建自己的 OrganizationServiceProxy 实例并使用它来访问 CRM。请务必引用您的插件正在执行的服务器以避免 "double hop" 问题。

如果真的需要,我会创建一个 ContinueOnError = true 的 ExecuteMultipleRequest,对于您的电子邮件,您可以检查 ExecuteMultipleResponse...

不过看起来有点矫枉过正。

如果 运行 在异步模式下,您可以捕获异常。捕获异常时一定要验证你的模式。

示例代码:

try
{
    ExecuteTransactionResponse response = 
        (ExecuteTransactionResponse)service.Execute(exMultReq);
}

catch (Exception ex)
{
    errored = true;
    if (context.Mode == 0) //0 sync, 1 Async. 
        throw new InvalidPluginExecutionException(
            $"Execute Multiple Transaction 
            Failed.\n{ex.Message}\n{innermessage}", ex);
}

if(errored == true)
{
    //Do more stuff to handle it, such as Log the failure.
}

同步插件不可能这样做。

更详细的总结,解释执行模式和用例可以在我的博客上找到:https://helpfulbit.com/handling-exceptions-in-plugins/

干杯。