LINQ to SQL e il modello di repository

Mi sento come se stessi girando in tondo. Non riesco a decidermi su quale sia il modello di repository giusto che utilizza LINQ to SQL . Se hai familiarità con MVC Storefront di Rob Conery vedrai che la sua implementazione avvolge i modelli generati da LINQ con un’altra class e tratta lo stesso generato da LINQ semplicemente come un object di trasferimento dati (DTO). Sembra qualcosa del genere:

//Custom wrapper class. namespace Data { public class Customer { public int Id {get;set;} public string Name {get;set;} public IList
Addresses {get;set;} } } //Linq-Generated Class - severly abbreviated namespace SqlRepository { public class Customer { public int Id {get;set;} public string Name {get;set;} public EntitySet
{get;set;} } } //Customer Repository namespace SqlRepository { public class UserRepository : IUserRepository { private _db = new DB(); //This is the Linq-To-Sql datacontext public IQueryable GetCusomters() { return from c in _db.Customers select new Customer // This is the wrapper class not the gen'd one { Id = c.Id, Name = c.Name, Addresses = new LazyList(c.Addresses) }; }

Qual è il vantaggio di farlo in questo modo (usando una class wrapper), al contrario del modo in cui Mike Hadlow suggerisce di utilizzare il pattern IRepository con LINQ to SQL nella sua versione di IRepository dove restituisce gli oggetti DTO dal repository?

Dove dovrebbe essere applicata e verificata la logica aziendale? Questo è in un layer separato chiamato dal repository su save / update o è integrato nella class wrapper?

Il fatto è che LINQ to SQL non è un vero Object Relation Mapper (ORM), è un generatore di livelli di accesso ai dati. Puoi renderlo un ORM andando a fondo a mano modificando file XML e giocando con SqlMetal e quant’altro, ma dove splende è come un DAL .

L’idea dietro un ORM è questa. Hai il tuo database SQL e i tuoi oggetti di dominio. Per progettare correttamente un database, si stanno andando a fare cose (come la normalizzazione) che logicamente non si traducono in un modello di object progettato correttamente e viceversa. Questo è chiamato “Impedance Mismatch”, il ruolo di un ORM è quello di affrontare tale discrepanza in modo pulito, efficace ed efficiente. L’interazione del database meno dolorosa è quasi una cosa secondaria.

L’idea alla base di un repository è che incapsula tutta la logica di persistenza e le dipendenze sull’infrastruttura dal resto dell’applicazione. Quando l’applicazione richiede un object Cliente, non dovrebbe essere necessario sapere se proviene da SQL Server, MySQL, un file XML o appartenenza a ASP.NET. Una volta ottenuto il disaccoppiamento, le eventuali modifiche apportate alla cronologia della persistenza non hanno alcun effetto sul resto della domanda.

Con questo in mente, diventa più chiaro perché ha fatto quello che ha fatto. LINQ to SQL viene utilizzato per generare il DAL, ma l’unica cosa che dovrebbe essere nota sul DAL è il repository, quindi una traduzione viene effettuata ai suoi oggetti di dominio. In questo modo può refactoring il suo modello di dominio senza preoccuparsi della sua storia di persistenza, e può refactoring il suo database senza preoccuparsi di effetti increspati attraverso la sua applicazione. Poteva anche iniziare a programmare sulla logica di business prima di decidere su domande come ORM da usare, o anche dove memorizzare i suoi dati.

Se dovesse utilizzare un vero ORM (come NHibernate ), quel codice di mapping viene gestito altrove (in XML o classi bootstrap). Penso che LINQ to SQL (e Robs open source DAL, SubSonic ) siano grandi progetti, ma più progettati per applicazioni a due livelli più piccole in cui qualcosa come il pattern di repository è eccessivo. La vetrina è anche un buon esempio del perché la complessità aggiuntiva di NHibernate può essere importante. Avrebbe potuto risparmiare un sacco di codice andando con qualcosa costruito per gestire questo tipo di scenario, piuttosto che farlo tutto manualmente.

Dipende da dove sono definiti i DTO e come si desidera testarlo. Se si utilizza DBML, LINQ su SQL desidera generare gli oggetti dati nel livello dati. Mentre LINQ to SQL supporta l’ ignoranza della persistenza, non si preoccupa di renderlo semplice. Entity Framework non lo supporta affatto.

Ciò significa che nel modello standard, il livello dati sta definendo tutte le quadro di dominio, il che è complicato se si desidera testare l’interfaccia utente / i livelli aziendali in vero isolamento dal livello dati.

Un approccio pragmatico potrebbe essere quello di utilizzare le definizioni dell’object dati dal livello dati nei test unitari, ma non il contesto dati (ovvero hide il contesto dati dietro l’interfaccia del repository, ma esporre i tipi di entity framework) – ma questo sta confondendo le acque un po ‘, e significa che l’interfaccia utente, ecc. hanno bisogno di fare un riferimento forte al livello dati. Ma se si pensa a questo come a un “livello di modello di dominio che capita di contenere anche un’implementazione di repository che potremmo o non potremmo usare” potresti giustificarlo.

Mantenere entity framework di dominio completamente separate rende il test delle unità e l’ inversione del controllo (IoC) più “puro”, ma aumenta la quantità di codice che hai (quindi a doppio taglio).

Gli oggetti generati sono serializzabili? Ho avuto l’impressione che non lo fossero. È solo un caso di isolamento come diceva Marc Gravel sopra.

Cosa succede se si cambia il repository e si dispone di un MySQL, Oracle, file XML, servizio Web o qualsiasi altro fornitore di dati (repository)? Saresti legato all’assembly LINQ to SQL per fare riferimento alle quadro, giusto? Quale, naturalmente, non vorresti.