2011-12-16 266 views
16

Running Dynamics CRM 2011部署3.需要定期更新數百萬個客戶記錄(增量更新)。使用標準更新(一個接一個)需要幾個星期。此外,我們不想直接觸摸數據庫,因爲它可能會在未來破壞東西。Dynamics CRM 2011批量更新

我們可以使用Dynamics CRM 2011 webservice/REST API中是否有批量更新方法? (WhatWhereHow)

+0

散裝明顯的例子創建或更新MS下面給出的CRM鏈接http://mscrmtutorials.blogspot.in/2014/07/bulk-insert-and-bulk-update-in-ms-crm.html – 2014-11-13 17:41:21

+0

你最終做了什麼?我們使用kingswaysoft – 2017-01-16 20:43:13

回答

11

是的,沒有,主要是沒有。有人可以糾正我,如果我錯了,在這種情況下,我會很樂意編輯/刪除我的答案,但一次只能完成一次。它甚至不嘗試處理基於集合的插入/更新/刪除。因此,除非您直接指導數據庫操作,否則需要數週時間。

webservice does allow for "bulk" inserts/deletes/updates,但我把「bulk」放在引號中,因爲它所做的一切都是建立一個異步過程,它執行所有相關的數據操作 - yep - 一次一個。有一部分SDK解決了這種數據管理(鏈接)。而要以這種方式更新記錄,您必須首先承受選擇所有要更新的數據的開銷,然後創建一個包含數據的xml文件,最後更新數據(請記住:一次一行)。因此,循環訪問數據併爲每個人發出Update請求實際上會更有效率。

(我會注意到,我們的組織沒有經歷就直接DB訪問處理SDK沒有什麼任何問題難忘,也沒看到我個人的互聯網讀數暗示別人有什麼。)

編輯:

見iFirefly的answer低於其他一些優秀的方法來解決這個問題。

1

不知道這將如何與數百萬記錄,但您可以選擇您的記錄,然後單擊功能區中的編輯按鈕。這將彈出「編輯多個記錄」對話框。您所做的任何更改將應用​​於您的所有記錄。

15

我意識到這是帖子已超過2歲,但我可以添加到它,以防其他人閱讀它,並有類似的需求。

彼得馬吉德的答案是針對的,因爲CRM過程一次請求一條記錄。沒有批量編輯按照您尋找的方式工作。如果您需要/希望獲得Microsoft支持,我鼓勵您不要直接聯繫數據庫。

如果您正在查看數百萬條記錄的定期更新,您有幾個選項。考慮使用Scribe或使用CRM SDK開發您自己的定製導入實用程序或腳本。

抄寫員可能會成爲您的最佳選擇,因爲它對數據導入具有成本效益,並允許您輕鬆更新和插入同一文件。

如果你編寫你自己的基於.Net/SDK的實用程序,我會建議使它成爲多線程的,並以編程方式在內存或磁盤上拆分輸入文件,並讓每個線程都使用自己的數據子集 - 當然,如果執行順序不必按照輸入文件的內容按時間順序排列。如果您可以通過多個線程分割和征服輸入文件,則可以大幅減少總體執行時間。 此外,如果您的企業策略允許您訪問其中一個CRM服務器,並且您可以將代碼直接放在服務器上並從那裏執行 - 您可以消除運行代碼的工作站與CRM網絡之間的網絡延遲服務。

最後但並非最不重要的一點,如果大量的導入數據來自另一個系統,您可以編寫一個CRM插件在您的特定實體的CRM中的Retrieve and RetrieveMultiple消息(事件)上運行,以編程方式檢索來自其他系統的期望數據(如果其他系統不可用 - 只需在CRM中使用緩存副本),並使CRM保持最新實時或「最後緩存」的基礎上。這當然是更多的編碼工作,但它可能消除了每隔幾周就要運行一次大型同步作業的需要。

6

我意識到這是一個古老的問題,但它在「CRM大量更新」中出現高位,因此需要在此提及Update Rollup 12 feature ExecuteMultiple - 它不會解決您的問題(大量),因爲iFirefly和Peter指向CRM一次只做一件事。它所做的就是將所有請求打包到一個信封中,讓CRM處理每次更新的執行,並減少應用程序和服務器之間的往返次數,前提是最終會爲每條記錄發出Update請求。

0

我爲Dynamics CRM 2011開發了一個非常大型的數據遷移項目。我們需要在週末加載大約300萬條記錄。我最終構建了一個控制檯應用程序(單線程),並在多臺機器上運行多個實例。每個控制檯應用程序都有一個id(1,2等),並負責根據與應用程序ID相匹配的唯一SQL WHERE子句加載數據段。

你可以對更新做同樣的事情。每個實例都可以查詢要更新的記錄子集,並可以通過SDK執行更新。由於我們在一個週末載入了數百萬條記錄,我認爲您可以在幾個小時內執行數百萬次更新(如果相對較小)。

0

微軟PFE團隊Dynamics CRM中寫道 新Another CRM SDK library是利用並行 來散裝保證線程安全執行請求。

你可以試試:並行執行請求 我很想知道,如果它的工作原理,並擴展到數百萬條記錄。

1

BulkUpdate API適用於我;它比一次更新記錄快10倍。以下是執行批量更新的代碼片段:

public override ExecuteMultipleResponse BulkUpdate(List<Entity> entities) 
    { 
     ExecuteMultipleRequest request = new ExecuteMultipleRequest() 
     { 
      Settings = new ExecuteMultipleSettings() 
      { 
       ContinueOnError = true, 
       ReturnResponses = true 
      }, 
      Requests = new OrganizationRequestCollection() 
     }; 

     for (int i = 0; i < entities.Count; i++) 
     { 
      request.Requests.Add(new UpdateRequest() { Target = entities[i] }); 
     } 

     return (ExecuteMultipleResponse) ServiceContext.Execute(request); 
    } 
0

CRM沒有實現更新批量數據的方式;有三種方法可以提高批量更新操作性能,但在內部它們無法改變CRM更新逐一記錄的事實。 基本思路是:

  • 減少浪費,以CRM服務器進行通信
  • 使用並行同時
  • 確保更新過程中不會觸發任何工作流/插件做多操作的時間。否則,你可能永遠也看不到過程結束...

3種方式來提高批量操作的性能:

  1. 彙總12之後有一個ExecuteMultipleRequest功能,它允許你發送多達1000個請求一次。這意味着您可以節省一些時間從發送1000個請求到CRM Web服務,但是,這些請求會一個接一個地處理。所以如果你的CRM服務器配置的很好,這種方法很可能不會有太大的幫助。
  2. 您可以使用OrganizationServiceContext實例進行批量更新。 OrganizationServiceContext實現工作單元模式,因此您可以執行多個更新並在一次調用中將這些操作傳輸到服務器。與ExecuteMultipleRequest相比,它對請求數量沒有限制,但如果在更新期間遇到故障,它將回滾所有更改。
  3. 使用多線程或多任務。無論哪種方式都會提高速度,但它們很可能會產生一些連接失敗或SQL錯誤,因此您需要在代碼中添加一些重試邏輯。
1

這是一個相當古老的問題,但沒有人提到更新/創建CRM 201X中的大量記錄的禁食方式(但也是最具挑戰性的) - 使用內置導入功能,這是完全可行的CRM SDK。有一個完美的MSDN文章: https://msdn.microsoft.com/en-us/library/gg328321(v=crm.5).aspx。總之,你必須:

1)構建包含要導入數據的Excel文件(簡單地從CRM 201X導出一些數據,並檢查結構的樣子,記得第3列是隱藏的)

2)創建導入映射實體(指定您創建的文件)

3)創建如有必要

4)創建和導入實體的importfile列映射,提供適當的映射

5)使用數據解析ParseImportRequest

6)使用TransformImportRequest

7使用ImportRecordsImportRequest

這種變換分析數據)導入數據是針對2011年CRM的步驟,現在在2017年,我們有更多的版本和它們之間存在細微的差別。檢查MSDN和SDK中提供的示例: https://msdn.microsoft.com/en-us/library/hh547396(v=crm.5).aspx

當然,第1點將是最困難的部分,因爲您必須構建完全對應於CRM預期的XML或docx文件,但我是假設你是從外部應用程序執行它,所以你可以使用一些很棒的.NET庫,這會讓事情變得更簡單。

當涉及到更新/創建記錄時,即使您採用並行性和批量更新請求,我從來沒有看到任何比標準CRM導入更快的東西。

如果出現錯誤的MSDN網站,我在這裏張貼從以上鍊接的例子展示如何將數據導入到CRM編程:

using System; 
using System.ServiceModel; 
using System.Collections.Generic; 
using System.Linq; 

// These namespaces are found in the Microsoft.Xrm.Sdk.dll assembly 
// located in the SDK\bin folder of the SDK download. 
using Microsoft.Xrm.Sdk; 
using Microsoft.Xrm.Sdk.Query; 
using Microsoft.Xrm.Sdk.Client; 
using Microsoft.Xrm.Sdk.Messages; 
using Microsoft.Xrm.Sdk.Metadata; 

// These namespaces are found in the Microsoft.Crm.Sdk.Proxy.dll assembly 
// located in the SDK\bin folder of the SDK download. 
using Microsoft.Crm.Sdk.Messages; 

namespace Microsoft.Crm.Sdk.Samples 
{  
    /// <summary> 
    /// This sample shows how to define a complex mapping for importing and then use the 
    /// Microsoft Dynamics CRM 2011 API to bulk import records with that mapping. 
    /// </summary> 
    public class ImportWithCreate 
    { 
     #region Class Level Members 

     private OrganizationServiceProxy _serviceProxy; 
     private DateTime _executionDate; 

     #endregion 

     /// <summary> 
     /// This method first connects to the organization service. Afterwards, 
     /// auditing is enabled on the organization, account entity, and a couple 
     /// of attributes. 
     /// </summary> 
     /// <param name="serverConfig">Contains server connection information.</param> 
     /// <param name="promptforDelete">When True, the user will be prompted to delete all 
     /// created entities.</param> 
     public void Run(ServerConnection.Configuration serverConfig, bool promptforDelete) 
     { 
      using (_serviceProxy = ServerConnection.GetOrganizationProxy(serverConfig)) 
      { 
       // This statement is required to enable early bound type support. 
       _serviceProxy.EnableProxyTypes(); 

       // Log the start time to ensure deletion of records created during execution. 
       _executionDate = DateTime.Today; 
       ImportRecords(); 
       DeleteRequiredRecords(promptforDelete); 
      } 
     } 

     /// <summary> 
     /// Imports records to Microsoft Dynamics CRM from the specified .csv file. 
     /// </summary> 
     public void ImportRecords() 
     { 
      // Create an import map. 
      ImportMap importMap = new ImportMap() 
      { 
       Name = "Import Map " + DateTime.Now.Ticks.ToString(), 
       Source = "Import Accounts.csv", 
       Description = "Description of data being imported", 
       EntitiesPerFile = 
        new OptionSetValue((int)ImportMapEntitiesPerFile.SingleEntityPerFile), 
       EntityState = EntityState.Created 
      }; 
      Guid importMapId = _serviceProxy.Create(importMap); 

      // Create column mappings. 

      #region Column One Mappings 
      // Create a column mapping for a 'text' type field. 
      ColumnMapping colMapping1 = new ColumnMapping() 
      { 
       // Set source properties. 
       SourceAttributeName = "src_name", 
       SourceEntityName = "Account_1", 

       // Set target properties. 
       TargetAttributeName = "name", 
       TargetEntityName = Account.EntityLogicalName, 

       // Relate this column mapping with the data map. 
       ImportMapId = 
        new EntityReference(ImportMap.EntityLogicalName, importMapId), 

       // Force this column to be processed. 
       ProcessCode = 
        new OptionSetValue((int)ColumnMappingProcessCode.Process) 
      }; 

      // Create the mapping. 
      Guid colMappingId1 = _serviceProxy.Create(colMapping1); 
      #endregion 

      #region Column Two Mappings 
      // Create a column mapping for a 'lookup' type field. 
      ColumnMapping colMapping2 = new ColumnMapping() 
      { 
       // Set source properties. 
       SourceAttributeName = "src_parent", 
       SourceEntityName = "Account_1", 

       // Set target properties. 
       TargetAttributeName = "parentaccountid", 
       TargetEntityName = Account.EntityLogicalName, 

       // Relate this column mapping with the data map. 
       ImportMapId = 
        new EntityReference(ImportMap.EntityLogicalName, importMapId), 

       // Force this column to be processed. 
       ProcessCode = 
        new OptionSetValue((int)ColumnMappingProcessCode.Process), 
      }; 

      // Create the mapping. 
      Guid colMappingId2 = _serviceProxy.Create(colMapping2); 

      // Because we created a column mapping of type lookup, we need to specify lookup details in a lookupmapping. 
      // One lookupmapping will be for the parent account, and the other for the current record. 
      // This lookupmapping is important because without it the current record 
      // cannot be used as the parent of another record. 

      // Create a lookup mapping to the parent account. 
      LookUpMapping parentLookupMapping = new LookUpMapping() 
      { 
       // Relate this mapping with its parent column mapping. 
       ColumnMappingId = 
        new EntityReference(ColumnMapping.EntityLogicalName, colMappingId2), 

       // Force this column to be processed. 
       ProcessCode = 
        new OptionSetValue((int)LookUpMappingProcessCode.Process), 

       // Set the lookup for an account entity by its name attribute. 
       LookUpEntityName = Account.EntityLogicalName, 
       LookUpAttributeName = "name", 
       LookUpSourceCode = 
        new OptionSetValue((int)LookUpMappingLookUpSourceCode.System) 
      }; 

      // Create the lookup mapping. 
      Guid parentLookupMappingId = _serviceProxy.Create(parentLookupMapping); 

      // Create a lookup on the current record's "src_name" so that this record can 
      // be used as the parent account for another record being imported. 
      // Without this lookup, no record using this account as its parent will be imported. 
      LookUpMapping currentLookUpMapping = new LookUpMapping() 
      { 
       // Relate this lookup with its parent column mapping. 
       ColumnMappingId = 
        new EntityReference(ColumnMapping.EntityLogicalName, colMappingId2), 

       // Force this column to be processed. 
       ProcessCode = 
        new OptionSetValue((int)LookUpMappingProcessCode.Process), 

       // Set the lookup for the current record by its src_name attribute. 
       LookUpAttributeName = "src_name", 
       LookUpEntityName = "Account_1", 
       LookUpSourceCode = 
        new OptionSetValue((int)LookUpMappingLookUpSourceCode.Source) 
      }; 

      // Create the lookup mapping 
      Guid currentLookupMappingId = _serviceProxy.Create(currentLookUpMapping); 
      #endregion 

      #region Column Three Mappings 
      // Create a column mapping for a 'picklist' type field 
      ColumnMapping colMapping3 = new ColumnMapping() 
      { 
       // Set source properties 
       SourceAttributeName = "src_addresstype", 
       SourceEntityName = "Account_1", 

       // Set target properties 
       TargetAttributeName = "address1_addresstypecode", 
       TargetEntityName = Account.EntityLogicalName, 

       // Relate this column mapping with its parent data map 
       ImportMapId = 
        new EntityReference(ImportMap.EntityLogicalName, importMapId), 

       // Force this column to be processed 
       ProcessCode = 
        new OptionSetValue((int)ColumnMappingProcessCode.Process) 
      }; 

      // Create the mapping 
      Guid colMappingId3 = _serviceProxy.Create(colMapping3); 

      // Because we created a column mapping of type picklist, we need to specify picklist details in a picklistMapping 
      PickListMapping pickListMapping1 = new PickListMapping() 
      { 
       SourceValue = "bill", 
       TargetValue = 1, 

       // Relate this column mapping with its column mapping data map 
       ColumnMappingId = 
        new EntityReference(ColumnMapping.EntityLogicalName, colMappingId3), 

       // Force this column to be processed 
       ProcessCode = 
        new OptionSetValue((int)PickListMappingProcessCode.Process) 
      }; 

      // Create the mapping 
      Guid picklistMappingId1 = _serviceProxy.Create(pickListMapping1); 

      // Need a picklist mapping for every address type code expected 
      PickListMapping pickListMapping2 = new PickListMapping() 
      { 
       SourceValue = "ship", 
       TargetValue = 2, 

       // Relate this column mapping with its column mapping data map 
       ColumnMappingId = 
        new EntityReference(ColumnMapping.EntityLogicalName, colMappingId3), 

       // Force this column to be processed 
       ProcessCode = 
        new OptionSetValue((int)PickListMappingProcessCode.Process) 
      }; 

      // Create the mapping 
      Guid picklistMappingId2 = _serviceProxy.Create(pickListMapping2); 
      #endregion 

      // Create Import 
      Import import = new Import() 
      { 
       // IsImport is obsolete; use ModeCode to declare Create or Update. 
       ModeCode = new OptionSetValue((int)ImportModeCode.Create), 
       Name = "Importing data" 
      }; 
      Guid importId = _serviceProxy.Create(import); 

      // Create Import File. 
      ImportFile importFile = new ImportFile() 
      { 
       Content = BulkImportHelper.ReadCsvFile("Import Accounts.csv"), // Read contents from disk. 
       Name = "Account record import", 
       IsFirstRowHeader = true, 
       ImportMapId = new EntityReference(ImportMap.EntityLogicalName, importMapId), 
       UseSystemMap = false, 
       Source = "Import Accounts.csv", 
       SourceEntityName = "Account_1", 
       TargetEntityName = Account.EntityLogicalName, 
       ImportId = new EntityReference(Import.EntityLogicalName, importId), 
       EnableDuplicateDetection = false, 
       FieldDelimiterCode = 
        new OptionSetValue((int)ImportFileFieldDelimiterCode.Comma), 
       DataDelimiterCode = 
        new OptionSetValue((int)ImportFileDataDelimiterCode.DoubleQuote), 
       ProcessCode = 
        new OptionSetValue((int)ImportFileProcessCode.Process) 
      }; 

      // Get the current user to set as record owner. 
      WhoAmIRequest systemUserRequest = new WhoAmIRequest(); 
      WhoAmIResponse systemUserResponse = 
       (WhoAmIResponse)_serviceProxy.Execute(systemUserRequest); 

      // Set the owner ID.     
      importFile.RecordsOwnerId = 
       new EntityReference(SystemUser.EntityLogicalName, systemUserResponse.UserId); 

      Guid importFileId = _serviceProxy.Create(importFile); 

      // Retrieve the header columns used in the import file. 
      GetHeaderColumnsImportFileRequest headerColumnsRequest = new GetHeaderColumnsImportFileRequest() 
      { 
       ImportFileId = importFileId 
      }; 
      GetHeaderColumnsImportFileResponse headerColumnsResponse = 
       (GetHeaderColumnsImportFileResponse)_serviceProxy.Execute(headerColumnsRequest); 

      // Output the header columns. 
      int columnNum = 1; 
      foreach (string headerName in headerColumnsResponse.Columns) 
      { 
       Console.WriteLine("Column[" + columnNum.ToString() + "] = " + headerName); 
       columnNum++; 
      } 

      // Parse the import file. 
      ParseImportRequest parseImportRequest = new ParseImportRequest() 
      { 
       ImportId = importId 
      }; 
      ParseImportResponse parseImportResponse = 
       (ParseImportResponse)_serviceProxy.Execute(parseImportRequest); 
      Console.WriteLine("Waiting for Parse async job to complete"); 
      BulkImportHelper.WaitForAsyncJobCompletion(_serviceProxy, parseImportResponse.AsyncOperationId); 
      BulkImportHelper.ReportErrors(_serviceProxy, importFileId); 

      // Retrieve the first two distinct values for column 1 from the parse table. 
      // NOTE: You must create the parse table first using the ParseImport message. 
      // The parse table is not accessible after ImportRecordsImportResponse is called. 
      GetDistinctValuesImportFileRequest distinctValuesRequest = new GetDistinctValuesImportFileRequest() 
      { 
       columnNumber = 1, 
       ImportFileId = importFileId, 
       pageNumber = 1, 
       recordsPerPage = 2, 
      }; 
      GetDistinctValuesImportFileResponse distinctValuesResponse = 
       (GetDistinctValuesImportFileResponse)_serviceProxy.Execute(distinctValuesRequest); 

      // Output the distinct values. In this case: (column 1, row 1) and (column 1, row 2). 
      int cellNum = 1; 
      foreach (string cellValue in distinctValuesResponse.Values) 
      { 
       Console.WriteLine("(1, " + cellNum.ToString() + "): " + cellValue); 
       Console.WriteLine(cellValue); 
       cellNum++; 
      } 

      // Retrieve data from the parse table. 
      // NOTE: You must create the parse table first using the ParseImport message. 
      // The parse table is not accessible after ImportRecordsImportResponse is called. 
      RetrieveParsedDataImportFileRequest parsedDataRequest = new RetrieveParsedDataImportFileRequest() 
      { 
       ImportFileId = importFileId, 
       PagingInfo = new PagingInfo() 
       { 
        // Specify the number of entity instances returned per page. 
        Count = 2, 
        // Specify the number of pages returned from the query. 
        PageNumber = 1, 
        // Specify a total number of entity instances returned. 
        PagingCookie = "1" 
       } 
      }; 

      RetrieveParsedDataImportFileResponse parsedDataResponse = 
       (RetrieveParsedDataImportFileResponse)_serviceProxy.Execute(parsedDataRequest); 

      // Output the first two rows retrieved. 
      int rowCount = 1; 
      foreach (string[] rows in parsedDataResponse.Values) 
      { 
       int colCount = 1; 
       foreach (string column in rows) 
       { 
        Console.WriteLine("(" + rowCount.ToString() + "," + colCount.ToString() + ") = " + column); 
        colCount++; 
       } 
       rowCount++; 
      } 

      // Transform the import 
      TransformImportRequest transformImportRequest = new TransformImportRequest() 
      { 
       ImportId = importId 
      }; 
      TransformImportResponse transformImportResponse = 
       (TransformImportResponse)_serviceProxy.Execute(transformImportRequest); 
      Console.WriteLine("Waiting for Transform async job to complete"); 
      BulkImportHelper.WaitForAsyncJobCompletion(_serviceProxy, transformImportResponse.AsyncOperationId); 
      BulkImportHelper.ReportErrors(_serviceProxy, importFileId); 

      // Upload the records. 
      ImportRecordsImportRequest importRequest = new ImportRecordsImportRequest() 
      { 
       ImportId = importId 
      }; 
      ImportRecordsImportResponse importResponse = 
       (ImportRecordsImportResponse)_serviceProxy.Execute(importRequest); 
      Console.WriteLine("Waiting for ImportRecords async job to complete"); 
      BulkImportHelper.WaitForAsyncJobCompletion(_serviceProxy, importResponse.AsyncOperationId); 
      BulkImportHelper.ReportErrors(_serviceProxy, importFileId); 
     } 

     /// <summary> 
     /// Deletes any entity records that were created for this sample. 
     /// <param name="prompt">Indicates whether to prompt the user 
     /// to delete the records created in this sample.</param> 
     /// </summary> 
     public void DeleteRequiredRecords(bool prompt) 
     { 
      bool toBeDeleted = true; 

      if (prompt) 
      { 
       // Ask the user if the created entities should be deleted. 
       Console.Write("\nDo you want these entity records deleted? (y/n) [y]: "); 
       String answer = Console.ReadLine(); 
       if (answer.StartsWith("y") || 
        answer.StartsWith("Y") || 
        answer == String.Empty) 
       { 
        toBeDeleted = true; 
       } 
       else 
       { 
        toBeDeleted = false; 
       } 
      } 

      if (toBeDeleted) 
      { 
       // Retrieve all account records created in this sample. 
       QueryExpression query = new QueryExpression() 
       { 
        EntityName = Account.EntityLogicalName, 
        Criteria = new FilterExpression() 
        { 
         Conditions = 
         { 
          new ConditionExpression("createdon", ConditionOperator.OnOrAfter, _executionDate), 
         } 
        }, 
        ColumnSet = new ColumnSet(false) 
       }; 
       var accountsCreated = _serviceProxy.RetrieveMultiple(query).Entities; 

       // Delete all records created in this sample. 
       foreach (var account in accountsCreated) 
       { 
        _serviceProxy.Delete(Account.EntityLogicalName, account.Id); 
       } 

       Console.WriteLine("Entity record(s) have been deleted."); 
      } 
     } 
     #region Main method 

     /// <summary> 
     /// Standard Main() method used by most SDK samples. 
     /// </summary> 
     /// <param name="args"></param> 
     static public void Main(string[] args) 
     { 
      try 
      { 
       // Obtain the target organization's web address and client logon 
       // credentials from the user. 
       ServerConnection serverConnect = new ServerConnection(); 
       ServerConnection.Configuration config = serverConnect.GetServerConfiguration(); 

       var app = new ImportWithCreate(); 
       app.Run(config, true); 
      } 

      catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> ex) 
      { 
       Console.WriteLine("The application terminated with an error."); 
       Console.WriteLine("Timestamp: {0}", ex.Detail.Timestamp); 
       Console.WriteLine("Code: {0}", ex.Detail.ErrorCode); 
       Console.WriteLine("Message: {0}", ex.Detail.Message); 
       Console.WriteLine("Trace: {0}", ex.Detail.TraceText); 
       Console.WriteLine("Inner Fault: {0}", 
        null == ex.Detail.InnerFault ? "No Inner Fault" : "Has Inner Fault"); 
      } 
      catch (System.TimeoutException ex) 
      { 
       Console.WriteLine("The application terminated with an error."); 
       Console.WriteLine("Message: {0}", ex.Message); 
       Console.WriteLine("Stack Trace: {0}", ex.StackTrace); 
       Console.WriteLine("Inner Fault: {0}", 
        null == ex.InnerException.Message ? "No Inner Fault" : ex.InnerException.Message); 
      } 
      catch (System.Exception ex) 
      { 
       Console.WriteLine("The application terminated with an error."); 
       Console.WriteLine(ex.Message); 

       // Display the details of the inner exception. 
       if (ex.InnerException != null) 
       { 
        Console.WriteLine(ex.InnerException.Message); 

        FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> fe = ex.InnerException 
         as FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>; 
        if (fe != null) 
        { 
         Console.WriteLine("Timestamp: {0}", fe.Detail.Timestamp); 
         Console.WriteLine("Code: {0}", fe.Detail.ErrorCode); 
         Console.WriteLine("Message: {0}", fe.Detail.Message); 
         Console.WriteLine("Trace: {0}", fe.Detail.TraceText); 
         Console.WriteLine("Inner Fault: {0}", 
          null == fe.Detail.InnerFault ? "No Inner Fault" : "Has Inner Fault"); 
        } 
       } 
      } 
      // Additional exceptions to catch: SecurityTokenValidationException, ExpiredSecurityTokenException, 
      // SecurityAccessDeniedException, MessageSecurityException, and SecurityNegotiationException. 

      finally 
      { 
       Console.WriteLine("Press <Enter> to exit."); 
       Console.ReadLine(); 
      } 
     } 
     #endregion Main method 
    } 
}