17

Aquí hay algo muy extraño que notaria.Copiar permisos/autenticación a hilos hijo ...?

Estoy escribiendo un CRM 2011 Silverlight extensiones y, bueno, todo está bien en mi instancia de desarrollo local. La aplicación usa OData para comunicarse, y usa System.Threading.Tasks.Task mucho para realizar todas las operaciones en segundo plano (FromAsync es una bendición).

Sin embargo, decidí probar mi aplicación en CRM 2011 Online y descubrí, para mi sorpresa, que ya no funcionaría; Recibiría una excepción de seguridad cuando termine las tareas de recuperación.

Usando violinista, encontré que el CRM está tratando de redirigir a la página de inicio de sesión en vivo, que no tiene mucho sentido, teniendo en cuenta que ya se había iniciado la sesión.

Después de algunos intentos más, he encontrado que la los errores se debieron a que estaba accediendo al servicio desde un hilo diferente al hilo de UI.

Aquí está un ejemplo rápido:

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

Parece casi obvio que me falta algunos fundamentos sobre cómo utilizan hilos permisos. Como en mi caso es preferible usar un hilo separado, ¿hay alguna forma de "copiar" los permisos/autenticación? Tal vez algún tipo de suplantación?

EDITAR: En caso de que alguien más tenga problemas con esto, es posible utilizar otros hilos (o Task, según sea el caso) siempre que se ejecute query.BeginExecute(null, null); en el hilo de la interfaz de usuario. Necesita una forma de recuperar el IAsyncResult devuelto al hilo de llamada, pero puede hacerlo utilizando un ManualResetEvent.

Pero todavía me gustaría saber por qué el permisos zurcidos/autenticación no es compartida entre los hilos ...

+1

Es probablemente relacionado con [contexto de ejecución del hilo actual] del (http://msdn.microsoft.com/en-us/library /system.threading.thread.executioncontext). – shambulator

+0

Muy posible, sin embargo, me gustaría señalar que cuando probaba mi código en servidores CRM locales, todo funcionaba bien. Por lo tanto, todavía no está claro qué está sucediendo exactamente. – Shaamaan

Respuesta

2

No estoy muy seguro, es que esto ayudará. Pero encontré una descripción de Jeffrey Richter página 770

"Al igual que las aplicaciones de consola, ASP.NET Web Form y las aplicaciones XML Web Service permiten cualquier subproceso para hacer lo que quiera. Cuando un subproceso de grupo de subprocesos comienza a procesar un cliente petición, puede asumir la cultura del cliente (System.Globalization.CultureInfo), permitiendo que el servidor web devuelva el formato específico de cultivo para números, fechas y horas.5 Además, en el , el servidor web puede asumir la identidad del cliente (System.Security.Principal. IPrincipal) para que el servidor pueda acceder solo a los recursos a los que el cliente tiene acceso . Cuando un hilo del grupo de subprocesos genera una operación asincrónica, se completará por otro subproceso de grupo de subprocesos, que procesará el resultado de una operación asincrónica. Mientras este trabajo se realiza en nombre de la solicitud original del cliente, la cultura y la información de identidad no fluye al nuevo subproceso de grupo de subprocesos de forma predeterminada, por lo que cualquier trabajo adicional hecho en nombre del cliente no utiliza ahora el cliente cultura e identidad información. Idealmente, queremos que la cultura y la información de identidad fluyan al otro subproceso subprocesos de grupo que todavía están trabajando en nombre del mismo cliente. "

Y aquí está su ejemplo, espero que esto ayude.

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

Y aquí es lo que estoy utilizando en mi solicitud

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