17

これが私が気付いた非常に奇妙なことです。

私はCRM2011Silverlight拡張機能を作成していますが、ローカル開発インスタンスではすべて問題ありません。アプリケーションはODataを使用して通信System.Threading.Tasks.Taskし、バックグラウンドですべての操作を実行するために多くを使用します(FromAsyncこれは祝福です)。

しかし、CRM 2011 Onlineでアプリケーションをテストすることにしましたが、驚いたことに、アプリケーションが機能しなくなったことがわかりました。取得タスクを終了すると、セキュリティ例外が発生します。

Fiddlerを使用して、CRMがライブログインページにリダイレクトしようとしていることがわかりました。これは、私がすでにログインしていることを考えると、あまり意味がありませんでした。

さらに何度か試したところ、UIスレッドとは別のスレッドからサービスにアクセスしていたためにエラーが発生したことがわかりました。

簡単な例を次に示します。

    //this will work
    private void button1_Click(object sender, RoutedEventArgs e)
    {
        var query = ctx.AccountSet;
        query.BeginExecute((result) =>
        {
            textBox1.Text = query.EndExecute(result).First().Name;
        }, null);
    }

    //this will fail
    private void button2_Click(object sender, RoutedEventArgs e)
    {
        System.Threading.Tasks.Task.Factory.StartNew(RestAsync);
    }

    void RestAsync()
    {
        var query = ctx.AccountSet;
        var async = query.BeginExecute(null, null);
        var task = System.Threading.Tasks.Task.Factory.FromAsync<Account>(async, (result) =>
        {
            return query.EndExecute(result).First(); // <- Exception thrown here
        });
        textBox1.Dispatcher.BeginInvoke(() =>
        {
            textBox1.Text = task.Result.Name;
        });
    }

スレッドがパーミッションをどのように使用するかについてのいくつかの基本が欠けていることはほぼ明らかなようです。私の場合は別のスレッドを使用することが望ましいので、権限/認証を「コピー」する方法はありますか?おそらく、ある種のなりすまし?

編集:他の誰かがこれに苦労している場合は、UIスレッドで実行されているTask限り、他のスレッド(または場合によっては)を使用することが可能です。返されたスレッドにquery.BeginExecute(null, null);戻る方法が必要ですが、を使用してそれを行うことができます。IAsyncResultManualResetEvent

しかし、なぜ私はとんでもない許可/認証がスレッド間で共有されないのか知りたいです...

4

1 に答える 1

2

よくわかりませんが、これが役に立ちますか。しかし、私はジェフリー・リッチターによる770ページからの説明を見つけました

カルチャとID情報はデフォルトでは新しいスレッドプールスレッドに流れないため、クライアントに代わって行われる追加の作業では、クライアントのカルチャとID情報は使用されません。理想的には、カルチャとID情報が、同じクライアントに代わって作業を行っている他のスレッドプールスレッドに流れるようにします。」

そして、これが彼の例です。これがお役に立てば幸いです。

private static AsyncCallback SyncContextCallback(AsyncCallback callback) 
{
  SynchronizationContext sc = SynchronizationContext.Current;
  // If there is no SC, just return what was passed in
  if (sc == null) return callback;
  // Return a delegate that, when invoked, posts to the captured SC a method that
  // calls the original AsyncCallback passing it the IAsyncResult argument
  return asyncResult => sc.Post(result => callback((IAsyncResult)result), asyncResult);
}

protected override void OnMouseClick(MouseEventArgs e) {
  // The GUI thread initiates the asynchronous Web request
  Text = "Web request initiated";
  var webRequest = WebRequest.Create("http://Wintellect.com/");
  webRequest.BeginGetResponse(SyncContextCallback(ProcessWebResponse), webRequest);
  base.OnMouseClick(e);
}

private void ProcessWebResponse(IAsyncResult result) {
  // If we get here, this must be the GUI thread, it's OK to update the UI
  var webRequest = (WebRequest)result.AsyncState;
  using (var webResponse = webRequest.EndGetResponse(result)) {
      Text = "Content length: " + webResponse.ContentLength;
  }
}

そして、これが私のアプリケーションで使用しているものです

 public override void UpdateCanvas(object parameter)
 {
      Action<GraphPane> startToUpdate = StartToUpdate;
       GraphPane selectedPane = Canvas.HostingPane.PaneList.Find(p =>  p.Title.Text.Equals(defaultPanTitle));
       startToUpdate.BeginInvoke(selectedPane, FormSyncContext.SyncContextCallback(RefreshCanvas), selectedPane);
 }

 public static AsyncCallback SyncContextCallback(AsyncCallback callback)
 {
       // Capture the calling thread's SynchronizationContext-derived object
       SynchronizationContext sc = SynchronizationContext.Current;

       // If there is no SC, just return what was passed in
       if (sc == null) return callback;

       // Return a delegate that, when invoked, posts to the captured SC a method that
       // calls the original AsyncCallback passing it the IAsyncResult argument
       return asyncResult => sc.Post(result => callback((IAsyncResult)result), asyncResult);
 }
于 2013-09-19T18:59:33.143 に答える