Демонстрируя решение только одного класса будет
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);
}
}
, а затем ваше использование может быть
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();
}
}
Я намеренно не включил Repository класс. Для того, чтобы класс инкапсулировал как запросы, так и команды для каждого объекта, является более убитым и ненужной абстракцией. Его почти дефект дизайна на фундаментальном уровне. Запросы и команды принципиально разные. Запросы самым простым способом могут быть созданы как методы расширений на интерфейсе ISession
. Команды можно сделать с помощью нескольких классов, как например ..
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);
}
}
The IStore
выше такой же, как Session
класса, за исключением того, что он не реализует интерфейс IDisposable
и не имеет метода Commit()
. ISession
тогда, очевидно, наследуется от IStore
, а также реализует IDisposable
и имеет один метод Commit()
. Это гарантирует, что ICommand<IStore>
никогда не сможет открывать или удалять соединения и не может совершать транзакции. Его обязанность заключается в определении команды и определении ее применения. Кто его применяет и что происходит, а что не в командном приложении, - это другая ответственность, которая связана с ICommandHandler<IStore>
.
Итак, вы хотите использовать единицу рабочего шаблона, но не хотите добавлять репозитории в единицу работы? Хммм ... не думаю. Но если проблема заключается в количестве изменений, которые необходимо внести для каждого нового объекта в систему, посмотрите [эту статью] (http://www.cuttingedge.it/blogs/steven/pivot/entry.php? ID = 84). – Steven
@Steven, так что вы говорите, что нормально создавать репозиторий для каждого нового объекта? –
Вот почему я против общих хранилищ, они обслуживают базу данных вместо приложения. Если вам нужно сохранить продукт, у вас есть репозиторий продуктов с помощью метода «Сохранить (продукт p)», и пусть репо обработает его оттуда. ProductCategories, UoW, что бизнес репо. Приложение знает только об абстракции репо, а не EF, nhibernate, таблицах и других подробностях о сохранении. – MikeSW