【问题标题】:How to safely ignore an error in a Dynamics CRM plugin?如何安全地忽略 Dynamics CRM 插件中的错误?
【发布时间】:2015-03-17 13:02:19
【问题描述】:

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

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 服务调用都参与与插件相同的事务,如果这些“子”操作失败,则会导致整个事务要回滚。它是否正确?是否有一种“安全”的方法可以忽略同步插件中“子”操作的错误?也许是一种实例化不重用插件上下文的 IOrganizationService 实例的方法?

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

【问题讨论】:

  • Caleb 你确定插件在那之后没有执行吗?我的意思是您的方法触发了另一个错误的插件,如果它是同步的,那么您正在查看的插件会因此而失败。为了防止这种情况发生,您可以将子插件移动到后期异步,或者您可以在子插件中管理异常。

标签: dynamics-crm dynamics-crm-2013


【解决方案1】:

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

但是,当您的插件在部分信任模式下在本地运行时,您实际上可以创建自己的 OrganizationServiceProxy 实例并使用它来访问 CRM。请确保引用您的插件正在执行的服务器以避免“双跳”问题。

【讨论】:

  • 我听说 ExecuteMultipleRequest 也会在事务之外执行,但我还没有测试过...
  • @Henk 当您说要避免“双跳”问题时,您的意思是要避免在与当前服务器/组织不同的服务器/组织上调用方法吗?有没有办法以编程方式从插件的上下文中获取 XRM 连接字符串,这样我就不必在插件注册的不安全信息中提供它?
  • 当服务器尝试使用经过身份验证的用户的身份访问另一台服务器上的资源时,会发生“双跳”。在大多数公司网络中,这是不允许的,因此会失败。允许访问同一服务器上的资源。获取 Organization.svc 的 URL 并非易事。
【解决方案2】:

如果在异步模式下运行,您可以捕获异常。请务必在捕获异常时验证您的模式。

示例代码:

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/

干杯。

【讨论】:

    【解决方案3】:

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

    但看起来有点矫枉过正。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-07-10
      • 2017-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多