Hai colpito il chiodo sulla testa identificando la difficoltà con l'utilizzo di Entità come oggetti di business. Dopo molte prove ed errori, ecco il modello in cui ci siamo sistemati, che ha funzionato molto bene per noi:
La nostra applicazione è divisa in moduli, e ogni modulo è diviso in tre livelli: Web (front-end), Core (attività) e dati. Nel nostro caso, a ciascuno di questi livelli viene dato un proprio progetto, quindi c'è una dura applicazione che impedisce alle nostre dipendenze di diventare strettamente accoppiate.
Il livello Core contiene classi di utilità, POCO e interfacce repository.
Il livello Web utilizza queste classi e interfacce per ottenere le informazioni necessarie. Ad esempio, un controller MVC può assumere una particolare interfaccia di repository come argomento del costruttore, quindi il nostro framework IoC inietta l'implementazione corretta di quel repository quando viene creato il controller. L'interfaccia del repository definisce i metodi selettori che restituiscono i nostri oggetti POCO (definiti anche nel livello aziendale Core).
Dati L'intera responsabilità del livello è quella di implementare le interfacce del repository definite nel livello Core. Ha un contesto Entity Framework che rappresenta il nostro archivio dati, ma piuttosto che restituire le Entità (che sono tecnicamente oggetti "dati"), restituisce i POCO definiti nel livello Core (i nostri oggetti "business").
Per ridurre la ripetizione, abbiamo una classe astratta, generica EntityMapper
, che fornisce funzionalità di base per la mappatura di Entità su POCO. Ciò rende la maggior parte delle implementazioni del nostro repository estremamente semplice. Per esempio:
public class EditLayoutChannelEntMapper : EntityMapper<Entity.LayoutChannel, EditLayoutChannel>,
IEditLayoutChannelRepository
{
protected override System.Linq.Expressions.Expression<Func<Entity.LayoutChannel, EditLayoutChannel>> Selector
{
get
{
return lc => new EditLayoutChannel
{
LayoutChannelId = lc.LayoutChannelId,
LayoutDisplayColumnId = lc.LayoutDisplayColId,
ChannelKey = lc.PortalChannelKey,
SortOrder = lc.Priority
};
}
}
public EditLayoutChannel GetById(int layoutChannelId)
{
return SelectSingle(c => c.LayoutChannelId == layoutChannelId);
}
}
Grazie ai metodi implementati dalla classe base EntityMapper, il repository sopra implementa la seguente interfaccia:
public interface IEditLayoutChannelRepository
{
EditLayoutChannel GetById(int layoutChannelId);
void Update(EditLayoutChannel editLayoutChannel);
int Insert(EditLayoutChannel editLayoutChannel);
void Delete(EditLayoutChannel layoutChannel);
}
EntityMappers fare molto poco in loro costruttori, quindi è bene se un controller ha più dipendenze del repository. Entity Framework non solo riutilizza le connessioni, ma i contesti di entità vengono creati solo quando viene richiamato uno dei metodi del repository.
Ogni modulo ha anche un progetto speciale Test, che contiene test di unità per le classi in questi tre livelli. Abbiamo persino escogitato un modo per rendere i nostri repository e altre classi di accesso ai dati in qualche modo testabili da unità. Ora che abbiamo configurato questa infrastruttura di base, l'aggiunta di funzionalità alla nostra applicazione Web è in genere piuttosto semplice e non troppo soggetta a errori.
Ho letto quel libro qualche tempo fa, andando a dare un'altra occhiata ai suoi esempi – Fabian
Questo secondo link mi ha guidato maggiormente su ciò che ho finalmente implementato. – Fabian