Voglio creare un livello di astrazione tra Entity Framework e il resto della mia applicazione. Ma ho qualche problema con Entity Framework.
Fondamentalmente (non ti mostro tutti i livelli dell'interfaccia che ho creato anch'io), ho diviso la mia applicazione in diversi progetti come questo:
Come ho detto, voglio creare un'astrazione dei miei oggetti datastorage (nel mio caso oggetto Database
, ma voglio una soluzione che funzioni anche per lo storage di file o WCF ad esempio) in modo che il mio livello aziendale non sappia nulla sul mio DAL implementazione.
Ecco un assaggio di ciò che ho fatto nel mio DAL:
public abstract class GenericDao<TEntity, TDomain, TDbContext> : IGenericDao<TDomain>
where TDbContext : DbContext, new()
where TEntity : class
where TDomain : class
{
protected TDbContext _context;
protected DbSet<TEntity> _dbSet;
public GenericDao(TDbContext dbContext)
{
this._context = dbContext;
this._dbSet = dbContext.Set<TEntity>();
}
public TDomain Create()
{
return this.ToDomain(this._dbSet.Create());
}
public IList<TDomain> GetAll()
{
return this._dbSet.ToList().Select(entity => this.ToDomain(entity)).ToList();
}
public void Update(TDomain domain)
{
var entity = this.ToEntity(domain);
var entry = this._context.Entry(entity);
entry.State = EntityState.Modified;
}
public void Remove(TDomain domain)
{
_dbSet.Remove(this.ToEntity(domain));
}
protected abstract TDomain ToDomain(TEntity entity);
protected abstract TEntity ToEntity(TDomain domain);
}
Probabilmente vedrai cosa c'è di sbagliato nel mio codice leggendolo: quando provo a eliminare o aggiornare un'entità, non sto manipolando un'entità collegata a Entity Framework. Se provo ad associare la mia entità al dbContext
, fallisce perché c'è già un'entità nel contesto con lo stesso id.
Ho già pensato a diverse soluzioni, ma nessuna di loro mi soddisfa.
Forse sto facendo qualcosa di sbagliato nel mio approccio? Sono un po 'confuso riguardo al repository e al pattern DAO (leggo qualcosa e l'esatto contrario di quella differenza su internet).
Hai due opzioni:
inizializza il nuovo dbcontext
per ogni operazione e dbcontext
al termine dell'operazione:
public abstract class GenericDao<TEntity, TDomain, TDbContext> : IGenericDao<TDomain>
where TDbContext : DbContext, new()
where TEntity : class
where TDomain : class
{
protected Func<TDbContext> _contextFactory;
public GenericDao(Func<TDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
public TDomain Create()
{
using(var context = _contextFactory())
{
return context.Set<TEntity>().Create();
}
}
public IList<TDomain> GetAll()
{
using(var context = _contextFactory())
{
return context.Set<TEntity>().ToList()
.Select(entity => this.ToDomain(entity)).ToList();
}
}
public void Update(TDomain domain)
{
using(var context = _contextFactory())
{
var entity = this.ToEntity(domain);
context.Attach(entity);
var entry = this._context.Entry(entity);
entry.State = EntityState.Modified;
context.SaveChanges();
}
}
public void Remove(TDomain domain)
{
using(var context = _contextFactory())
{
var entity = this.ToEntity(domain);
context.Attach(entity);
context.Set<TEntity>.Remove(entity);
context.SaveChanges();
}
}
protected abstract TDomain ToDomain(TEntity entity);
protected abstract TEntity ToEntity(TDomain domain);
}
oppure puoi provare a trovare un'entità nella tua istanza di dbcontext
usando la proprietà Local
di DbSet
:
var contextEntity = context.Set<TEntity>().Local
.Where(c=>c.Id == entity.Id).FirstOrDefault();
Sembra che tu stia bloccando il codice per un'implementazione all'interno della tua astrazione. Se hai iniettato un'interfaccia al tuo tipo generico piuttosto che concreto (come EF), il tuo GenericDao diventa molto più flessibile. È possibile iniettare qualsiasi implementazione che si sceglie fornendo implementa l'interfaccia richiesta. Nel tuo caso, WCF, File, Qualunque. Per esempio;
protected IDbContext _context;
public GenericDao(IDbContext dbContext)
{
this._context = dbContext;
}
public void Remove(TDomain domain)
{
_context.Remove(this.ToEntity(domain));
}
//abstraction
public interface IDbContext
{
void Remove(Entity entity);
}
//EF Implementation
public MyEfClass : IDbContext
{
public void Remove(Entity entity)
{
//code to remove for EF example
context.Attach(entity);
context.State = EntityState.Modified;
context.Set<TEntity>.Remove(entity);
context.SaveChanges();
}
}
//WCF Implementation
public MyWCFClass : IDbContext
{
public void Remove(Entity entity)
{
//Wcf implementation here
}
}
//File example
public FileWriter : IDbContext
{
public void Remove(Entity entity)
{
LoadFile();
FindEntry(entity);
WriteFile(entity);
SaveFile();
}
public void LoadFile()
{
//use app settings for file directory
}
}