2016-02-17 14 views
5

Utilizzo API Web ASP.Net 2/.Net 4.5.2.Elemento di ritenzione principale all'interno di lavoro in coda in coda

Sto tentando di mantenere il principale di chiamata quando si accoda un elemento di lavoro in background. A tal fine, sto cercando di:

Thread.CurrentPrincipal = callingPrincipal; 

Ma quando lo faccio, ottengo un ObjectDisposedException:

System.ObjectDisposedException: maniglia di sicurezza è stato chiuso

Come mantengo il principale corrente all'interno dell'elemento di lavoro in background?
Posso fare una copia del principale in qualche modo?

public void Run<T>(Action<T> action) 
{ 
    _logger.Debug("Queueing background work item"); 
    var callingPrincipal = Thread.CurrentPrincipal; 
    HostingEnvironment.QueueBackgroundWorkItem(token => 
    { 
     try 
     { 
      // UNCOMMENT - THROWS EXCEPTION 
      // Thread.CurrentPrincipal = callingPrincipal; 
      _logger.Debug("Executing queued background work item"); 
      using (var scope = DependencyResolver.BeginLifetimeScope()) 
      { 
       var service = scope.Resolve<T>(); 
       action(service); 
      } 
     } 
     catch (Exception ex) 
     { 
      _logger.Fatal(ex); 
     } 
     finally 
     { 
      _logger.Debug("Completed queued background work item"); 
     } 
    }); 
} 
+0

Avete effettivamente bisogno di 'Thread.CurrentPrincipal', o avete davvero bisogno di' HttpContext.User'? Forse approfondisci il motivo per cui hai bisogno di fluire l'attuale principale in questo modo. –

+0

Qual è la ragione per cui si mantiene il Principal corrente in Thread in background? – Win

+0

Per ottenere alcuni dati personali che richiede molto tempo, ad esempio. – VMAtm

risposta

5

Risulta ClaimsPrincipal ora ha un costruttore di copia.

var principal = new ClaimsPrincipal(Thread.CurrentPrincipal); 

Questo sembra risolvere il problema conservando tutte le informazioni relative a identità e attestazioni. Segue la funzione completa:

public void Run<T>(Action<T> action) 
{ 
    _logger.Debug("Queueing background work item"); 
    var principal = new ClaimsPrincipal(Thread.CurrentPrincipal); 

    HostingEnvironment.QueueBackgroundWorkItem(token => 
    { 
     try 
     { 
      Thread.CurrentPrincipal = principal; 
      _logger.Debug("Executing queued background work item"); 
      using (var scope = DependencyResolver.BeginLifetimeScope()) 
      { 
       var service = scope.Resolve<T>(); 
       action(service); 
      } 
     } 
     catch (Exception ex) 
     { 
      _logger.Fatal(ex); 
     } 
     finally 
     { 
      _logger.Debug("Completed queued background work item"); 
     } 
    }); 
} 
0

Il problema della vostra situazione è che l'attività in background in esecuzione dopo il Thread.CurrentPrincipal in via di dismissione. Ciò accade a causa del modello ASP.NET: la richiesta viene gestita nel contesto dell'utente e successivamente vengono liberati tutti i valori corrispondenti all'utente. Quindi questo è esattamente ciò che accade alla tua identità. Prova a salvare le informazioni sull'utente e sulla sua identità per impersonarlo in seguito.

è possibile rivedere un support article from Microsoft per impersonare le operazioni nei siti ASP.NET, ma non credo che questo sarà utile per voi:

System.Security.Principal.WindowsImpersonationContext impersonationContext; 
impersonationContext = 
    ((System.Security.Principal.WindowsIdentity)callingPrincipal.Identity).Impersonate(); 

//Insert your code that runs under the security context of the authenticating user here. 

impersonationContext.Undo(); 

o, può essere, è possibile utilizzare l'utente .Token, qualcosa di simile:

HostingEnvironment.QueueBackgroundWorkItem(token => 
{ 
    try 
    { 
     _logger.Debug("Executing queued background work item"); 
     using (HostingEnvironment.Impersonate(callingPrincipal.Identity)) 
     { 
      using (var scope = DependencyResolver.BeginLifetimeScope()) 
      { 
       var service = scope.Resolve<T>(); 
       action(service); 
      } 
     } 
     // UNCOMMENT - THROWS EXCEPTION 
     // Thread.CurrentPrincipal = callingPrincipal; 
    } 
    catch (Exception ex) 
    { 
     _logger.Fatal(ex); 
    } 
    finally 
    { 
     _logger.Debug("Completed queued background work item"); 
    } 
}); 

vi suggerisco di rivedere il vostro disegno di architettura in modo da poter trovare un modo per spostare l'operazione di sfondo per altro contesto, in cui l'identità dell'utente sarebbe rimanere più a lungo. Altro modo, ad esempio, consiste nell'utilizzare il passare la corrente OperationContext al Task:

// store local operation context 
var operationContext = OperationContext.Current; 
TaskFactory.StartNew(() => 
{ 
    // initialize the current operation context 
    OperationContext.Current = operationContext; 
    action(); 
}) 
+0

OP è abbastanza diverso dalla rappresentazione.Il problema con OP è il thread principale è disposto mentre il Thread in Background è in fase di elaborazione. ** Non è una buona pratica in ASP.Net ** ed è per questo che Yuval Itzchakov e io chiediamo. Normalmente, archiviamo i dati nella memoria persistente come il database prima dell'elaborazione nel thread in background. – Win

+0

Capisco il problema e ho cercato di spiegare come non eseguire il binding a 'CurrentThread' per l'operazione lunga. – VMAtm