2016-06-03 12 views
5

Nel mio progetto web api 2, se voglio usare la libreria OData (che sembra impressionante e molto allettante) per le interrogazioni su alcune proprietà, questo costringerebbe il lato client a conoscere le proprietà esatte dei miei modelli di database. È una buona pratica? C'è un modo per evitare questo disaccoppiamento?Query con OData senza esporre i modelli ORM?

Per i seguenti modelli:

public class LetterEntity 
    { 
     public int Id {get; set;} 

     public string Title {get; set;} 

     public string Content {get; set;} 

     public string Source {get; set;} 

     public DateTime SendingTime {get; set;} 

     public string AnotherWierdString {get; set; 
     ... 
    } 

    public class LetterDTO 
    { 
     public int Id {get; set;} 

     public string Title {get; set;} 

     public string LetterContent {get; set;} 

     public string Source {get; set;} 

     public DateTime SendingTime {get; set;} 
    } 

    public class LetterInsideFolderDTO 
    { 
     public string Title {get; set;} 

     public string Source {get; set;} 
    } 


public class LettersController : ApiController 
{ 
    // In this approach method, I hate the fact that a LetterEntity must be used for the query. 
    [HttpGet] 
    [Route("api/letters")] 
    [EnableQuery] 
    public IQueryable<LetterInsideFolderDTO> Get(ODataQueryOptions<LetterEntity> query) 
    { 
     IQueryable<Letter> letters = db.Letters; 

     var afterQuery = query.ApplyTo(letters) 

     IQueryable<LetterInsideFolderDTO> dtos = afterQuery.ProjectTo<LetterInsideFolderDTO>(afterQuery) 

     return dtos; 
    } 


    // Is there a way to do something like the following?: 
    [HttpGet] 
    [Route("api/letters")] 
    [EnableQuery] 
    public IQueryable<LetterInsideFolderDTO> Get(ODataQueryOptions<LetterDTO> query) 
    { 
     IQueryable<Letter> letters = db.Letters; 

     // Convert the query to work on the entities somehow? Should I use a mapping between LetterDTO to LetterEntity? 
     // I only have a map from LetterEntity to LetterDTO 
     var afterQuery = query.ApplyTo(letters) 

     IQueryable<LetterInsideFolderDTO> dtos = afterQuery.ProjectTo<LetterInsideFolderDTO>(afterQuery) 

     return dtos; 
    } 
} 

A causa del fatto che in questo momento prendo modello Entità direttamente nella query clienti, v'è una forte accoppiamento tra client e server. Per esempio se voglio interrogare ed ottenere tutte le lettere che ha "abc" all'interno del campo Content, ho bisogno di rotta verso il seguente:

api/letters/?$filter=contains(Content,'abc') 

Se domani decido di cambiare la proprietà da "Contenuto" a "LetterContent" il codice di tutti i client sarà rotto.

Come posso superarlo?

Grazie!

EDIT:

Vi prego di fare un esempio concreto, non capisco ancora cosa hateoas sono (se questo aiuta a risolvere questo problema), Come può servizio di documentazione di aiuto a me? Impegnerà comunque i clienti a cambiare il loro codice se deciderò di cambiare i miei modelli EDM?

+1

Un altro modo è non usare ODATA :), perché ODATA, il client deve conoscere EdmModel (schema ODATA) prima di fare query :) –

+0

Basta esporre il DTO che sarebbe essenzialmente un ResourceModel. Vedere https://spring.io/understanding/HATEOAS –

+0

@CuongLe esiste un'altra alternativa per molte query diverse? Non voglio un'azione diversa per ogni query sul mio server –

risposta

0

se voglio usare la biblioteca di OData (che sembra impressionante e molto allettante) per le query più di alcune proprietà, che avrebbe costretto il lato client per conoscere le proprietà esatte dei miei modelli di database. È una buona pratica?

Dipende da cosa si intende per "costringerebbe lato client per conoscere le proprietà esatte". Esistono due modi:

  1. Utilizzare i proxy generati automaticamente da datasvcutil. In effetti, ciò costringerebbe il lato client a conoscere le proprietà esatte poiché sono hard-coded in client-side. Quando il lato server viene modificato, il client si rompe: client/server sono strettamente accoppiati. In generale è brutto, ma di solito se hai bisogno di smth per essere fatto rapidamente - datasvcutil è il tuo strumento.

  2. Impara il tuo cliente a leggere il documento di servizio, in modo che possa decidere dinamicamente quali risorse può interrogare. Hai tutto per questo - documento di servizio, metadati.

Ricordare che OData è costruito su architettura REST che ha una serie di vantaggi che vengono raggiunti tramite una serie di vincoli. Diversi vincoli sono l'indirizzabilità e HATEOAS.

L'indirizzabilità significa che ogni risorsa deve avere il proprio indirizzo.

HATEOAS significa che in un dato momento, il cliente, in base all'ipermedia in rappresentazione della risorsa corrente, deve disporre di tutte le informazioni necessarie per decidere dove effettuare il transito successivo.

per sapere dove client di transito deve

  1. sapere come trovare le risorse (URL) nel flusso di dati in cui egli può transito. Come ottenere dati con testo e URL: il client deve sapere come trovare gli URL. L'URL può avere significati diversi, come diversi URL per le operazioni CRUD.

  2. Ottieni questi dati con le risorse. In generale il cliente deve sapere come avviare il servizio di query

Il primo punto viene risolto tramite i profili. Profilo: consente ai clienti di conoscere la semantica aggiuntiva associata alla rappresentazione delle risorse (https://tools.ietf.org/html/rfc6906). Consideralo documentazione OData. In caso di OData il tuo cliente deve sapere che i dati in OData sono rappresentati tramite i formati Atom o Json. Il cliente deve conoscere i principi di costruzione di query al servizio OData, di ottenere record specifici e così via.

Se client chiama OData indirizzo root - smth come ... OData.svc, avrebbe ottenuto l'elenco di tutte le risorse che può interrogare (documento di servizio). Ecco come viene risolto il secondo punto.

Si può arrivare oltre e ottenere i metadati tramite il suffisso $ metadata. Questo ti darebbe tutte le proprietà delle risorse.

+0

Spero di averlo chiarito ora nella mia domanda. Voglio poter interrogare da un campo che è presente SOLO nel mio modello di entità. Non è presente nel DTO, quindi i clienti devono saperlo, il che causa un brutto accoppiamento. Puoi dare un esempio concreto del mio problema? Non capisco ancora come usare HATEOAS mi aiuterà –

+0

@ S.Peter Sono confuso - mi chiedi se è una buona pratica che il lato client possa conoscere le proprietà esatte dei tuoi modelli di database e che creerebbe l'accoppiamento e più tardi stai dicendo che vuoi essere in grado di eseguire una query in base a un campo che è presente SOLO nel modello di entità. Perché non esponi il tuo modello di entità allora? – nikita

+0

Dico che non è una buona pratica che il lato client possa conoscere le proprietà esatte dei modelli di database e chiedere un modo per ottenere la stessa query che ho descritto nella mia domanda (una query che si riferisce a una proprietà che è presente solo nel modello di database) senza esponendo il modello di database –

0

Credo che esponendo le entità direttamente è una cattiva pratica nella maggior parte dei casi. Consiglierei DTO in quasi tutti i casi. Ti consente di evolvere il tuo database e la tua logica di business senza rompere l'API. Esistono alcuni ottimi casi di utilizzo per OData, ad esempio iniziative di dati aperti in cui il governo pubblica i dati così come sono.

ho dovuto costruito un app che era essenzialmente griglie sui dati con tutto il filtraggio e opzioni di ordinamento. Volevo usare OData ma non ho trovato un modo per fare query su entità ma proiettare su DTOs quindi ho costruito la mia libreria per convertire i filtri jqgrid in query IQueryable - https://github.com/KodarLtd/WebApiJQGridFilters Nota che non consiglio di usare questo codice perché non è pieno libreria in primo piano e non è affatto documentata. Fornisco solo una prova di quanto io creda fermamente nell'approccio DTO.

vorrei essere smentito in modo da poter usare OData ma tornare DTOs per il mio prossimo progetto.

+0

Utilizziamo ApiController e si desidera utilizzare solo la sintassi OData per l'operazione GET. Esiste un modo per prendere ODataQueryOptions e far eseguire quella query al modello EDM reale (i.e Letter)? O devo prendere parametro di tipo ODataQueryOptions nella mia azione GET? –

+0

Come dice la mia risposta, credo che questo non sia possibile, così ho scritto la mia cosa da fare praticamente (anche se è inferiore a OData in ogni altro modo). Se trovi che ho torto e puoi ottenerlo con OData, fammi sapere. Quando dico che non è possibile intendo che non c'è modo di impostare una proprietà e di farla finita. È possibile elaborare autonomamente le opzioni di query e applicarle all'entità corrispondente. Ecco un articolo che potrebbe aiutarti se segui questa strada - http://www.ben-morris.com/parsing-odata-queries-decoupled-data-entities-webapi/ – Stilgar

Problemi correlati