Durante il tentativo di creare un livello di accesso ai dati per un nuovo progetto, mi sono imbattuto in ciò che posso solo immaginare come un problema OOP/Design/Generics (utilizzando EF 4.3 per accedere al database).Come evitare la necessità di fare riferimento a Entity Framework sul mio livello di servizio?
Principalmente ho voluto realizzare due cose con questo livello di dati:
- Diversi oggetti di contesto che ho sul mio progetto dovrebbe condividere la stessa stringa di connessione.
- Classe di repository astratta con implementazione comune.
Per qualche motivo, non riesco a compilare la mia soluzione senza fare riferimento a EntityFramework sul livello di servizio. Quello che sto cercando è un modo per risolvere questo problema. Ecco cosa ho:
//Project/Namespace BusinessLogicLayer.DomainClasses
//POCO classes mapped on Entity Framework.
//Project/Namespace DataAccessLayer.Base
//Base classes and interfaces for all data access layer, such as:
public abstract class BaseContext<TContext> : DbContext where TContext : DbContext
{
//To allow multiple contexts sharing the same connection string
protected BaseContext(): base("name=MyConnectionString") {}
}
//Generic interface for a read-only repository
public interface IReadOnlyRepository<T> : IDisposable where T : class
//Generic interface for a read/write repository
public interface IRepository<T> : IReadOnlyRepository<T> where T : class
//Basic implementation for a read-only repository
public abstract class BaseReadOnlyRepository<C, T> : IReadOnlyRepository<T>
where T : class
where C : BaseContext<C>, new()
{
}
//Basic implementation for a read/write repository
public abstract class BaseRepository<C, T> : IRepository<T>
where T : class
where C : BaseContext<C>, new()
{
}
//Project DataAccessLayer.AccountContext/ Namespace DataAccessLayer
//Context class:
public class AccountContext : BaseContext<AccountContext> {}
//With this, I can have simple repositories:
public class UserRepository : BaseRepository<AccountContext, User>
{ //All implementation comes from the base abstract class, unless I need to change it (override methods)
}
Ho un livello di servizio tra l'accesso ai dati e l'applicazione (Windows Form). Poiché ho un repository generico, mi è sembrata un'idea logica e buona avere servizi generici. Alla fine, molto simile alla struttura repository:
//Project/Namespace BusinessLogicLayer.Services
//Service layer supposed to reference only the repository project and not Entity Framework.
//Generic interface for a read-only service working with a read-only repository
public interface IReadOnlyService<T> where T : class {}
//Generic interface for a read/write service working with a read/write repository
public interface IService<T> : IReadOnlyService<T> where T : class
//Base implementation for a read-only service
public abstract class BaseReadOnlyService<T, R> : IReadOnlyService<T>
where T : class
where R : IReadOnlyRepository<T>, new()
{
}
//Base implementation for a read/write service
public abstract class BaseService<T, R> : IService<T>
where T : class
where R : IRepository<T>, new()
{
}
//Concrete sample service
public class UserService : BaseService<User, UserRepository>
{ //As with the repository I can change the default behavior of implementation overriding methods
}
Con questa configurazione, l'unico modo per compilare è quello di riferimento Entity Framework sul progetto livello di servizio. Come evitare la necessità di fare riferimento a Entity Framework lì?
A questo punto, sono disposto a buttare tutto fuori e ricostruire tutto ma questo è l'unico modo in cui ho trovato per farlo funzionare date le mie esigenze (DbContext condivisione stringhe di connessione, repository generico per evitare la replica del codice) .
Apprezzare qualsiasi aiuto. Grazie.
--edit - anche qui qualche passo in più che ho fatto 3 ore dopo che ho postato l'interrogo
Per capire questo, ho iniziato a creare un progetto di esempio con lo stesso codice di cui sopra oltre a qualche implementazione per simulare il più possibile i risultati del progetto originale.
Ho creato il progetto delle classi di dominio, l'intero progetto del livello dati di base e quindi il progetto di contesto. Ho notato che ho bisogno di fare riferimento a Entity Framework sul progetto di contesto anche se la classe di contesto non deriva direttamente da DbContext. Invece, deriva da una classe astratta che deriva da DbContext. Questo è ok anche se il mio contesto avrà DbSets e qualsiasi altra implementazione relativa a DbContext.
Il prossimo è il progetto di repository. È necessario fare riferimento a tutti gli altri tre (dominio, livello dei dati di base e contesto). Il mio repository non ha codice. Tutta la funzionalità si trova sull'antenato. Provo a compilare il progetto di repository e VS mi impone di fare riferimento a Entity Framework. Mi chiedo se sia davvero solo questione di incorporare le librerie. Se questo è confermato, sarà una sorpresa. La libreria Entity Framework è presente sull'output degli altri progetti. Perché dovrei fare riferimento anche qui? Cosa sta facendo VS richiede questo?
Ad ogni modo, a scopo di test, ho aggiunto il riferimento. Dopotutto, sono all'interno del livello dati. Posso vivere con quello. Passare al livello di servizio. Per semplicità, ho inserito tutte le classi di servizio nello stesso progetto.
Una possibile pecca è che uno dei vincoli per le classi di servizi astratti è l'interfaccia di repository.Ciò mi richiede di aggiungere un riferimento al livello dati di base sul mio livello di servizio. Forse già qui c'è qualcosa che posso fare che mi permette di usare solo il riferimento del repository. Non ho altra scelta che fare riferimento al livello dati di base.
Infine, il mio servizio concreto viene creato e VS mi dà il seguente messaggio di errore: Il tipo 'System.Data.Entity.DbContext' è definito in un assembly a cui non viene fatto riferimento. È necessario aggiungere un riferimento all'assembly 'EntityFramework, Versione = 4.3.1.0, Culture = neutral, PublicKeyToken = b77a5c561934e089'.
Quindi, alla fine, l'unico modo per andare avanti è fare riferimento a Entity Framework sul livello di servizio. E a un certo punto, quando costruisci l'app Windows Form, dovrò anche fare riferimento a Entity Framework.
Cosa devo fare per evitare di avere questi riferimenti? Quali miglioramenti posso avere su questa struttura?
Quello che so è che la mia app certamente non deve sapere che Entity Framework è coinvolto ovunque negli altri livelli. Neanche lo strato di servizio. I servizi consumeranno solo repository. I repository possono anche fornire dati falsi per i test.
Nel caso qualcuno fosse interessato, ho caricato il progetto che ho creato mentre scrivevo questo. È un file zip da 1,17 Mb senza binari (eccetto la DLL Entity Framework 4.3.1 che ho ottenuto tramite Nuget). Link: http://www.mediafire.com/?b45zkedy2j7eocc.
Ancora grazie per l'aiuto.
Sembra una domanda stupida, ma sento la necessità di essere sicuro: il livello di accesso ai dati è compilato con il livello di servizio così com'è adesso? – tmesser
Humm ... Non è affatto stupido. In effetti penso che tu mi abbia messo nella giusta direzione. Dopo aver letto il tuo commento ho creato un progetto di esempio che dimostra il problema. Ho messo i risultati dopo la domanda originale sopra. Per quanto riguarda la tua domanda, nessun livello di accesso ai dati viene compilato in dll differenti. –