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

C# CRM插件:沙盒中的自定义异常,c#,.net-4.0,dynamics-crm,.net-4.5,dynamics-crm-2013,C#,.net 4.0,Dynamics Crm,.net 4.5,Dynamics Crm 2013,我有一个Dynamics CRM 2013插件正在沙箱中执行 此代码具有以下自定义异常类: [Serializable] public class PluginValidationException : Exception { public PluginValidationException() { } protected PluginValidationException(SerializationInf

我有一个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)
        {
        }
    }
在插件中引发此异常时,将导致一个通用错误窗口,日志文件中没有详细信息:

未处理的异常: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(布尔performDuplicateCheck,Guid auditingTransactionId) 在Microsoft.Crm.Application.Platform.EntityProxy.Create中(布尔performDuplicateCheck) 在Microsoft.Crm.Application.Platform.EntityProxy.CreateAndRetrieve(字符串[]列集,布尔performDuplicateCheck)中 位于Microsoft.Crm.Application.WebServices.InlineEdit.CommandBase.UpdateEntity(实体,布尔检索) 在Microsoft.Crm.Application.WebServices.InlineEdit.SaveCommand.ExecuteCommand(字符串命令XML)中 位于Microsoft.Crm.Application.WebServices.InlineEdit.CommandBase.Execute(字符串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:Create of contact]

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

System.Runtime.Serialization.SerializationException:程序集“Demo.DemoPlugin,版本=1.0.0.0,区域性=中性,PublicKeyToken=FBB51BA1BA1E588D276”中的类型“Demo.Helpers.PlugInvalizationException”未标记为可序列化。 在Microsoft.Crm.Sandbox.SandboxAppDomainHelper.Execute(IServiceEndpointNotificationService serviceBusService、IOOrganizationServiceFactory organizationServiceFactory、String pluginTypeName、String pluginConfiguration、String PluginSecureConfiguration、IPluginExecutionContext requestContext) 在Microsoft.Crm.Sandbox.SandboxWorker.Execute(SandboxCallInfo callInfo、SandboxPluginExecutionContext requestContext、Guid pluginAssemblyId、Int32 sourceHash、String assemblyName、Guid pluginTypeId、String pluginTypeName、String pluginConfiguration、String PlugInsureConfig、SandboxRequestCounter和workerCounter)

根据一些阅读资料,我不认为这是一个bug,而是因为自定义
异常
类在.NET4(我使用的是.NET4.5)中不受信任

有人知道如何创建一个自定义的异常类来处理CRM沙箱吗。我正在使用自定义异常类,因为我捕获错误,需要区分
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);                    
        }

我不认为你想要什么是可能的,如果你想要在错误对话框中显示消息,你必须抛出
InvalidPluginExecutionException

对于同步插件,可以选择显示自定义错误 在web应用程序的错误对话框中通过 插件引发InvalidPlugineExecutionException异常,异常为 自定义消息字符串作为异常消息属性值

建议插件只通过 InvalidPlugineExecutionException返回平台


作为一方,我不会费心检查插件本身的注册情况,这种情况在2013年没有多大意义。回到CRM 4,插件必须手动注册,这有一定的道理。现在我们有了解决方案,正确的注册是一项开发和测试任务,而不是运行时检查。

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

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

下面是一个示例插件,其中有些示例有效,有些则无效:

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,");
    }
}
我相信这是一个实际的错误,我已经打开了与微软的支持票,我们将看看他们是否同意

更新

我和微软一起创建了一张罚单:(不确定这些数字是什么意思,但它们都在主题中,希望将来对某人有益:REG:115122213520585 SRXCAP:1318824373ID)。他们确认CRM中不支持沙盒插件的自定义异常


请向上投票,让Microsoft修复此问题,或者至少更好地处理它

我不希望在错误对话框中显示消息,我希望自定义异常,这样我可以
捕获它并处理它-如果它返回到平台,代码将抛出
InvalidPlugineExecutionException
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;
    }
}