17

这是我注意到的非常奇怪的事情。

我正在编写一个 CRM 2011 Silverlight 扩展,而且,在我的本地开发实例上一切都很好。应用程序使用 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;
        });
    }

似乎很明显,我缺少一些关于线程如何使用权限的基础知识。由于在我的情况下最好使用单独的线程,有没有办法“复制”权限/身份验证?也许是某种模仿?

Task编辑:如果其他人为此苦苦挣扎,只要query.BeginExecute(null, null);在 UI 线程上执行,就可以使用其他线程(或视情况而定)。您需要一种方法来检索返回IAsyncResult给调用线程,但您可以使用ManualResetEvent.

但我仍然想知道为什么线程之间不共享该死的权限/身份验证......

4

1 回答 1

2

我不太确定,这是否会有所帮助。但我找到了 Jeffrey Richter 第 770 页的描述

默认情况下,文化和身份信息不会流向新的线程池线程,因此代表客户端完成的任何其他工作现在都不会使用客户端的文化和身份信息。理想情况下,我们希望文化和身份信息流向仍在代表同一客户工作的其他线程池线程。”

这是他的例子,我希望这会有所帮助。

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 回答