17

我注意到這裏有一些很奇怪的東西。將權限/身份驗證複製到子線程...?

我正在編寫一個CRM 2011 Silverlight擴展,當然,在我的本地開發實例中,這一切都很好。應用程序使用OData進行通信,並且使用System.Threading.Tasks.Task大量地執行後臺中的所有操作(FromAsync是祝福)。但是,我決定在CRM 2011 Online中測試我的應用程序,發現令我驚訝的是,它不再有效;結束檢索任務時,我會收到安全異常。

使用招,我發現,CRM試圖給我重定向到Live登錄頁面,這並沒有太大的意義,考慮到我已經登錄。

一些更多的嘗試之後,我發現,錯誤是因爲我從不同於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,視情況而定)是可能的,只要在UI線程上執行query.BeginExecute(null, null);。您需要一種方法將返回的IAsyncResult恢復到調用線程,但您可以使用ManualResetEvent來完成此操作。

但我還是想知道爲什麼該死的權限/驗證不是線程之間共享...

+1

它可能涉及到[當前線程的執行上下文(http://msdn.microsoft.com/en-us/library什麼/system.threading.thread.executioncontext)。 – shambulator

+0

很有可能,但是我想指出的是,在本地,本地CRM服務器上測試我的代碼時,一切正常。所以,現在還不清楚究竟發生了什麼。 – Shaamaan

回答

2

我不太清楚,這是有幫助的。但是我發現Jeffrey Richter的描述頁面770

「與控制檯應用程序一樣,ASP.NET Web Form和XML Web Service應用程序允許任何線程執行任何想要的操作。當線程池線程開始處理客戶端 請求,它可以假設客戶端的文化(System.Globalization.CultureInfo),允許 Web服務器返回文化特定格式的數字,日期和時間.5在 此外,Web服務器可以假設客戶端的身份( System.Security.Principal。 IPrincipal),以便服務器只能訪問允許客戶端訪問的資源。當一個線程池線程產生一個異步操作時,它將被完成 由另一個線程池線程執行,它將處理異步操作的結果。 儘管代表原始客戶端請求執行此項工作,但文化 和身份信息默認不會流到新的線程池線程,因此任何 代表客戶端完成的額外工作現在都不使用客戶端的文化和身份 信息。理想情況下,我們希望文化和身份信息流向其他線程,但仍然在代表同一客戶端工作的線程池。「

這裏是他的例子,我希望這會有所幫助。

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