编辑
如果将 10 位领域专家放在一个房间里会发生什么?是的,您有 11 条意见。 (其中 10 个被声明为反模式)
谢谢大家的详细解答。我将研究它们并考虑它们如何帮助我解决特定问题。
当与 ORM 和依赖注入(inject)一起使用时,我很难理解存储库和工作单元。考虑以下非常标准的接口(interface):
public interface IRepository<TAggregateRoot> : ITransientDependency
{
void Add(TAggregateRoot aggregateRoot);
void Delete(TAggregateRoot aggregateRoot);
IEnumerable<TAggregateRoot> GetAll();
}
public interface IUnitOfWork : ITransientDependency
{
void Commit();
void Rollback();
}
我脑子里有一些场景,我想用这些方法来涵盖。
- 将单个实体插入存储库
- 删除聚合,其中链接的实体也应删除
- 在 2 个以上存储库上进行事务
使用 NHibernate 的默认实现可能如下所示:
public abstract class NHibernateRepository<TAggregateRoot> : IRepository<TAggregate>
{
protected NHibernateRepository(ISession session) {}
}
public sealed NHibernateUnitOfWork : IUnitOfWork
{
public NHibernateUnitOfWork(ISession session)
{}
public void Commit() {
_session.Flush();
}
}
1.场景:将单个实体插入存储库
// ASP.NET MVC controller, but valid for any
// other arbitary application service
public class MyController : Controller {
private readonly IPeopleRepository _repository;
// di -> declaring IPeopleRepository dependency
public MyController(IPeopleRepository repository) {
_repository = repository;
}
public void AddPerson(Person person) {
_repository.Add(person);
}
}
现在,我将人员添加到存储库后会发生什么?对了,没什么。即使单个插入不完全是一个工作单元(事务、技术),像 EF 和 NHibernate 这样的 ORM 框架仍然需要将更改提交到数据库,因为他们奇特的 session 和DBContexts在技术上是工作单元和存储库。
如何克服第一个问题?为我所做的一切开始一个工作单元?
2.场景:删除聚合,其中链接的实体也应删除
查看以下聚合:
public class Person : IAggregateRoot {
private readonly List<Cat> _cats = new List<Cat>();
public IEnumerable<Cat> Cats {
get { return _cats; }
}
public void AddCat(Cat cat) {
//
}
让我们通过存储库使用其根删除聚合:
IPersonRepository.Remove(person);
现在,从技术上讲,Person 聚合到的所有实体都已被删除。由于代码中不再引用它们,垃圾收集器充当数据库管理器并从内存中删除了猫。
但是这在 ORM 存储库实现中看起来如何? 工作单元在哪里发挥作用?
3.场景:在超过 2 个存储库上进行事务
好的,我得到了我喜欢的 SomethingService。他必须在多个存储库上做一些事情,因此显然需要一个需要工作单元的事务。
public SomethingService : ISomethingService {
public ISomethingService(IFirstRepo repo1, ISecondRepo repo2, IUnitOfWork uow)
{
...
}
public void DoSomething() {
repo1.AddThis();
repo2.GetThisOne();
repo2.BecauseOfTheOneAboveDeleteThis();
uow.Commit();
}
}
对我来说看起来很好,但是考虑到上面的 NHibernate 存储库和 工作单元 实现,这不起作用,仅仅因为每个(工作单元, 2 个存储库)有不同的 NHibernate session 实例!
我考虑使用拦截器进行面向方面的编程,但这只能部分起作用,因为IoC拦截服务方法时,存储库已经使用自己的 session 创建,因此无法共享工作单元的 session 。
如何克服这个问题?是否有任何成熟的工作示例可以在没有任何肮脏黑客的情况下运行? (例如单例工作单元)
只是想说:是的,我想将存储库与 ORM 一起使用。它们是一种很好的方式来抽象框架,让我按照我(或我的客户)想要的方式设计我的域,而不是像框架想要的那样。
非常感谢您阅读这面文字墙。
最佳答案
<强>1。工作单元处理可以采取一些不同的方式,
根据您的建议, session 被注入(inject)到存储库中,我不会走这条路,而是使用 sessionFactory
public interface IUnitOfWork : IDisposable
{
ISession CurrentSession { get; }
void Commit();
void Rollback();
}
public class NHibernateUnitOfWork : IUnitOfWork
{
private readonly ISessionFactory _sessionFactory;
[ThreadStatic]
private ISession _session;
[ThreadStatic]
private ITransaction _transaction;
public NHibernateUnitOfWork(ISessionFactory sessionFactory)
{
_sessionFactory = sessionFactory;
_session = _sessionFactory.OpenSession();
_transaction = _session.BeginTransaction();
}
public static ISession CurrentSession { get { return _session; } }
public void Dispose()
{
_transaction = null;
_session.Close();
_session = null;
}
public void Commit()
{
_transaction.Commit();
}
public void Rollback()
{
if (_transaction.IsActive) _transaction.Rollback();
}
}
public class Repository : IRepository
{
public void Add(IObj obj)
{
if (NHibernateUnitOfWork.CurrentSession == null)
throw new Exception("No unit of work present");
NHibernateUnitOfWork.CurrentSession.Save(obj);
}
}
// ASP.NET MVC controller, but valid for any
// other arbitary application service
public class MyController : Controller
{
private readonly IPeopleRepository _repository;
// di -> declaring IPeopleRepository dependency
public MyController(IPeopleRepository repository) {
_repository = repository;
}
public void AddPerson(Person person)
{
using (IUnitOfWork uow = new NHibernateUnitOfWork())
{
try
{
_repository.Add(person);
uow.Commit();
}
catch(Exception ex)
{
uow.RollBack();
}
}
}
}
虽然这是处理该问题的一种方法,但还有更聪明的方法,一种是使用 ActionFilter,它在操作之前启动事务并在全部成功时提交,或者您可以使用 HttpModule 来处理交易处理..
或者
您可以选择完全不同的路径并实现命令模式,其中每个操作都是一个命令,无论它有多复杂,并且处理程序应该启动并提交事务,看看 https://fnhmvc.codeplex.com/
<强>2。如果使用正确的映射并使用正确的UOW来删除父实体子实体将被自动删除
<强>3。如果使用上述工作单元模式,这将不是问题
公共(public)SomethingService:ISomethingService { 公共(public) ISomethingService(IFirstRepo repo1, ISecondRepo repo2, IUnitOfWork uow) { ... }
public void DoSomething()
{
using (IUnitOfWork uow = new NHibernateUnitOfWork())
{
try
{
repo1.AddThis();
repo2.GetThisOne();
repo2.BecauseOfTheOneAboveDeleteThis();
uow.Commit();
}
catch(Exception ex)
{
uow.RollBack();
}
}
}
关于c# - DDD : Repository, 工作单元、ORM 和依赖项注入(inject),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25648676/