引导 Unity - Composition Root 位置

Bootstrapping Unity - Composition Root location

我有一个简单的 .NET 项目,我想知道引导 Unity 的最佳方法是什么。 我从一个带有一堆控制器的 WebApp 开始。这些控制器中的每一个都有自己的处理程序 class,控制器将实现委托给该处理程序。内容如下:

public class UsersHandler : IUsers
{
    IAuthenticationClient authenticationClient;
    ILogger logger;

    public UsersHandler(IAuthenticationClient authClient, ILogger logger) { ... }       
}

在 Global.asax 的 Application_Start 方法中,我正在创建 UnityContainer 并注册类型。 还有第二个项目(Class 库),它基本上是业务层。

我现在创建了一个新的 class 库(我们称之为 'XYZ')来处理应用程序的不同职责。这里也使用了 DI。

对于初学者,我创建了一个 Singleton class 作为该项目的入口点,我在其中实例化 UnityContainer 并注册类型。

现在我有了这个工作,我开始想知道应该由谁来处理类型注册,也就是说,我的应用程序的组合根应该是什么。 WebApp 将是首选,但这需要添加对 'XYZ' 项目的引用,这感觉不对,因为它被业务层使用。

1) Composition root 是否应该是一个新的 class 库,同时引用 WebApp 和 'XYZ' 并在我的 WebApp 的 global.asax 中初始化? 但是,这会导致循环依赖,因为这个 Bootstrapper 项目会知道 WebApp,反之亦然。

2) 如果我需要解决 'XYZ' 项目中的依赖关系怎么办?目前我有一个 UnityContainer class 的实例,所以我可以这样做:

var logger = container.Resolve<ILogger>();

无论如何,这是一个好习惯吗?

  1. 你应该在启动方法中执行一次
  2. 您可以实现 Unity 依赖解析器,而不要执行 container.Resolve<IDependency>()
  3. 之类的代码

你可以在这里阅读一篇关于它的非常好的文章https://www.asp.net/web-api/overview/advanced/dependency-injection

this 说得很好

"All components in an application should be composed as late as possible"

然后您应该将它们组合到 WebApp 项目的 global.asax 中。 全部(基本回答了两个问题)

组合根是组合应用程序中所有模块的地方。 在您的情况下,WebApp 将是该项目,因此应引用您解决方案中的所有项目。 WebApp 应该指业务层、XYZ 和您添加的所有其他项目。

"However, that would cause a circular dependency.."

不,只有组合根引用解决方案中的项目。

那么业务层和XYZ是如何协同工作的呢?

他们没有。 DIP 的核心原则是依赖于抽象,要在您的解决方案中充分使用 DIP,您需要正确设计代码。

假设我们在业务层项目中有一个 class 依赖于 XYZ 项目

using XYZ;
namespace BusinessLayer
{
    public class businessData
    {
        public int GetData()
        {
            var data = new XYZData(); //from the XYZ project
            return data
        }
    }
}

而这个 class 在 XYZ 项目中

namespace XYZ
{
    public class XYZData
    {
        public int GetData()
        {
            return 1;
        }
    }
}

现在我们有了项目 BusinessLayer 和 XYZ 之间的依赖关系。 为了解决这个问题,我们需要让 BusinessLayer 中的 businessData 依赖于抽象而不是细节。

要做到这一点,我们需要使用接口来消除 classes 之间的依赖关系,当处理好后,我们不需要在 BusinessLayer 项目中引用 XYZ,因为它不在再用了。

但是我们在哪里存储接口?

为此,您创建了一个 ModelProject,解决方案中的所有项目都将引用它。 ModelProject 不引用任何东西,因为它的目的是仅存储 DTO 和接口等。

解决方案中的引用应该是这样的(对不起我的绘画技巧z):

其中 WebApp 作为组合根,应指代解决方案中的所有项目。其余项目应该只参考ModelProject

要解决 businessData 中的依赖关系,请将此接口添加到 Modelproject

namespace Modelproject
{
    public interface IXYZData
    {
        int GetData();
    }
}

然后在classes

中实现接口

商业项目

using Modelproject;
namespace BusinessLayer
{
    public class businessData
    {
        private IXYZData _data;
        public businessData(IXYZData data)
        {
            this._data = data;
        }

        public int GetData()
        {
            return _data.GetData();
        }
    }
}

对于 XYZ 项目

using Modelproject;
namespace XYZ
{
    public class XYZData: IXYZData
    {
        public int GetData()
        {
            return 1;
        }
    }
}

然后在 WebApp 的 global.asax 中用 XYZData class 解析接口 IXYZData。 通过 Unity 那将是

var container = new UnityContainer();
container.RegisterType<IXYZData, XYZData>(new HierarchicalLifetimeManager());
GlobalConfiguration.Configuration.DependencyResolver = new UnityResolver(container);

希望这有帮助,干杯

更新:

如果您想在另一个 class 中使用 businessData class,您应该应用与我们在添加接口的 XYZData class 上所做的相同的结构。

namespace Modelproject
{
    public interface IbusinessData
    {
        int GetData();
    }
}

然后将接口继承添加到class

using Modelproject;
namespace BusinessLayer
{
    public class businessData: IbusinessData
    {
    .....

最后告诉 Global.asax 中的 Unity 容器将接口解析到 class

container.RegisterType<IbusinessData, businessData>(new HierarchicalLifetimeManager());

现在,在每个需要使用 IbusinessData 的 class 中,将其添加到 class 的构造函数中,就像我们在 businessData 构造函数中所做的那样,然后 Unity 将确保注入所有在运行时创建 class 时的依赖项。