Come implementare diverse rappresentazioni / modelli delle stesse risorse nel servizio API REST (ASP.NET)?

0

Panoramica

Sto creando un servizio REST con ASP.NET WebAPI e Entity Framework. Devo esporre rappresentazioni di risorse diverse in base ai ruoli degli utenti. Ad esempio, api/employees restituirà diversi oggetti Employee per l'amministratore e per l'utente normale:

// Model for regular user
public class EmployeeBasic : EncryptableEntity {
    public string FirstName { get; set; }
    public string LastName { get; set; }

    // ...
}

// Model for admin
public class EmployeeExtended : EmployeeBasic {
    public decimal Salary { get; set; }
}

Ci sono alcuni fattori che rendono questo un po 'complicato per me:

  1. Ogni risorsa ha filtri e ordinamenti sul lato server. I modelli di ordinamento e filtro dovrebbero anche variare in base al ruolo dell'utente (ad esempio, l'utente normale dovrebbe essere in grado di ordinare per nome completo, ma non per stipendio). Dovrebbero, ovviamente, essere inclusivi - l'amministratore dovrebbe essere in grado di ordinare / filtrare in base a tutti i campi che l'utente normale può + qualche altro (quindi un'eredità potrebbe essere utile qui).
  2. Alcuni campi sono crittografati nel database - questo lo rende più complicato, perché alcuni campi non possono essere filtrati / ordinati nella query EntityFramework. Devono essere decodificati e quindi filtrati / ordinati localmente.

Soluzione corrente

Prima di rendermi conto che ho bisogno di diverse rappresentazioni di risorse, sono riuscito a creare una soluzione abbastanza universale per l'interrogazione delle risorse crittografate:

1) EFEncryptedQuery class: questa classe recupera i record dal database (con alcuni filtri), li decrittografa, applica ulteriori filtri / ordinamenti locali e associa le entità aziendali con AutoMapper.

Esempio di utilizzo:

public IList<Business.EmployeeBasic> GetAll(EmployeesListFilters filters, 
    ISortModel<Business.EmployeeBasic> sortModel)
{
    using (var dbContextScope = _dbContextScopeFactory.CreateReadOnly())
    {
        var query = new EFEncryptedQuery<DB.Employee, Business.EmployeeBasic>(
            DBContextFrom(dbContextScope).Employees);

        query.AddFiltersModel(filters);
        query.AddSortModel(sortModel);

        return query.ExecuteAndGetResults();
    }
}

2) I filtri e i modelli di ordinamento sono classi che contengono mappe con LambdaExpressions che vengono successivamente utilizzate per l'ordinamento / il filtro. Diamo un'occhiata all'esempio del modello di ordinamento:

public class EmployeeBasicSortModel : ServiceSortModel<DB.Employee, Business.EmployeeBasic>
{
    public EmployeeBasicSortModel(PublicSortModel sortModel) : base(sortModel)
    {
    }

    // Expressions used for local sorting (mostly for encrypted properties)
    protected override void CreateLocalSortsMap(List<SortMapping<BusinessModel.EmployeeBasic>> localSortsMap)
    {
        localSortsMap.Add(SortMapping<Business.EmployeeBasic>.Create("FullName", c => c.FirstName + " " + c.LastName));
    }

    // Expressions used for database sorting
    protected override void CreateQuerySortsMap(List<SortMapping<DB.Employee>> querySortsMap)
    {
        querySortsMap.Add(SortMapping<DB.Employee>.Create("InsertDate", c => c.InsertDateUTC));
    }
}

Base ServiceSortModel contiene la logica di come applicare i mapping a IEnumerable<BusinessModel> e IQueryable<DBModel> oggetti. Inoltre, i modelli di filtro contengono non solo i mapping lambda, ma anche le proprietà con valori di filtro. Queste proprietà e mappature dovrebbero idealmente essere ereditate da classi derivate.

L'idea e il problema

La soluzione di cui sopra è stata molto comoda per me, prima di rendermi conto che ho bisogno di supportare più di una rappresentazione delle stesse risorse. Poi, ho iniziato a chiedermi se posso fare qualcosa per creare metodi di servizio come la seguente dichiarazione:

public IList<TEmployee> GetAll<TEmployee>(IFiltersModel<TEmployee> filters, 
        ISortModel<TEmployee> sortModel)
    where TEmployee : Business.EmployeeBasic

Tale metodo si aspetterebbe di ottenere modelli di filtro e ordinamento per classi specifiche dalla gerarchia Employees. Di conseguenza, restituirebbe l'elenco degli impiegati con una rappresentazione adeguata. All'inizio l'idea sembrava grandiosa, ma mi ha portato a un codice molto complicato e alcuni problemi di covarianza che ho descritto qui: link

Per ora, sono riuscito a risolvere i problemi di covarianza di ordinamento creando alcune classi davvero brutte con più tipi generici, ma non sono soddisfatto della soluzione. Inoltre, ho ancora problemi a capire come posso far funzionare i filtri in questo modo.

Domande

La soluzione di cui sopra ha qualche possibilità di funzionare davvero? Più tempo trascorro su di esso, più sembra complicato. Probabilmente mi manca qualcosa di cruciale che mi porta a soluzioni troppo complicate. In che modo queste cose vengono tipicamente implementate? Ho pensato che creare una soluzione universale come EFEncryptedQuery lo renderà facile da usare ... ma forse un semplice gruppo di istruzioni if / else funzionerebbe meglio qui? Sarò grato per qualsiasi suggerimento.

    
posta PJDev 13.05.2017 - 23:04
fonte

0 risposte

Leggi altre domande sui tag