Démontrant une solution avec une seule classe serait
public class Session : ISession
{
private readonly DbContext _dbContext;
public Session(DbContext dbContext)
{
_dbContext = dbContext;
}
public TEntity Single<TEntity>(Expression<Func<TEntity, bool>> expression) where TEntity : class
{
return _dbContext.Set<TEntity>().SingleOrDefault(expression);
}
public IQueryable<TEntity> Query<TEntity>() where TEntity : class
{
return _dbContext.Set<TEntity>().AsQueryable();
}
public void Commit()
{
try { _dbContext.SaveChanges(); }
catch (DbEntityValidationException ex)
{
var m = ex.ToFriendlyMessage();
throw new DbEntityValidationException(m);
}
}
public void Dispose()
{
_dbContext.Dispose();
}
public void Add<TEntity>(IEnumerable<TEntity> items) where TEntity : class
{
items.ToList().ForEach(Add);
}
public void Add<TEntity>(TEntity item) where TEntity : class
{
_dbContext.Set<TEntity>().Add(item);
}
public void Remove<TEntity>(TEntity item) where TEntity : class
{
_dbContext.Set<TEntity>().Remove(item);
}
public void Remove<TEntity>(Expression<Func<TEntity, bool>> expression) where TEntity : class
{
var items = Query<TEntity>().Where(expression);
Remove<TEntity>(items);
}
public void Remove<TEntity>(IEnumerable<TEntity> items) where TEntity : class
{
items.ToList().ForEach(Remove);
}
}
et votre utilisation peut être
public class User
{
public int? Id { get; set; }
public string Name { get; set; }
public DateTime Dob { get; set; }
}
public class Usage
{
private readonly ISession _session;
public Usage(ISession session) { _session = session; }
public void Create(User user)
{
_session.Add(user);
_session.Commit();
}
public void Update(User user)
{
var existing = _session.Single<User>(x => x.Id == user.Id);
// this gets cumbursome for an entity with many properties.
// I would use some thing like valueinjecter (nuget package)
// to inject the existing customer values into the one retreived from the Db.
existing.Name = user.Name;
existing.Dob = user.Dob;
_session.Commit();
}
}
J'ai délibérément inclus un dépôt classe. Avoir une classe encapsulant à la fois des requêtes et des commandes pour chaque entité est une over-kill et une abstraction inutile. C'est presque un défaut de conception à un niveau fondamental. Les requêtes et les commandes sont des préoccupations fondamentalement différentes. Les requêtes de la manière la plus simple peuvent être créées en tant que méthodes d'extensions sur l'interface ISession
. Les commandes peuvent être effectuées en utilisant quelques classes comme tels ..
public interface ICommand<in TSource>
{
void ApplyTo(TSource source);
}
public interface ICommandHandler<out TSource>
{
void Handle(ICommand<TSource> command);
}
public class LinqCommandHandler : ICommandHandler<IStore>
{
private readonly ISession _session;
public LinqCommandHandler(ISession session)
{
_session = session;
}
public void Handle(ICommand<IStore> command)
{
command.ApplyTo(_session);
_session.Commit();
}
}
public class UpdateDobForUserName : ICommand<IStore>
{
public string UserName { get; set; }
public DateTime Dob { get; set; }
public void OnSend(IStore store)
{
var existing = store.Query<User>().SingleOrDefault(x => x.Name == UserName);
existing.Dob = Dob;
}
}
public class Usage
{
private readonly ICommandHandler<IStore> _commandHandler;
public Usage(ICommandHandler<IStore> commandHandler)
{
_commandHandler = commandHandler;
}
public void Update()
{
var command = new UpdateDobForUserName {UserName = "mary", Dob = new DateTime(1960, 10, 2)};
_commandHandler.Handle(command);
}
}
Le IStore
ci-dessus est la même que la classe Session
, sauf qu'il ne met pas en œuvre l'interface IDisposable
et ne dispose pas d'une méthode Commit()
. Le ISession
hérite alors évidemment d'un IStore
et implémente également IDisposable
et a une méthode Commit()
. Cela garantit un ICommand<IStore>
ne peut jamais ouvrir ou disposer des connexions et ne peut pas valider. Sa responsabilité est de définir une commande et de définir comment elle est appliquée. Qui l'applique et ce qui se passe et ce qui ne se passe pas sur l'application de commande est une responsabilité différente qui est avec le ICommandHandler<IStore>
.
Vous souhaitez donc utiliser l'unité de motif de travail, mais vous ne souhaitez pas ajouter les référentiels à l'unité de travail? Hmmm ... ne le pense pas. Mais si le problème est le nombre de changements que vous devez apporter à chaque nouvelle entité du système, jetez un oeil à [cet article] (http://www.cuttingedge.it/blogs/steven/pivot/entry.php? id = 84). – Steven
@Steven donc vous dites que c'est ok pour créer un référentiel pour chaque nouvelle entité? –
C'est pourquoi je suis contre les dépôts génériques, ils servent la base de données au lieu de l'application. Si vous avez besoin d'enregistrer un produit, vous disposez d'un référentiel de produit avec une méthode 'Enregistrer (produit p)' et laissez le service repo gérer à partir de là. ProductCategories, montre que l'activité du repo. L'application ne connaît que l'abstraction de la pension, pas EF, nhibernate, les tables et autres détails de persistance. – MikeSW