2014-04-06 69 views
5

我有一個在沙盒中執行的Dynamics CRM 2013插件。CRM插件:沙盒中的自定義例外

該代碼具有以下自定義異常類:

[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.Runtim e.Serialization.SerializationException:Microsoft Dynamics CRM遇到錯誤。在Microsoft.Crm.Application.Platform.ServiceCommands.CreateCommand.Execute -2147220970 調用堆棧 在Microsoft.Crm.Application.Platform.ServiceCommands.PlatformCommand.XrmExecuteInternal() :針對管理員或支持參考號:#1355B4E4Detail () at Microsoft.Crm.Application.Platform.EntityProxy.Create(Boolean performDuplicateCheck,Guid auditingTransactionId) at Microsoft.Crm.Application.Platform.EntityProxy.Create(Boolean performDuplicateCheck) at Microsoft.Crm.Application.Platform.EntityProxy .CreateAndRetrieve(String [] columnSet,Boolean performDuplicateCheck) at Microsoft.Crm.Application.WebServices.InlineEdit.CommandBase.U pdateEntity(實體的實體,布爾檢索) 在Microsoft.Crm.Application.WebServices.InlineEdit.SaveCommand.ExecuteCommand(字符串commandXml) 在Microsoft.Crm.Application.WebServices.InlineEdit.CommandBase.Execute(字符串commandXml) 系統.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,字符串pluginTypeName,字符串pluginConfiguration,字符串pluginSecureConfig,IPluginExecutionContext的RequestContext) 在Microsoft.Crm.Sandbox.SandboxWorker.Execute(SandboxCallInfo callInfo,SandboxPluginExecutionContext的RequestContext,的Guid pluginAssemblyId ,的Int32 sourceHash,字符串的AssemblyName,的Guid pluginTypeId,字符串pluginTypeName,字符串pluginConfiguration,字符串pluginSecureConfig,SandboxRequestCounter & workerCounter)

我不這樣做,根據一些閱讀,認爲這是一個錯誤 - 而這是因爲定製Exception類不是,本質上是tr從.NET 4開始(我正在使用.NET 4.5。)

有誰知道如何製作一個自定義異常類,它將與CRM Sandbox一起工作。我正在使用自定義異常類,因爲我發現錯誤並需要區分InvalidPluginExecutionException因插件未正確註冊而導致的異常。

已更新2014年4月8日

這裏是映入例外,有顯著簡化了把它#2插件代碼:

 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);      
     } 
+0

我有一個類似的問題。你能解決這個問題嗎? – Daryl

+0

在我的測試中,默認的Exception.StackTrace實現會執行某種序列化,如果Exception不是標準類型(即自定義異常),則會阻止訪問堆棧跟蹤。你見過類似的東西嗎? – Daryl

回答

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,"); 
    } 
} 

這可以從最上面的嘗試捕捉被稱爲在你的插件像這樣:

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中不受支持。

請投票選舉此Connect Ticket微軟修復此問題或至少處理它更好!

+0

哇,謝謝你在這方面的工作。當時,我當時放棄了,並沒有使用「異常」模式 - 這使得代碼變得很糟糕。我轉向其他項目,從來沒有回到它,但它一直在我的腦海。我將在這個週末花一些時間來實現我的目標。再次感謝您付出努力並在此分享! – Nicknow

+0

@Nicknow我爲插件創建了一個簡單的Assert語句,聲明所有必需的屬性存在於預實體中。它會拋出一個自定義異常,這是C#的標準最佳實踐。希望人們能夠投票連接票和微軟讓我們的生活更美好! – Daryl

2

我不認爲你想要什麼是可能的,如果你想在該錯誤對話框中輸入消息,你必須拋出InvalidPluginExecutionException

Handle Exceptions in Plug-Ins

對於同步插件,您可以選擇通過讓 插件拋出InvalidPluginExecutionException異常與 自定義消息顯示在Web應用程序的錯誤對話框自定義錯誤 消息字符串作爲例外消息屬性值

建議插件僅將 InvalidPluginExecutionException返回給平臺。

作爲一個方面,我不會理會檢查登記在插件本身,這一局面不CRM 4做出了很大的意義在2013年又回到了插件必須手動註冊了有一些意義。現在我們有解決方案,正確的註冊是一項開發和測試任務 - 不是運行時間檢查。

+0

我不想在錯誤對話框中輸入消息,我想要一個自定義異常,這樣我就可以「抓住」它並處理它 - 如果它返回到平臺,代碼將拋出「InvalidPluginExecutionException」。我完全不同意在代碼中不驗證 - 任何未驗證的插件都不會通過代碼審查。即使插件現在在解決方案中,他們的註冊也可以通過插件註冊工具進行更改 - 即使在管理解決方案中。不檢查有效執行是不好的做法B/C支票很便宜,但故障排除可能會非常昂貴。 – Nicknow

+0

另外,自定義異常在非沙箱模式下工作得很好。我的代碼捕獲異常,將詳細信息寫入跟蹤服務(或者配置了其他日誌服務),並且 - 如果合適的話 - 使用用戶友好的多語言支持消息拋出「InvalidPluginExecutionException」。 – Nicknow

+0

當你說'我的代碼捕獲異常'時,代碼在哪裏運行? –

相關問題