2010-09-28 15 views
6

Sto vivendo una crisi architettonica filosofica di metà carriera. Vedo le linee molto chiare tra ciò che è considerato codice client (UI, Web Services, MVC, MVP, ecc.) E il livello di servizio. Le linee del livello di servizio, tuttavia, stanno diventando più sfocate di minuto in minuto. E tutto è iniziato con la possibilità di interrogare il codice con Linq e il concetto di caricamento Lazy.Queryability e Lazy Loading in C# offuscano le linee di Data Access vs Business Logic?

Ho creato un Business Layer costituito da contratti e implementazioni. Le implementazioni potrebbero quindi avere dipendenze da altri contratti e così via. Questo viene gestito tramite un contenitore IoC con DI. C'è un servizio che gestisce il DataAccess e tutto ciò che fa è restituire un UnitOfWork. Questo UnitOfWork crea una transazione quando viene estesa e impegna i dati nel metodo Commit. [View this Article (Testability and Entity Framework 4.0)]:

public interface IUnitOfWork : IDisposable { 
    IRepository<T> GetRepository<T>() where T : class; 
    void Commit(); 
} 

Il Repository è generico e lavora contro due implementazioni (EF4 e un InMemory DataStore). T è costituito da POCO generati dallo schema del database o dai mapping EF4. La testabilità è incorporata nel design del repository. Possiamo sfruttare l'implementazione in-memory per affermare i risultati con le aspettative.

public interface IRepository<T> where T : class { 
    IQueryable<T> Table { get; } 
    void Add(T entity); 
    void Remove(T entity); 
} 

Mentre l'origine dati è astratta, IQueryable mi dà ancora la possibilità di creare query dove voglio all'interno della logica di business. Ecco un esempio.

public interface IFoo { 
    Bar[] GetAll(); 
} 

public class FooImpl : IFoo { 
    IDataAccess _dataAccess; 
    public FooImpl(IDataAccess dataAccess) { 
     _dataAccess = dataAccess; 
    } 

    public Bar[] GetAll() { 
     Bar[] output; 
     using (var work = _dataAccess.DoWork()) { 
      output = work.GetRepository<Bar>().Table.ToArray(); 
     } 
     return output; 
    } 
} 

Ora è possibile vedere come le query potrebbero diventare ancora più complesse quando si eseguono join con filtri complessi.

Pertanto, le mie domande sono:

  1. importa che non esiste una chiara distinzione tra BLL e DAL?.
  2. La queryabilità considera l'accesso ai dati o la logica aziendale quando si trova dietro un livello del repository che si comporta come un'astrazione InMemory?

Aggiunta: Più ci penso, forse la seconda domanda era l'unica che avrebbe dovuto essere posta.

risposta

6

Penso che il modo migliore per rispondere alle vostre domande è di fare un passo indietro e considerare perché la separazione tra livelli di logica aziendale e livelli di accesso ai dati è la pratica consigliata.

Nella mia mente, le ragioni sono semplici: mantenere la logica di business separata dallo strato di dati perché la logica di business è dove il valore è, la logica livello di dati e di business avrà bisogno di cambiare nel tempo più o meno indipendentemente l'uno altro, e e la logica di business deve essere leggibile senza dover avere una conoscenza dettagliata di ciò che fa tutto il livello di accesso ai dati.

Quindi la prova del nove per i vostri ginnastica di query si riduce a questo:

  1. Si può fare una modifica lo schema dei dati nel sistema senza stravolgere una parte significativa della logica di business?
  2. La logica aziendale è leggibile per te e per altri sviluppatori C#?
+1

+1 per il primo punto, in particolare – arootbeer

+2

+1. Scopo in primo luogo, filosofia secondo. –

+0

Il tuo primo punto è la mia preoccupazione. Anche se EF4 è in grado di creare oggetti di dominio sudo con le sue capacità di mappatura, se si apportano modifiche nello schema del database, è molto probabile che si desideri propagare tali modifiche agli oggetti EF4, influenzando così la logica aziendale. Questo impatto può essere minimizzato eseguendo una regressione sui test delle unità. Ma un cambiamento è un cambiamento. –

1

1. Solo se ci si preoccupa di più della filosofia piuttosto che di cose fatte. :)

2. Direi che è una logica di business perché nel frattempo hai un'astrazione. Chiamerei quella parte del repository parte di DAL e tutto ciò che la usa, BL.

Ma sì, questo è sfocato anche per me. Non penso che importi comunque. Il punto di usare schemi come questo è scrivere un codice pulito, utilizzabile che sia facile da comunicare allo stesso tempo, e che l'obiettivo sia raggiunto in entrambi i modi.

1

1.Does importa che non esiste una chiara distinzione tra BLL e DAL ?.

Di sicuro importa! Qualsiasi programmatore che utilizza la proprietà Table deve comprendere le ramificazioni (roundtrip del database, traduzione delle query, rilevamento degli oggetti). Ciò vale anche per i programmatori che leggono le lezioni di logica aziendale.

2. La queryabilità considera l'accesso ai dati o la logica aziendale quando si trova dietro un livello del repository che si comporta come un'astrazione InMemory?

L'astrazione è una coperta che nascondiamo i nostri problemi sotto.

Se l'astrazione è perfetta, le query potrebbero essere considerate in modo astratto come operative rispetto alle raccolte in memoria e quindi non sono accesso ai dati.

Tuttavia, le astrazioni perdono. Se vuoi delle query che hanno senso nel mondo dei dati, ci deve essere uno sforzo per lavorare al di sopra e al di là dell'astrazione. Questo sforzo extra (che sconfigge l'astrazione) produce un codice di accesso ai dati.


Alcuni esempi:

output = work.GetRepository<Bar>().Table.ToArray(); 

Questo è il codice è (astrattamente) fine. Ma nel mondo dei dati risulta nella scansione di un intero tavolo ed è (almeno in generale) stupido!


badquery = work.GetRepository<Customer>().Table.Where(c => c.Name.Contains("Bob")).ToArray(); 
goodquery = work.GetRepository<Customer>().Table.Where(c => c.Name.StartsWith("Bob")).ToArray(); 

Goodquery è meglio di male query quando c'è un indice su Customer.Name. Ma questo fatto non è disponibile per noi se non solleviamo l'astrazione.


badquery = work.GetRepository<Customer>().Table 
    .GroupBy(c => c.Orders.Count()) 
    .Select(g => new 
    { 
    TheCount = g.Key, 
    TheCustomers = g.ToList() 
    }).ToArray(); 
goodquery = work.GetRepository<Customer>().Table 
    .Select(c => new {Customer = c, theCount = c.Orders.Count()) 
    .ToArray() 
    .GroupBy(x => x.theCount) 
    .Select(g => new 
    { 
    TheCount = g.Key, 
    TheCustomers = g.Select(x => x.Customer).ToList() 
    }) 
    .ToArray(); 

goodquery è meglio di male interrogazione dal badquery sarà rieseguire una query del database chiave di gruppo, per ogni gruppo (e peggio, è altamente improbabile che ci sia un indice per aiutare con i clienti di filtraggio da c.Orders.Count()).


Testability è incorporato nel disegno Repository.Possiamo sfruttare l'implementazione InMemory per affermare i risultati con le aspettative.

Non fare illusioni che le tue query siano state testate se effettivamente le esegui contro raccolte in memoria. Quelle query non sono testabili a meno che non sia coinvolto un database.

+0

Dopo averci pensato un po 'di più, sto capendo il tuo punto sull'astrazione perdita. Anche se l'origine di IQueryable è in astratto, qualsiasi query creata sul livello della business logic sta effettivamente creando gli script che vengono eseguiti da EF4. Quindi perdita. Quindi, ecco la mia prossima domanda, quindi dovrei IQueryable con caricamento lazy mai esposto al di fuori del DAL? Presentato in modo diverso, se l'ObjectSet EF4 con una connessione aperta al DB dovesse essere mai estratto oltre il DAL? –

+0

Mai? Se stai operando in questi termini in bianco e nero, allora no. I programmatori che scrivono la logica di business e non capiscono i database non dovrebbero avere accesso a un generatore di query. –