首页 文章

如何在不引入依赖关系的情况下使用Onion Architecture实现UnitOfWork?

提问于
浏览
2

我正在设置一个asp.Net Mvc 4应用程序,并希望使用Onion Architecture模式进行配置 . 在过去,我使用了像这样的工作单元模式

public class UnitOfWork : IUnitOfWork, IDisposable
{

private IRepository<CallModel> _callRepo;
private IRepository<UserModel> _userRepo;

    public IRepository<CallModel> CallRepo
    {
        get
        {
            if (_callRepo == null)
            {
                _callRepo = new Repository<CallModel>();
            }
            return _callRepo;
        }
    }

    public IRepository<UserModel> UserRepo
    {
        get
        {
            if (_userRepo == null)
            {
                _userRepo = new Repository<UserModel>();
            }
            return _userRepo;
        }
    }
}

然后我会将UnitOfWork类的实例传递给Controller来做这样简单的CRUD .

public class QuestionsController : Controller
{
    private IUnitOfWork _unitOfWork;

    [Inject]
    public QuestionsController(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

我已将应用程序分成三个项目 .

  • 核心

  • 基础设施

  • 网站

我的所有接口都在Core项目中,并在Infrastructure项目中实现了IRepository接口 . 如果我将UnitOfWork类放在Core项目中,那么因为它在Infrastructure项目中需要一个新的Repository,我正在创建一个从Core到Infrastructure的依赖项 . 如果我将它包含在基础设施中,那么Web项目(具有控制器)将依赖于基础设施,整个解决方案最终看起来不像洋葱,更像是意大利面 .

4 回答

  • 0

    我在Core项目中拥有所有接口,并在Infrastructure项目中实现了IRepository接口 . 如果我将UnitOfWork类放在Core项目中,那么因为它在Infrastructure项目中需要一个新的Repository,我正在创建一个从Core到Infrastructure的依赖项 .

    嗯,不是真的 . 您的工作单元应该依赖于IRepository,而不是Repository实现本身 . 如果您正在使用依赖注入,这不应该成为问题,因为它应该找到正确的类型并在运行时提供它 . 我不确定Onion架构是否可以在不使用DI的情况下实现 .

    请参阅david.s的答案,因为这正是我设置的方式 - 有一个项目的唯一目的是连接依赖项 .

  • 1

    我所做的是另一个名为 DependencyResolution 的项目,该项目引用了 CoreInfrastructure ,我在其中配置了IoC容器 . 然后我只能从 Web 项目中反驳 DependencyResolution .

  • 0

    我会喜欢david.s创建项目名为DependencyResolution,但让它重新启动Web,核心和基础设施 .

    在那个项目中你可以做到:

    [assembly: PreApplicationStartMethod(typeof(Start), "Register")]
    
    namespace DependencyResolution
    {
        public static class Start
        {
            public static void Register()
            {
                UnityConfig.Register();
            }
        }
    }
    

    并注册DI .

    namespace DependencyResolution
    {    
        public static class UnityConfig
        {
            public static void Register()
            {
                DependencyResolver.SetResolver(new UnityDependencyResolver());
            }
        }
    }
    

    因此,不需要Web和基础架构之间的依赖 .

    最好的祝福

  • 0

    对于它仍然值得的东西,我已经实现了我自己的库,它应用了UnitOfWork模式,与我之前在任何代码示例中看到的有点不同,但我发现它在实践中运行良好 . 简而言之:我有点复制.NET事务的工作方式,创建一个范围,然后在必要时在环境单元工作(-manager)中登记资源 . 基本上发生的是当处理新的消息/请求时,执行以下代码:

    public void Handle<TMessage>(TMessage message)
    {
        using (var scope = CreateMessageProcessorContextScope())
        {
            HandleMessage(message);
            scope.Complete();
        }
    }
    

    现在和事务一样,只要Thread仍然在范围内,就会出现一个环境UnitOfWork控制器,其中在请求期间使用和更改的所有资源都可以动态地登记 . 他们通过实现具有两种方法的IUnitOfWork接口来实现此目的:

    public interface IUnitOfWork
    {
        bool RequiresFlush();
    
        void Flush();
    }
    

    然后,实现此接口的实例可以按如下方式登记:

    MessageProcessorContext.Current.Enlist(this);
    

    通常,Repository-class将实现此接口,并且当它检测到它的托管聚合被更改/添加/删除时,它可以自我登记(双重登记被忽略) .

    在我的例子中,框架假设您正在使用一个IOC框架,它将为您解析所有消息处理程序和存储库,因此我通过将当前IUnitOfWorkManager的实例注入到工作控制器的环境单元中来更容易地进行登记 . 必要时的构造函数 . 这样,工作单元管理器的依赖关系和需要刷新的实际部分(存储库,服务等)相反:

    internal sealed class OrderRepository : IOrderRepository, IUnitOfWork
    {
        private readonly IUnitOfWorkManager _manager;
        private readonly Dictionary<Guid, Order> _orders;
    
        public OrderRepository(IUnitOfWorkManager manager)
        {
            if (manager == null)
            {
                throw new ArgumentNullException("manager");
            }
            _manager = manager;
        }
    
        bool IUnitOfWork.RequiresFlush()
        {
            return _orders.Values.Any(order => order.HasChanges());
        }
    
        void IUnitOfWork.Flush()
        {
            // Flush here...
        }
    
        public void Add(Order order)
        {
            _orders.Add(order.Id, order);
            _manager.Enlist(this);
        }
    }
    

    一旦成功处理了请求(没有抛出异常),就会调用scope.Complete(),触发控制器检查所有登记的项目是否(仍然)需要刷新(通过调用RequiresFlush()),如果是,则刷新它们(通过调用Flush()) .

    总而言之,这允许一个非常可维护的解决方案(在我看来),其中可以动态添加新的存储库和其他依赖项,而无需更改任何主单元工作类,就像TransactionManager不需要预先知道哪些项目可能参与任何特定交易 .

相关问题