CRM插件:沙盒中的自定义异常

时间:2014-04-06 02:13:39

标签: c# .net-4.0 dynamics-crm .net-4.5 dynamics-crm-2013

我在Sandbox中执行了一个Dynamics CRM 2013插件。

此代码具有以下自定义异常类:

    [Serializable]
    public class PluginValidationException : Exception
    {
        public PluginValidationException()
        {
        }

        protected PluginValidationException(SerializationInfo info, StreamingContext context) 
            : base(info, context)
        {            
        }

        public PluginValidationException(string message)
            : base(message)
        {
        }

        public PluginValidationException(string message, Exception inner)
            : base(message, inner)
        {
        }
    }

当插件中抛出此异常时,会导致一般错误窗口,日志文件中没有详细信息:

enter image description here

未处理的异常:System.ServiceModel.FaultException`1 [[Microsoft.Xrm.Sdk.OrganizationServiceFault,Microsoft.Xrm.Sdk,Version = 6.0.0.0,Culture = neutral,PublicKeyToken = 31bf3856ad364e35]]:System。 Runtime.Serialization.SerializationException:Microsoft Dynamics CRM遇到错误。管理员或支持的参考编号:#1355B4E4详细信息:   -2147220970               调用堆栈          在Microsoft.Crm.Application.Platform.ServiceCommands.PlatformCommand.XrmExecuteInternal()    在Microsoft.Crm.Application.Platform.ServiceCommands.CreateCommand.Execute()    在Microsoft.Crm.Application.Platform.EntityProxy.Create(Boolean performDuplicateCheck,Guid auditingTransactionId)    在Microsoft.Crm.Application.Platform.EntityProxy.Create(Boolean performDuplicateCheck)    在Microsoft.Crm.Application.Platform.EntityProxy.CreateAndRetrieve(String [] columnSet,Boolean performDuplicateCheck)    在Microsoft.Crm.Application.WebServices.InlineEdit.CommandBase.UpdateEntity(实体实体,布尔检索)    在Microsoft.Crm.Application.WebServices.InlineEdit.SaveCommand.ExecuteCommand(String commandXml)    在Microsoft.Crm.Application.WebServices.InlineEdit.CommandBase.Execute(String commandXml)           System.Runtime.Serialization.SerializationException:Microsoft Dynamics CRM遇到错误。管理员或支持的参考编号:#1355B4E4   2014-04-06T02:04:30.0972001Z       [Demo.DemoPlugin:Demo.DemoPlugin.BasicCrmPlugin] [d86b89ab-f1bc-e311-9408-000c29254b18:Demo.DemoPlugin.BasicCrmPlugin:创建联系人]

查看CRM跟踪日志显示以下内容:

System.Runtime.Serialization.SerializationException:输入' Demo.Helpers.PluginValidationException'在汇编' Demo.DemoPlugin,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = fbb51ba1e588d276'未标记为可序列化。            在Microsoft.Crm.Sandbox.SandboxAppDomainHelper.Execute(IServiceEndpointNotificationService serviceBusService,IOrganizationServiceFactory organizationServiceFactory,String pluginTypeName,String pluginConfiguration,String pluginSecureConfig,IPluginExecutionContext requestContext)            在Microsoft.Crm.Sandbox.SandboxWorker.Execute(SandboxCallInfo callInfo,SandboxPluginExecutionContext requestContext,Guid pluginAssemblyId,Int32 sourceHash,String assemblyName,Guid pluginTypeId,String pluginTypeName,String pluginConfiguration,String pluginSecureConfig,SandboxRequestCounter& workerCounter)

基于一些阅读,我不相信这是一个错误 - 而是因为自定义Exception类本身不受.NET 4(我使用.NET 4.5)的信任。 )

有没有人知道如何制作适用于CRM Sandbox的自定义异常类。我使用自定义异常类,因为我发现错误并需要区分InvalidPluginExecutionException因插件注册错误而导致的异常。

2014年4月8日更新

以下是插件中捕获异常的代码,并将其放在Stackoverflow上进行了大量简化:

        try
        {
            //TODO: Prevalidation Logic
            ValidatePluginExecution(crmContext, logging, out keyName);
            //TODO: Postvalidation Logic
        }
        catch (PluginValidationException ex)
        {
            //TODO: Specific logging for Plugin Validation Exception
            throw new InvalidPluginExecutionException("Did Not Validate");                    
        }
        catch (InvalidPluginExecutionException ex)
        {
            logging.Write("InvalidPluginExectionException at Plugin Validation");                    
            throw;
        }
        catch (Exception ex)
        {
            logging.Write("Unhandled Exeception During Plugin Validation Operation");
            logging.Write(ex);
            throw new InvalidPluginExecutionException("Error.  Download Log and submit to the Help Desk.", ex);                    
        }

2 个答案:

答案 0 :(得分:2)

我不认为你想要什么是可能的,如果你想在那个错误对话框中留言,你必须抛出InvalidPluginExecutionException

Handle Exceptions in Plug-Ins

  

对于同步插件,您可以选择显示自定义错误   通过让您的Web应用程序的错误对话框中的消息   插件抛出一个InvalidPluginExecutionException异常   自定义消息字符串作为异常消息属性值

     

建议插件只能通过   InvalidPluginExecutionException返回平台。

作为一方,我不打算检查插件本身的注册情况,这种情况在2013年没有多大意义。回到CRM 4,其中插件必须手动注册它有一些意义。现在我们有解决方案,正确的注册是开发和测试任务 - 而不是运行时检查。

答案 1 :(得分:2)

经过一些额外的测试,这是我能够确定的:

显然,只有在明确这样做的情况下,才能访问堆栈跟踪以查找异常。从沙盒插件中抛出异常时,只要异常是CRM平台知道的异常,就不会显示异常的堆栈跟踪。 (不知道它在做什么,这里,但我猜测它正在查看异常的类型,并以不同的方式处理不同的类型)。如果类型未知,则会导致CRM尝试序列化a中不允许的异常,因为它使用反射(为什么必须序列化,不确定)。

这是一个示例插件,其中包含一些有效的示例,还有一些没有实例:

public class TestPlugin: IPlugin
{
    public void Execute(IServiceProvider serviceProvider)
    {
        try
        {
            OtherMethod();
        }
        catch (Exception ex)
        {
            var trace = (ITracingService)serviceProvider.GetService(typeof (ITracingService));
            trace.Trace("Throwing Plugin");
            // Doesn't work
            throw new InvalidPluginExecutionException("Error ", ex);
        }
    }

    // Works:
    //public void Execute(IServiceProvider serviceProvider)
    //{
        //try
        //{
            //OtherMethod();
        //}
        //catch (Exception ex)
        //{
            //var trace = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
            //trace.Trace("Throwing Plugin");
            //throw new InvalidPluginExecutionException("Error " + ex);
        //}
    //}

    // Doesn't Work:
    //public void Execute(IServiceProvider serviceProvider)
    //{
    //    try
    //    {
    //        OtherMethod();
    //    }
    //    catch (Exception ex)
    //    {
    //        throw;
    //    }
    //}

    // Doesn't Work:
    //public void Execute(IServiceProvider serviceProvider)
    //{
    //    try
    //    {
    //        OtherMethod();
    //    }
    //    catch (Exception ex)
    //    {
    //        throw new InvalidPluginExecutionException("Error", ex);
    //    }
    //}

    public void OtherMethod()
    {
        throw new MyException();
    }
}

public class MyException : Exception
{

}

所以回答你的问题:我编写了一个异常处理程序,以便能够遍历内部异常并确定它是否有效抛出:

/// <summary>
/// Exception Handler For Exceptions when executing in Sandbox Isolation Mode
/// </summary>
public class ExceptionHandler
{
    /// <summary>
    /// Determines whether the given exception can be thrown in sandbox mode.
    /// Throws a Safe if it can't
    /// </summary>
    /// <param name="ex">The ex.</param>
    /// <returns></returns>
    /// <exception cref="InvalidPluginExecutionException"></exception>
    /// <exception cref="Exception"></exception>
    public static bool CanThrow(Exception ex)
    {
        var exceptionRootTypeIsValid = IsValidToBeThrown(ex);
        var canThrow = exceptionRootTypeIsValid;
        var innerException = ex.InnerException;

        // While the Exception Types are still valid to be thrown, loop through all inner exceptions, checking for validity
        while (canThrow && innerException != null)
        {
            if (IsValidToBeThrown(ex))
            {
                innerException = innerException.InnerException;
            }
            else
            {
                canThrow = false;
            }
        }

        if (canThrow)
        {
            return true;
        }

        var exceptionMessage = ex.Message +
                                   (ex.InnerException == null
                                       ? string.Empty
                                       : " Inner Exception: " + ex.InnerException.ToStringWithCallStack());

        // ReSharper disable once InvertIf - I like it better this way
        if (exceptionRootTypeIsValid)
        {
            // Attempt to throw the exact Exception Type, with the 
            var ctor = ex.GetType().GetConstructor(new[] { typeof(string) });
            if (ctor != null)
            {
                throw (Exception) ctor.Invoke(new object[] { exceptionMessage });
            }
        }

        throw new Exception(exceptionMessage);
    }

    /// <summary>
    /// Determines whether the specified ex is valid to be thrown.
    /// Current best guess is that it is not 
    /// </summary>
    /// <param name="ex">The ex.</param>
    /// <returns></returns>
    private static bool IsValidToBeThrown(Exception ex)
    {
        var assembly = ex.GetType().Assembly.FullName.ToLower();
        return assembly.StartsWith("mscorlib,") || assembly.StartsWith("microsoft.xrm.sdk,");
    }
}

这可以从插件中最重要的try catch调用,如下所示:

catch (InvalidPluginExecutionException ex)
{
    context.LogException(ex);
    // This error is already being thrown from the plugin, just throw
    if (context.PluginExecutionContext.IsolationMode == (int) IsolationMode.Sandbox)
    {
        if (Sandbox.ExceptionHandler.CanThrow(ex))
        {
            throw;
        }
    }
    else
    {
        throw;
    }
}
catch (Exception ex)
{
    // Unexpected Exception occurred, log exception then wrap and throw new exception
    context.LogException(ex);
    ex = new InvalidPluginExecutionException(ex.Message, ex);
    if (context.PluginExecutionContext.IsolationMode == (int)IsolationMode.Sandbox)
    {
        if (Sandbox.ExceptionHandler.CanThrow(ex))
        {
            // ReSharper disable once PossibleIntendedRethrow - Wrap the exception in an InvalidPluginExecutionException
            throw ex;
        }
    }
    else
    {
        // ReSharper disable once PossibleIntendedRethrow - Wrap the exception in an InvalidPluginExecutionException
        throw ex;
    }
}

我认为这是一个实际的错误,我已经向微软公开了支持票,我们将看看他们是否同意......

<强>更新!!

我与微软创建了一张票:(不确定这些数字是什么意思,但他们在主题中,希望将来对某人有益:REG:115122213520585 SRXCAP:1318824373ID)。他们确实证实了CRM for Sandboxed插件不支持自定义异常。

请对此Connect Ticket进行投票,让Microsoft修复此问题或至少更好地处理它!