In una semplice applicazione puoi usare una singola classe come entità EF, modello di dominio e modello di visualizzazione.
Nelle applicazioni più complesse tutti questi modelli sono diventati classi diverse. Ad esempio, le prime entità del codice e i modelli MVC di ASP.NET hanno annotazioni diverse ( KeyAttribute
in EF, DisplayFormatAttribute
in MVC) e in genere non devi mischiarle in una singola classe.
Inoltre, tutte le proprietà EF dovrebbero essere scrivibili, ma le proprietà del dominio possono essere di sola lettura. I modelli MVC possono contenere specifici SelectList
, ma i modelli e le entità di dominio non dovrebbero.
Quindi, nell'applicazione complessa, hai modelli separati a livello di ciascuna applicazione (presentazione, dominio, accesso ai dati) e dovresti mappare questi modelli ai limiti del tier.
Guarda il piccolo esempio di un blog. Ogni post in un blog ha data / ora di creazione e (immagine) una categoria:
FE
[TableName("PostCategories")]
public class EfCategory
{
public int CategoryId { get; set; }
public string Name { get; set; }
}
public class EfPost
{
public DateTime Created { get; set; }
public virtual Category Category { get; set; }
public int CategoryId { get; set; }
. . .
}
Come vedi, Post.Created
è una proprietà scrivibile e leggibile e Category
ha un attributo TableName
. Puoi usare queste classi nel livello dominio?
No. Innanzitutto, la proprietà data / ora di creazione è di sola lettura nel dominio, non è possibile modificarla. In secondo luogo, cosa succede se si desidera riscrivere l'accesso ai dati con NHibernate o ADO.NET? Non puoi causare che il tuo modello di dominio Category
abbia l'attributo TableName
.
Quindi, puoi creare modelli di dominio duplicati:
Dominio
public class DomainCategory
{
public int Id { get; private set; }
public string Name { get; private set; }
public DomainCategory(int id, string name)
{
Id = id;
Name = name;
}
}
. . .
public EfCategoryRepository : ICategoryRepository
{
private readonly DbContextFactory dbContextFactory;
. . .
public CategoryRepository(DbContextFactory dbContextFactory)
{
this.dbContextFactory = dbContextFactory;
}
. . .
public DomainCategory GetById(int id)
{
using (var dbContext = dbContextFactory.CreateReadOnlyContext())
{
var entity = dbContext.Set<EfCategory>.Single(c => c.Id == id);
return new DomainCategory(entity.CategoryId, entity.Name);
}
}
}
Ora l'interfaccia del dominio ICategoryRepository
che "conosce" i modelli di dominio, è implementata dalla classe di accesso ai dati EF EfCategoryRepository
. Questa implementazione "conosce" i modelli EF e può mappare EF al dominio.
Dominio (continua)
public class DomainPost
{
public DomainCategory { get; set; }
public DateTime Created { get; private set; }
. . .
public DomainPost(EfCategory category, DateTime created, ...)
{
Category = new DomainCategory(category.CategoryId, category.Name);
Created = created;
. . .
}
}
La proprietà del dominio Created
è a differenza della proprietà EF di sola lettura.
Infine, se pensiamo al modello di modifica della vista (ASP.NET MVC), scopriremo che questo modello dovrebbe contenere l'elenco delle categorie e non dovrebbe contenere Created
perché l'utente non può cambiarlo:
Presentazione
public MvcEditPost
{
public int Id { get; set; }
[Display("Category")]
public int CategoryId { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
}
public ActionResult Edit(int id)
{
var domainPost = postRepository.GetById(id);
var modelPost = new MvcEditPost
{
Id = domainPost.Id,
CategoryId = domainPost.Category.Id,
Categories = categoryRepository.GetAll()
.Select(c => new SelectListItem
{
Selected = c.Id == domainPost.Category.Id,
Text = c.Name,
Value = c.Id.ToString(),
}),
};
return View(modelPost);
}
@model MvsEditPost
@using (Html.BeginForm("Post", "Edit", { Id = Model.Id }))
{
@Html.DropDownListFor(m => m.CategoryId, Model.Categories)
}
Quindi, il codice di presentazione è anche "noto" sia per i modelli di presentazione che per i modelli di dominio e può mappare i primi in secondi. Questa differenziazione del modello ti consente di rendere tutti i livelli indipendenti. Più precisamente, un livello di presentazione e un livello di accesso ai dati dipendono ora da un livello di dominio. In questo modo è possibile riscrivere semplicemente l'accesso ai dati (da SQL Server a Oracle, da EF a NHibernate, da Single data-server a cloud), oppure è possibile eseguire una semplice riscrittura delle presentazioni (da MVC a desktop, da MVC a console). Non saresti in grado di farlo senza la differenziazione dei modelli.