Entity Framework Entity - Alcuni dati dal servizio Web - La migliore architettura?

10

Attualmente stiamo utilizzando Entity Framework come un ORM su alcune applicazioni Web, e fino ad ora ci ha soddisfatto come tutti i nostri dati sono archiviati in un singolo database. Stiamo utilizzando il modello di repository e disponiamo di servizi (il livello del dominio) che li utilizzano e restituiscono le entità EF direttamente ai controller MVC di ASP.NET.

Tuttavia, è arrivato il requisito di utilizzare un'API di terze parti (tramite un servizio web) che ci fornirà informazioni aggiuntive relative all'utente nel nostro database. Nel nostro database utenti locale, memorizzeremo un ID esterno che possiamo fornire all'API per ottenere ulteriori informazioni. Vi sono molte informazioni disponibili, ma per semplicità, una di queste è relativa alla società dell'utente (nome, responsabile, stanza, titolo di lavoro, ubicazione, ecc.). Queste informazioni verranno utilizzate in vari punti delle nostre app Web, anziché essere utilizzate in un unico posto.

Quindi la mia domanda è: dov'è il posto migliore per popolare e accedere a queste informazioni? Dato che viene utilizzato in vari luoghi, non è molto sensato scaricarlo su una base ad hoc ovunque utilizziamo nell'applicazione Web, quindi è opportuno restituire questi dati aggiuntivi dal livello dominio.

Il mio pensiero iniziale era solo quello di creare una classe di modello wrapper che contenga l'entità EF (EFUser) e una nuova classe 'ApiUser' contenente le nuove informazioni - e quando otteniamo un utente, otteniamo EFUser, e poi ottenere le informazioni aggiuntive dall'API e popolare l'oggetto ApiUser. Tuttavia, mentre ciò andrebbe bene per ottenere utenti singoli, cade quando si ottengono più utenti. Non possiamo raggiungere l'API quando ottieni un elenco di utenti.

Il mio secondo pensiero era solo quello di aggiungere un metodo singleton all'entità EFUser che restituisce l'ApiUser, e basta popolarlo quando necessario. Questo risolve il problema di cui sopra poiché accediamo solo quando ne abbiamo bisogno.

O il pensiero finale era di mantenere una copia locale dei dati nel nostro database e sincronizzarla con l'API quando l'utente si collegava. Questo è un lavoro minimo in quanto è solo un processo di sincronizzazione - e non abbiamo il sovraccarico di colpire DB e API ogni volta che vogliamo ottenere informazioni sugli utenti. Tuttavia, questo significa archiviare i dati in due punti e significa anche che i dati non sono aggiornati per qualsiasi utente che non ha effettuato l'accesso per un po 'di tempo.

Qualcuno ha qualche consiglio o suggerimento su come meglio gestire questo tipo di scenario?

    
posta stevehayter 12.12.2013 - 17:14
fonte

3 risposte

3

Il tuo caso

Nel tuo caso tutte e tre le opzioni sono valide. Penso che l'opzione migliore sia probabilmente quella di sincronizzare le tue fonti di dati in un posto in cui l'applicazione asp.net non è nemmeno a conoscenza. Cioè, evita ogni volta i due recuperi in primo piano, sincronizza l'API con il db in silenzio). Quindi se questa è un'opzione praticabile nel tuo caso, io dico di farlo.

Una soluzione in cui si effettua il recupero 'una volta' come suggerisce l'altra risposta non sembra molto praticabile in quanto non mantiene la risposta da nessuna parte e ASP.NET MVC effettuerà il recupero per ogni richiesta più e più volte.

Eviterei il singleton, non penso che sia una buona idea per molti dei soliti motivi.

Se la terza opzione è non valida - un'opzione è caricarla pigro. Cioè, avere una classe estendere l'entità, e farlo colpire l'API su una base bisogno di . È un'astrazione molto pericolosa anche se è ancora più magico e non ovvio.

Credo che in realtà si riducano a diverse domande:

  • Con quale frequenza l'API chiama i dati? Non spesso? Terza opzione. Spesso? All'improvviso la terza opzione non è troppo praticabile. Non sono sicuro di essere contro le chiamate ad-hoc come te.
  • Quanto costa una chiamata API? Paghi per chiamata? Sono veloci? Gratuito? Se sono veloci, effettuare una chiamata ogni volta potrebbe funzionare, se sono lenti è necessario avere una sorta di previsione in atto e effettuare le chiamate. Se costano soldi, questo è un grande incentivo per il caching.
  • Quanto deve essere veloce il tempo di risposta? Ovviamente più veloce è meglio, ma la possibilità di sacrificare la velocità per semplicità potrebbe valerne la pena in alcuni casi, specialmente se non è rivolta direttamente a un utente.
  • Quanto sono diversi i dati API dai tuoi dati? Sono due cose concettualmente diverse? In tal caso, potrebbe essere ancora meglio esporre l'API all'esterno piuttosto che restituire direttamente il risultato dell'API con il risultato e lasciare che l'altro lato effettui la seconda chiamata e gestisca la gestione.

Una parola o due sulla separazione delle preoccupazioni

Permettetemi di discutere contro ciò che Bobson sta dicendo sulla separazione delle preoccupazioni qui. Alla fine della giornata - mettere tale logica in entità come quella viola la separazione delle preoccupazioni altrettanto male.

Avere un repository di questo tipo viola la separazione delle preoccupazioni altrettanto male inserendo la logica della presentazione centrica nel livello della logica aziendale. Il tuo repository ora è improvvisamente a conoscenza delle cose relative alla presentazione come il modo in cui visualizzi l'utente nei tuoi controller mvc asp.net.

In questa domanda correlata ti ho chiesto di accedere alle entità direttamente da un controller. Consentitemi di citare una delle risposte qui:

"Welcome to BigPizza, the custom Pizza shop, may I take your order?" "Well, I'd like to have a Pizza with olives, but tomato sauce on top and cheese at the bottom and bake it in the oven for 90 minutes until it's black and hard like a flat rock of granite." "OK, Sir, custom Pizzas are our profession, we'll make it."

The cashier goes to the kitchen. "There is a psycho at the counter, he wants to have a Pizza with... it's a rock of granite with ... wait ... we need to have a name first", he tells the cook.

"No!", the cook screams, "not again! You know we tried that already." He takes a stack of paper with 400 pages, "here we have rock of granite from 2005, but... it didn't have olives, but paprica instead... or here is top tomato ... but the customer wanted it baked only half a minute." "Maybe we should call it TopTomatoGraniteRockSpecial?" "But it doesn't take the cheese at the bottom into account..." The cashier: "That's what Special is supposed to express." "But having the Pizza rock formed like a pyramid would be special as well", the cook replies. "Hmmm ... it is difficult...", the desparate cashier says.

"IS MY PIZZA ALREADY IN THE OVEN?", suddenly it shouts through the kitchen door. "Let's stop this discussion, just tell me how to make this Pizza, we are not going to have such a Pizza a second time", the cook decides. "OK, it's a Pizza with olives, but tomato sauce on top and cheese at the bottom and bake it in the oven for 90 minutes until it's black and hard like a flat rock of granite."

(Leggi il resto della risposta, è davvero bello)

È ingenuo ignorare il fatto che ci sia un database - c'è un database, e non importa quanto tu voglia astrarre ciò, non sta andando da nessuna parte. La tua applicazione sarà a conoscenza dell'origine dati. Non sarai in grado di "scambiarlo a caldo". Gli ORM sono utili ma perdono a causa di quanto sia complesso il problema che risolvono e per un sacco di motivi di prestazioni (come Select n + 1 per esempio).

    
risposta data 17.12.2013 - 07:54
fonte
2

Con una corretta separazione delle preoccupazioni , nulla al di sopra del livello Entity Framework / API dovrebbe nemmeno capire da dove provengono i dati . A meno che la chiamata API non sia costosa (in termini di tempo o elaborazione), l'accesso ai dati che lo utilizza dovrebbe essere trasparente come l'accesso ai dati dal database.

Il modo migliore per implementare questo, quindi, sarebbe aggiungere proprietà extra all'oggetto EFUser che lazy-load i dati API come necessario. Qualcosa del genere:

partial class EFUser
{
    private APIUser _apiUser;
    private APIUser ApiUser
    {
       get { 
          if (_apiUser == null) _apiUser = API.LoadUser(this.ExternalID);
          return _apiUser;
       }
    }
    public string CompanyName { get { return ApiUser.UserCompanyName; } }
    public string JobTitle{ get { return ApiUser.UserJobTitle; } }
}

Esternamente, la prima volta che viene utilizzato CompanyName o JobTitle ci sarà una singola chiamata API fatta (e quindi un piccolo ritardo), ma tutte le chiamate successive finché l'oggetto non viene distrutto sarà altrettanto veloce e facile come accesso al database.

    
risposta data 16.12.2013 - 18:43
fonte
0

Un'idea è quella di modificare ApiUser per non avere sempre le informazioni aggiuntive. Invece, metti un metodo su ApiUser per recuperarlo:

ApiUser apiUser = backend.load($id);
//Locally available data directly on the ApiUser like so:
String name = apiUser.getName();
//Expensive extra data available after extra call:
UserExtraData extraData = apiUser.fetchExtraData();
String managerName = extraData.getManagerName();

Potresti anche modificarlo leggermente per utilizzare il caricamento lazy di dati aggiuntivi, in modo da non dover estrarre UserExtraData dall'oggetto ApiUser:

//Extra data fetched on first get:
String managerName = apiUser.lazyGetExtraData().getManagerName();

In questo modo, quando hai una lista, i dati extra non verranno recuperati per impostazione predefinita. Ma puoi ancora accedervi mentre attraversi la lista!

    
risposta data 18.12.2013 - 10:23
fonte

Leggi altre domande sui tag