c# - DDD : Repository, 工作单元、ORM 和依赖项注入(inject)

标签 c# entity-framework nhibernate dependency-injection domain-driven-design

编辑

如果将 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/

相关文章:

sql - 强制 LINQ to SQL 或 ENTITIES 发出 BETWEEN 运算符

Linq-to-NHibernate OrderBy 不工​​作

.net - 将 Quartz .NET 与 NHibernate 集成

c# - 将 List<T> 转换为 List<object>

c# - 流利的断言 : Approximately compare the properties of objects stored in Lists

c# - 如何获取实现给定接口(interface)的所有加载类型的所有实例?

c# - 具有离线存储的 Entity Framework

c# - 计算有多少项目出现在具有特定 ID 的项目之前

c# - 转换数值类型 : Which style to use?

sql - NHibernate 是否支持从 SQL View 映射?