17

Ecco qualcosa di strano che noterei.Copia permessi/autenticazione su thread figlio ...?

Sto scrivendo un'estensione Silverlight CRM 2011 e, beh, tutto va bene sulla mia istanza di sviluppo locale. L'applicazione utilizza OData per comunicare e utilizza molto System.Threading.Tasks.Task per eseguire tutte le operazioni in background (FromAsync è una benedizione).

Tuttavia, ho deciso di testare la mia domanda in CRM 2011 online e ho scoperto, con mia sorpresa, che non avrebbe funzionato più; Riceverò un'eccezione di sicurezza quando terminerò le attività di recupero.

Utilizzando Fiddler, ho scoperto che il CRM sta cercando di me reindirizzamento alla pagina di login dal vivo, che non ha molto senso, considerando che ero già collegato.

Dopo alcuni più tentativi, ho scoperto che il errori erano perché stavo accedendo al servizio da un thread diverso rispetto al thread dell'interfaccia utente.

Ecco un rapido esempio:

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

Sembra quasi ovvio che mi manca alcuni fondamenti su come le discussioni utilizzano i permessi. Dal momento che l'utilizzo di un thread separato è preferibile nel mio caso, esiste un modo per "copiare" le autorizzazioni/l'autenticazione? Forse una sorta di imitazione?

MODIFICA: nel caso in cui qualcun altro stia cercando di risolvere questo problema, è possibile utilizzare altri thread (o Task, a seconda del caso) purché query.BeginExecute(null, null); sia eseguito sul thread dell'interfaccia utente. È necessario un modo per recuperare il IAsyncResult restituito al thread chiamante, ma è possibile farlo utilizzando uno ManualResetEvent.

Ma mi piace ancora di sapere perché il permesso dannatamente/autenticazione non è condivisa tra i fili ...

+1

E 'probabilmente legato alla [contesto di esecuzione del thread corrente] la (http://msdn.microsoft.com/en-us/library /system.threading.thread.executioncontext). – shambulator

+0

Piuttosto possibile, tuttavia vorrei sottolineare che durante il test del mio codice su server CRM locali, tutto funzionava perfettamente. Quindi, è ancora piuttosto poco chiaro su cosa stia succedendo esattamente. – Shaamaan

risposta

2

io non sono del tutto sicuro, è che questo vi aiuterà. Ma ho trovato una descrizione di pagina per pagina Jeffrey Richter 770

"Come applicazioni console, applicazioni Web Form e servizi Web XML ASP.NET consentono qualsiasi thread per fare ciò che vuole. Quando un thread pool di thread inizia a elaborare un client di , può assumere la cultura del client (System.Globalization.CultureInfo), consentendo a il server Web di restituire la formattazione specifica della cultura per numeri, date e tempi.5 Nell'aggiunta , il server Web può assumere l'identità del client (System.Security.Principal. IPrincipal) in modo che il server possa accedere solo alle risorse a cui è consentito il client per l'accesso . Quando un thread del thread pool genera un'operazione asincrona, verrà completato da un altro thread pool di thread, che elaborerà il risultato di un'operazione asincrona. Mentre questo lavoro viene eseguito per conto della richiesta originale del client, la cultura e le informazioni sull'identità non passano al nuovo thread del pool di thread per impostazione predefinita, quindi qualsiasi lavoro aggiuntivo eseguito per conto del client non sta ora utilizzando il client cultura e identità informazioni. Idealmente, vogliamo che le informazioni su cultura e identità vengano trasferite agli altri thread di pool che continuano a funzionare per conto dello stesso client. "

Ed ecco il suo esempio, spero che questo possa essere d'aiuto.

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

e qui è quello che sto usando nella mia richiesta

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