Passing State o Context Across Layers in Web API

0

Mi sto imbattendo in uno scenario interessante su cui non sono stato in grado di ottenere risposte. Mi piacerebbe vedere se ci sono soluzioni eleganti per questo. Ecco alcuni casi d'uso:

  1. Voglio passare l'identità / utente principale dell'utente che ha chiamato un'azione API in un controller su livelli di Servizi e Repository. Ci sono decisioni importanti che vengono prese a livello di servizio a seconda delle affermazioni e / o privilegi / permessi dell'utente.

  2. Desidero identificare un tenant (si verifica usando intestazioni HTTP o nomi host) ma passare le informazioni sui titolari ai livelli Servizi e Repository in modo che possano essere utilizzate diverse cache e database Redis a seconda del titolare.

Posso facilmente creare un metodo in un servizio di base chiamato WithTenant (oggetto tenant); che posso usare per passare inquilini.

Questo significa che ogni chiamata di servizio che faccio avrà bisogno di questa chiamata a Conner.

Ad es. una chiamata:

 _inventoryService.CheckInventory() 

ora cambierà in:

_inventoryService.WithTenant(tenant).CheckInventory();

Allo stesso modo, se voglio passare HttpRequest oltre al Tenant, dovrò fare questo:

_inventoryService.WithHttpRequest (Request) .WithTenant (inquilino) .CheckInventory ();

Questo diventa ancora più complesso in quanto ho bisogno di passare questo al livello del repository. My Mongo DB Context è un singleton. Potrei doverlo cambiare per accettare l'inquilino in modo che la stringa di connessione possa essere modificata in base al tenant.

Quindi stavo pensando:

  1. Non c'è un modo in cui posso passare un oggetto Contesto attraverso questi livelli? Magari costruire una classe Context e impostare proprietà come Request o Tenant o qualsiasi altra cosa di cui ho bisogno e poi passarla attraverso i layer?
  2. Ancora più importante, c'è un modo per iniettarlo dinamicamente nei Costruttori di questi servizi, quindi non ho bisogno di ricollegarlo manualmente di nuovo? So che posso memorizzare il mio contesto nell'oggetto Request o forse anche in un contesto Owin. Potrei in qualche modo iniettarlo? Se è così, non ho visto nessun buon esempio su più livelli.

Suppongo che sto semplicemente cercando di semplificare la capacità di passare lo stato tra i livelli. Funzioni di taglio trasversale come Caching, Registrazione, Modifica dell'accesso al database su base Tenant, quindi se potessi creare un contesto centrale, ciò semplificherebbe enormemente le cose.

Qualche idea? Mi piacerebbe vedere cosa stanno facendo gli altri per risolvere questi problemi.

    
posta Anup Marwadi 19.09.2017 - 21:07
fonte

2 risposte

1

a tutti coloro che potrebbero voler sapere come è stato risolto, si è rivelata una soluzione semplice con un po 'di comprensione della durata delle iniezioni di dipendenza e di strategie di architettura pulite. La seguente è stata la logica applicata:

  1. Identifica il titolare - Cerca il titolare in base alle strategie disponibili (vale a dire un ID titolare in un'attestazione o un'intestazione host o la richiesta di origine).

  2. Ottieni il profilo del inquilino - Una volta identificato il conduttore, sono state ottenute le sue informazioni sul profilo. Ciò includeva le informazioni del centro dati del titolare e vari altri parametri (code, cluster di memorizzazione nella cache, ecc.)

  3. Costruisci un contesto di titolari (istanze singolari per Redis, Mongo e mantieni una stringa di connessione per i database SQL). Mantieni questo contesto tenant in una cache di memoria in modo che il recupero futuro sia facile.

  4. Creazione di una risoluzione del contesto dei titolari per risoluzione utilizzando Unity (o uno strumento DI simile). Per Resolve assicura che la risoluzione del contesto del titolare si verifichi solo una volta durante una richiesta e che la stessa istanza possa essere utilizzata su tutta la linea. Questo era importante perché lo stesso Tenant Context era necessario a livello del filtro dell'attributo, Application e Domain Services e talvolta fino al livello del repository. Tutti questi avrebbero lo stesso contesto invece di crearne uno nuovo per ogni classe su / giù per la catena alimentare.

  5. Usa il contesto in ogni livello per ottenere un factory di connessione per connettersi a Mongo, Redis, SQL o Code.

Una volta che ho avuto la fabbrica, una sostituzione globale mi ha permesso di usarlo su tutta la linea (ho avuto fortuna perché stavo già usando un'architettura più pulita).

Se qualcuno è veramente interessato a dare una occhiata al codice / dettagli, ecco un link che potrebbe aiutare: link

    
risposta data 22.11.2017 - 18:40
fonte
1

Mi sembra che Utente sia un input per CheckInventory e dovrebbe essere un parametro.

Mentre l'inquilino d'altra parte ti dice quale servizio chiamare il CheckInventory Method.

Avrei qualcosa di simile

public class myController
{
    private Dictionary<string,IService> services;

    public void CheckInv()
    {
         var tennant //from header
         var user //from token
         services[tenant.Id].CheckInv(user);
    }
}

È possibile inserire il proprio elenco di servizi nel controller e il repository mongoDb corretto (non utilizzare staticamente) in ogni servizio.

In alternativa, racchiudi il dizionario in una classe factory in modo da poter gestire il tenant non trovato, il caching e altri casi limite.

    
risposta data 20.09.2017 - 18:26
fonte

Leggi altre domande sui tag