Restituzione di entità parzialmente riempita

3

Il mio collega sta cercando di recuperare solo le proprietà necessarie come best practice per evitare carichi di lavoro non necessari sul lato del database come di seguito.

    public async Task<IEnumerable<Product>> GetProducts(List<int> ids)
    {
        var items = await _context.Products.Where(u => ids.Contains(u.Id))
                        .Select(u => new { u.Id, u.Name, u.Published })
                        .ToListAsync();

        return items.Select(u => new Product{ Id = u.Id, Name = u.Name,Published = u.Published });
    }

    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public bool Published { get; set; }
        public string MetaTitle { get; set; }
        public string MetaKeywords { get; set; }
        public string Sku { get; set; }
        public decimal Price { get; set; }
        public decimal Cost { get; set; }
        public DateTime CreatedAt { get; set; }
        ///...And other properties goes here
    }

Come hai visto, ci sono solo 3 proprietà recuperate dal database nonostante il prodotto abbia molte proprietà.

L'IMO che recupera tutte le proprietà non ha realmente un carico di lavoro importante. Lo farei solo nel caso in cui ci sia un problema di prestazioni nella query. sono stato indicato che non è possibile sapere cosa è esattamente ritornato dal metodo senza leggere i dettagli di implementazione. Non solo, non è possibile utilizzare esattamente lo stesso nome del metodo quando è necessario un altro metodo che abbia esattamente lo stesso segno di metodo ma recuperi proprietà diverse. Ad esempio

public async Task<IEnumerable<Product>> GetProducts(List<int> ids)
{
    //logic goes here
    return items.Select(u => new Product{ Id = u.Id, Name = u.Name,Published = u.Published });
}

public async Task<IEnumerable<Product>> GetProducts(List<int> ids)
{
    //logic goes here
    return items.Select(u => new Product{ Id = u.Id, Name = u.Name,Cost = u.Cost, Sku = u.Sku });
}

Il codice non verrà compilato perché ci sono esattamente gli stessi 2 metodi e uno di loro dovrebbe differenziare il suo nome. Sono sicuro che è davvero difficile dare nomi significativi a questi casi.

I suoi argomenti stavano restituendo tipi specifici (DTO) avrebbero causato un sacco di tipi nel progetto.

In realtà molti tipi non sono solo un problema. Non è possibile utilizzare esattamente lo stesso nome del metodo ed è molto difficile dare nomi significativi per i tipi e i nomi dei metodi.

La mia soluzione a questo problema, non mettere questa logica in un metodo. Query in linea ovunque venga utilizzata. Già non esiste una riutilizzabilità della query e anche la query in realtà non contiene alcun tipo di logica che meriti di essere riutilizzata in altri luoghi. Ma secondo la sua idea, le query non dovrebbero essere eseguite al di fuori dei metodi di servizio, perché le rigide regole di stratificazione.

Qual è la tua idea su come restituire l'entità parzialmente riempita dal metodo? In secondo luogo, cosa succede se vogliamo recuperare solo le proprietà richieste, quindi qual è il modo migliore per farlo?

So che dirai di usare DTO ma cercherò di progettare tutto in questo modo rendendo tutto più difficile quindi a mio avviso dovrebbe essere usato solo nel caso di problemi correlati alle prestazioni, ma ancora in fase di allestimento di query risolvono tutti questi problemi ma alcuni le persone hanno regole di stratificazione rigorose senza ragioni pragmatiche.

Mi piacerebbe sentire le tue opinioni.

    
posta Freshblood 21.05.2017 - 22:50
fonte

4 risposte

7

No, l'entità dovrebbe avere tutti i suoi dati. Consentendo di essere parzialmente popolato, si otterranno errori dispari poiché questo tipo può essere utilizzato ovunque, ma i ricevitori non avranno alcuna idea se sono completamente o parzialmente popolati. Questo è un buon modo per ottenere un comportamento inaspettato. Ancora peggio, alla fine avrai bisogno di un diverso sottoinsieme di dati rispetto al caso che stai chiedendo ora, e qualcuno creerà un nuovo modo per caricare solo QUESTO sottoinsieme di dati. Il tuo problema ora è ulteriormente esacerbato. È anche improbabile che tu abbia importanti miglioramenti delle prestazioni selezionando un insieme più piccolo di colonne da una tabella; SQL ha ancora bisogno di leggere l'intera riga, credo. Hai testato anche questo approccio? Potrebbe essere che stai ottimizzando prematuramente.

I tuoi oggetti dovrebbero essere progettati attorno ai casi d'uso che supportano. Eventuali dati devono essere perché tali dati sono necessari affinché l'oggetto soddisfi il proprio scopo. Le DTO abbinano le tabelle del database (o altre cose del livello dati, forse un servizio web) e quindi non sono mai realmente appropriate come entità. Se hai un caso d'uso che non ha bisogno di tutti i dati, allora dovrebbe essere un tipo separato. Se la classe esistente soddisfa le esigenze del caso d'uso, senza modifiche non è necessario creare alcun tipo separato. Ma non dovrebbe essere speciale per essere solo parzialmente popolato.

    
risposta data 21.05.2017 - 23:17
fonte
5

Le entità parzialmente riempienti nelle applicazioni di database forniscono raramente un guadagno in termini di prestazioni, poiché il numero di roundtrip di una query ha in genere un'influenza molto maggiore rispetto alla quantità di dati trasferiti in un round trip. Nella maggior parte dei casi pratici, non fa un'enorme differenza se si estraggono 3 o 30 colonne da una tabella DB a condizione che il numero di query non cambi. Pertanto, nel 99% dei casi, l'uso di entità parzialmente riempite non è raccomandabile, si tratta di un'ottimizzazione prematura. Per lo stesso motivo, raramente offre vantaggi nell'utilizzo di DTO diversi per la stessa tabella di database.

Ovviamente, ci sono delle eccezioni da questa regola - per esempio, se un'entità contiene un testo di grandi dimensioni o un campo binario e si hanno casi d'uso in cui non è necessario il campo grande. Quindi può essere utile caricare l'entità senza questo campo. Ma questo è un caso raro, dovresti ottimizzare solo per questo dopo aver dimostrato che è la causa di un problema di prestazioni.

Se segui questo consiglio, non entrerai nel problema di denominazione, poiché per quei rari casi in cui carichi parzialmente le entità, non dovrebbe essere difficile fornire un metodo con un nome speciale per questo caso eccezionale, o un DTO specifico se preferisci questo. Il caso standard, tuttavia, dovrebbe essere sempre il caso in cui l'intera entitity viene restituita con tutti gli attributi riempiti.

Nel contesto della mappatura relazionale a oggetti, questo è un argomento ben noto, si chiama "oggetti parziali". Alcuni ORM li supportano, alcuni non li supportano e alcuni li supportano, ma con un grande segnale di pericolo nella documentazione .

    
risposta data 22.05.2017 - 08:08
fonte
2

Se il caso d'uso impone, preferisci più entità rispetto a parziale riempito. Ad esempio, forse la tua query iniziale restituisce un elenco di prodotti. Se sono necessarie solo poche proprietà, crea un oggetto con solo queste 3 proprietà. Una volta che un utente fa clic su un particolare prodotto nell'elenco, vai a ottenere il prodotto completo utilizzando un oggetto diverso con tutte le proprietà riempite.

Questo ottimizzerà il trasferimento dei dati se l'applicazione è configurata in quel modo.

    
risposta data 23.05.2017 - 16:46
fonte
0

Penso che tu stia confondendo le best practice del database (specifica sempre i campi che stai restituendo, piuttosto che usare una selezione jolly) e selezionando solo i dati che ti servono.

Il primo problema è una best practice, non perché cerca di restituire solo i dati necessari, ma perché consente alla tabella di evolvere con meno problemi. Ad esempio, puoi aggiungere righe alla tabella e non si romperà nulla. Inoltre, specificando i campi, puoi garantire l'ordine in cui verranno selezionati i dati.

Non c'è niente di sbagliato nel restituire tutti i campi in una query, anche se non li stai usando (anche se la tabella ha 2000 colonne è probabilmente una storia diversa, ma è di per sé una cattiva progettazione).

In realtà, questa è spesso una situazione "il tuo chilometraggio può variare". supponiamo che tu stia restituendo 1 milione di righe, quindi la selezione di un sottoinsieme di colonne può comportare una quantità di dati molto inferiore restituita. Ma in questi casi, dovresti creare nuovi oggetti anonimi (se il tuo linguaggio supporta tali) piuttosto che riempire parzialmente un'entità.

Ricordare che gli ORM non sono generalmente adatti agli aggiornamenti batch di grandi dimensioni, quindi non è necessario utilizzare le entità se è necessario aggiornare un numero elevato di righe. Dovresti rilasciare quelli come comandi batch. Ciò significa che devi limitare l'utilizzo dell'aggiornamento delle entità a insiemi di dati più piccoli.

    
risposta data 23.05.2017 - 19:49
fonte

Leggi altre domande sui tag