2010-05-07 15 views
14

Nella mia esperienza di creazione di applicazioni Web, ho sempre utilizzato un approccio a più livelli. Un DAL che ottiene i dati dal db e popola gli oggetti e BLL che ottiene gli oggetti dal DAL ed esegue qualsiasi logica di business richiesta su di essi e il sito Web che ottiene i dati di visualizzazione dalla BLL. Ho recentemente iniziato a studiare LINQ e la maggior parte degli esempi mostra le query che si verificano direttamente dai code-behind dell'applicazione Web (è possibile che abbia visto solo esempi eccessivamente semplificati). Nelle architetture n-tier, questo è sempre stato visto come un grande no-no.
Sono un po 'incerto su come progettare una nuova applicazione Web. Ho utilizzato Server Explorer e la progettazione dbml in VS2008 per creare le relazioni dbml e oggetto. Mi sembra poco chiaro se il dbml sia considerato il livello DAL, se il sito web deve chiamare metodi all'interno di un BLL, che quindi farebbe le query LINQ, ecc.
Quali sono alcune best practice di architettura generale o approcci a creare una soluzione di applicazione Web utilizzando LINQ to SQL?LINQ alle migliori pratiche per le applicazioni Web SQL

risposta

16

Ho paura che abbiate davvero visto esempi eccessivamente semplificati. LINQ to SQL (System.Data.Linq) è il tuo livello DAL. Le classi generate da L2S sono il tuo dominio (ma non confondere con Domain-Driven Design). Inoltre, puoi ancora scrivere il tuo Business Layer.

Io cerco sempre di impedire la fuoriuscita del LINQ su SQL DataContext nel livello di presentazione (la tua app Web). Quindi non dovrebbe essere in grado di creare o impegnare un DataContext. Né si devono restituire oggetti IQueryable<T> al livello di presentazione. IMO il livello aziendale dovrebbe avere il pieno controllo sulla durata del DataContext (unità di lavoro) e la forma delle query SQL.

Tuttavia, ci sono diversi sapori. Alcune persone tentano di rilassare questi vincoli. Altri addirittura vanno molto oltre. Dipende dal tuo gusto e dalle dimensioni dell'applicazione. Più grande è l'applicazione, più è giustificato aggiungere strati di astrazione.

Quando non si consente a IQueryable s e altri dati correlati di uscire dal livello aziendale, si finiscono per avere alcune sfide interessanti. Ad esempio, il livello di presentazione deve indicare al livello aziendale come ordinare i risultati. Mentre è possibile lasciare che il livello di presentazione ordini i risultati stessi, ciò significherebbe che si dovranno ottenere tutti i dati dal database e dalla pagina al livello di presentazione, cosa che potrebbe portare a un sistema con prestazioni molto scadenti. Esistono diverse soluzioni a questo problema. In tutti i casi dovrai informare il livello aziendale su come ordinare i risultati per te. Le soluzioni possono essere trovate qui in SO quando si cerca LINQ dynamic sort. Ho già scritto una soluzione del genere, here.

Un'altra sfida che non consente di disabilitare IQueryable s lasciando che BL porti è che anche gli oggetti di dominio spesso non possono lasciare il BL. La maggior parte degli oggetti del dominio LINQ to SQL conterrà proprietà lazy-loaded (ad esempio, raccolte in altri oggetti di dominio). Tuttavia, quando DataContext ha il controllo del livello aziendale, verrà eliminato, prima di restituire i risultati al livello di presentazione. Quando la presentazione accede a una proprietà lazy loaded, si verificherà un'eccezione, poiché lo DataContext è già stato eliminato. Quando si smaltisce lo DataContext nel proprio livello aziendale, questo comportamento è ovviamente "di progettazione".Consentendo al livello di presentazione di ottenere proprietà lazy load significa che BL perde il controllo sulle query che vengono inviate al database, perdendo così il controllo sulle prestazioni.

Per risolvere questo problema, è necessario restituire Data Transfer Objects (DTO) dal BL al livello di presentazione. Un DTO conterrà solo dati e no interno DataContext e nessuna proprietà caricata pigro. Un DTO può essere formattato appositamente per la richiesta effettiva a portata di mano. Le DTO ovviamente portano alla creazione di codice in testa, quindi la dimensione del sistema e le esigenze di prestazioni devono giustificarlo. Per rendere più facile per me, tendo a mettere i metodi di proiezione statici sul DTO. Anche se questo non è conforme al principio separation of concerns, credo che sia una soluzione molto pratica. Guardiamo ad esempio a questo CustomerDTO:

public class CustomerDTO 
{ 
    public int CustomerId { get; set; } 
    public string Name { get; set; } 
    // City is flatterned from Address.City. 
    public string City { get; set; } 

    internal static IQueryable<CustomerDTO> AsDTO(IQueryable<Customer> customers) 
    { 
     return 
      from customer in customers 
      select new CustomerDTO() 
      { 
       CustomerId = customer.Id, 
       Name = customer.Name, 
       City = customer.Address.City 
      }; 
    } 
} 

Questa DTO definisce una AsDTO metodo interno, che è in grado di convertire un insieme di oggetti di dominio Customer ad una raccolta di CustomerDTO DTOs. Ciò rende molto più semplice la conversione di oggetti di dominio in DTO. Guardate ad esempio a questo metodo BL:

public static CustomerDTO[] GetCustomersByCountry(string country) 
{ 
    using (var db = ContextFactory.CreateContext()) 
    { 
     IQueryable<Customer> customers = 
      (from customer in db.Customers 
      where customer.Address.Country == country 
      orderby customer.Name, customer.Id); 

     return CustomerDTO.AsDTO(customers).ToArray(); 
    } 
} 

La cosa bella di questo approccio è che quando si guarda la query SQL, si vedrà che solo il cliente Id, Nome e la Città del tavolo indirizzo verranno essere recuperato dal database. Questo perché il metodo AsDTO traduce uno IQueryable in un altro, consentendo a LINQ di SQL di eseguire l'operazione totale nel database.

Spero che questo dia alcune idee su cosa è possibile fare. Naturalmente, questa è la mia opinione sull'argomento e le cose che ho trovato pratico nelle mie situazioni.

+0

grazie per la risposta completa. Mi fa rabbrividire nel fare l'accesso ai dati nel livello di presentazione. Attualmente tendo a fare la maggior parte del mio ordinamento presso il datalayer in modo che non sia un problema. Non ho mai sentito parlare di DTO, sembra qualcosa da approfondire. – derek

+0

Spesso si ritiene che le DTO abbiano un sovraccarico. Sono particolarmente utili quando si inviano dati attraverso il filo (ad esempio, quando si hanno servizi Web WCF o ASMX). Ad esempio, quando leggi "Microsoft.NET: Architecting Applications for the Enterprise" di Dino Esposito, noterai che Dino pensa che di solito generano un sovraccarico quando trasferisci oggetti tra livelli dello stesso AppDomain. Mentre ha ragione su questo, ho comunque trovato che sono utili in quel particolare scenario e vedo il sovraccarico diventare più piccolo quando la tecnologia migliora ... – Steven

+0

Per esempio, i nuovi costrutti di linguaggio C# come le proprietà automatiche rendono più facile la definizione di DTO e un strumento di refactoring come Refactor! Pro consente di generare automaticamente un DTO da una definizione di tipo anonima all'interno di una query LINQ. – Steven

4

LINQ to SQL è l'accesso DB nell'implementazione DAL, se si desidera separare tra DAL e BLL. Se si dispone di un'applicazione Web meno complessa (e non si intende mai invertire i backend DB), è possibile uscire senza DAL/BLL espliciti e fare tutto ciò che si trova nel codice. LINQ to SQL funziona alla grande per operazioni di sola lettura, ma sembra un po 'più di lavoro per implementare le operazioni di scrittura.

Problemi correlati