2016-09-26 4 views
0

私のAsyncサービスは非常に異常な動作をしています。 物語: プラグインがあります。これはLead Createで発生します。プラグイン自体の目的は、リードのカスタム列挙を作成することです。プラグインは、番号を保持するAutonumberingエンティティのフィールドから最後のnumerを取得します。次に、プラグインはAutonumberingエンティティの番号フィールドを1だけインクリメントし、取得した番号をLeadに割り当てます。非同期サービスエンティティを複数回処理します

問題は次のとおりです。 リードの大量生産(ナンバリングのクラッシュテスト)を実行すると、次のようになります。 400、Autonumberingカウンタは0から始まり、すべてのリードが処理されるとAutoneumberingカウンタは〜770の値で終了し、推定値は400を超えます。

非同期サービスは同じリードを複数回処理します。いくつかの場合は1回だけ、他の場合は2〜5回です。

これはどうしてですか?彼らは複数のスレッドのためのインスタンスを使用することができるよう まず私のコンテキスト・ファクトリー・サービス変数は、クラス内で宣言された:1

public void Execute(IServiceProvider serviceProvider) 
{ 
    Entity target = ((Entity)context.InputParameters["Target"]); 
    target["new_id"] = GetCurrentNumber(service, LEAD_AUTONUMBER); 
    service.Update(target); 
    return; 
} 

public int GetCurrentNumber(IOrganizationService service, Guid EntityType) 
{ 
    lock (_locker) 
    { 
     Entity record = service.Retrieve("new_autonumbering", EntityType, new ColumnSet("new_nextnumber")); 
     record["new_nextnumber"] = int.Parse(record["new_nextnumber"].ToString()) + 1; 
     service.Update(record); 

     return int.Parse(record["new_nextnumber"].ToString()); 
    } 
} 

UPDATE:

は、ここに私のコードです。

public class IdAssignerPlugin : IPlugin 
{ 
    private static  IPluginExecutionContext context; 
    private static  IOrganizationServiceFactory factory; 
    private static  IOrganizationService service; 

    public void Execute(IServiceProvider serviceProvider) 
    { 
     context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext)); 
     factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory)); 
     service = factory.CreateOrganizationService(null); 
     [...] 
    } 
} 

@HenkvanBoeijenのコメント後、私は、これは安全な方法ではないことに気づいたので、私はExecute()メソッドにすべての宣言を移動しました。

public class IdAssignerPlugin : IPlugin 
{ 
    public void Execute(IServiceProvider serviceProvider) 
    { 
     IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));; 
     IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));; 
     IOrganizationService service = factory.CreateOrganizationService(null);; 

     [...] 
    } 
} 

しかし、処理は、今非常に急速に行くが、これは、複数の処理から私を救っていません。

UPDATE 2:私も

など、状況Retry Count = 0と11回の操作後、残りの操作はRetry Count = 1を持っており、16の後にそれがRetry Count = 2であることに気づいたシステムジョブでは(このテストでは、私は20点のリードを作成しましたprogrammaticaly、およびassigment後にカウンタは私last number = 33を示しており、私はすべてのretry count値をまとめる場合には、自動番号でlast numberに似ていた、33で出てくる)

screenshot

+1

'context'と' service'から来るのか?あなたのコードはスレッドセーフではないようです。 –

+0

@HenkvanBoeijen、私は質問の更新1に答えました。 –

+0

再試行は、おそらくロックを取得してスキップするためのワークフローであるため、ロックを取得するためにワークフローが待機するように、Thread.Sleepで再試行する必要があります。 – dynamicallyCRM

答えて

0

この問題が見つかりました。 CRMがプラグインエラーを表示していた後のすべての作業で11回試行した後(追加情報なしのGeneric SQL Errorであり、これは過負荷によって引き起こされる可能性があります)、SQL Timeout Errorのようなものです。CRMの

イベント実行パイプラインは以下の通りです:

  1. イベントが起こりました。術後 (非同期 - - 私の場合は術後)同期非同期&操作前、次のパラメータに基づいて
  2. イベントリスナーcathesイベントハンドラに送信し
  3. その後イベントは、非同期キューのエージェントに入りますプラグインをいつ実行するかを決定します。
  4. このイベントプラグインに関連する非同期キューエージェントが実行されます。
  5. プラグインは作業を行い、成功した場合は0、失敗した場合は1を返します。
  6. 0の場合、Async Queue Agentは現在のパイプラインをSucceededのステータスで閉じ、CRMコアに通知を送信します。

エラーは、おそらくコード(ステップ5)エンティティ内部Autonumberingの更新後に生じたが、完了前の状態のタスクは、(ステップ6)に成功しました。

このエラーのため、CRMは同じInputParametersでタスクを再度実行します。

私は全体のexecute()メソッドに私のロック()文を推定し、メソッドの最後に更新エンティティ要求を移動:

マイCRMサーバは、非常に私は次の回避策に出てくるので、オーバーロードされていません。

すべてがうまくいっています。欠点は、この方法でプラグインを(ほぼ)古くは良い同期に戻すことですが、私のサーバーはこの問題を抱えないように過負荷になっていないと言いました。

私は歴史的な理由のために私のコード投稿:

public class IdAssignerPlugin : IPlugin 
{ 
    public const string AUTONUMBERING_ENTITY = "new_autonumber"; 
    public static Guid LEAD_AUTONUMBER = 
new Guid("yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"); 

    static readonly object _locker = new object(); 

    public void Execute(IServiceProvider serviceProvider) 
    { 
     var context = (IPluginExecutionContext)serviceProvider.GetService(
typeof(IPluginExecutionContext)); 
     var factory = (IOrganizationServiceFactory)serviceProvider.GetService(
typeof(IOrganizationServiceFactory)); 
     var service = factory.CreateOrganizationService(null); 

     if (PreventRecursiveCall(context)) 
     return; 

     lock (_locker) 
     { 
      if (context.InputParameters.Contains("Target") && 
context.InputParameters["Target"] is Entity) 
      { 
       Entity autoNumber; 
       Entity target = ((Entity)context.InputParameters["Target"]); 
       if (target.LogicalName.Equals("lead", 
StringComparison.InvariantCultureIgnoreCase)) 
       { 
        autoNumber = GetCurrentNumber(service, LEAD_AUTONUMBER); 
        target["new_id"] = autoNumber["new_nextnumber"]; 
       } 
       service.Update(autoNumber); 
       service.Update(target); 
      } 
     } 
     return; 
    } 

    public int GetCurrentNumber(IOrganizationService service, Guid EntityType) 
    { 
     Entity record = 
service.Retrieve(AUTONUMBERING_ENTITY, EntityType, new ColumnSet("new_nextnumber")); 
     record["new_nextnumber"] = int.Parse(record["new_nextnumber"].ToString()) + 1; 
     return record; 
    } 

    protected virtual bool PreventRecursiveCall(IPluginExecutionContext context) 
    { 
     if (context.SharedVariables.Contains("Fired")) return true; 
     context.SharedVariables.Add("Fired", 1); 
     return false; 
    } 
} 
+0

また、これは再試行を制御するのに役立ちます。 http://develop1.net/public/post/Control-Async-Workflow-Retries.aspx –

0

IServiceProviderからコンテキストとサービスを取得していない場合を除いて、何度も処理されている理由は分かりません。

これを防ぐ簡単な方法は、プラグインが最初に起動されたときにSharedPluginVariableを確認することです。存在する場合は終了し、存在しない場合は共有プラグイン変数を追加します。すべてのプラグインで、プラグインが自動的にトリガーする無限ループを防ぐために、これをデフォルトで行います。

/// <summary> 
/// Allows Plugin to trigger itself. Delete Messge Types always return False 
/// since you can't delete something twice, all other message types return true 
/// if the execution key is found in the shared parameters. 
/// </summary> 
/// <param name="context"></param> 
/// <returns></returns> 
protected virtual bool PreventRecursiveCall(IExtendedPluginContext context) 
{ 
    if (context.Event.Message == MessageType.Delete) 
    { 
     return false; 
    } 

    var sharedVariables = context.SharedVariables; 
    var key = $"{context.PluginTypeName}|{context.Event.MessageName}|{context.Event.Stage}|{context.PrimaryEntityId}"; 
    if (context.GetFirstSharedVariable<int>(key) > 0) 
    { 
     return true; 
    } 

    sharedVariables.Add(key, 1); 
    return false; 
} 
+0

残念ながら、これは私の問題を解決しません。また、上の質問の 'Update 2'を見てください。 –

関連する問題