如何使用 Unity 容器从 Winforms 应用程序中的 BLL class 实例化 DAL class

How do I use Unity Container to Instantiate a DAL class From a BLL class in a Winforms App

我正在尝试在使用 Visual Studio 2015 和 .net 4.5.2 的 Winforms C# 解决方案中使用 Microsoft Unity 容器。和 Entity Framework 6.

我有一个包含以下项目的分层 WinForms 解决方案:

我最初将 AppStartup 项目创建为 WinForms 应用程序,然后删除了所有 Winform 文件。 AppStartup 项目充当应用程序启动,其唯一目的是注册我的所有 Unity 容器类型,如下所示:

public class UnityFramework
{
    public UnityContainer MyAppUnityContainer = null;
    public UnityFramework()
    {
        ClassRegistrations();
    }

    private void ClassRegistrations()
    {
        MyAppUnityContainer = new UnityContainer();

        MyAppUnityContainer.RegisterType<MyApp.BLL.IDepartmentDataServices, 
                                         MyApp.BLL.DepartmentDataServices>();

        MyAppUnityContainer.RegisterType<MyApp.DAL.IDepartmentUnitOfWork, 
                                         MyApp.DAL.DepartmentUnitOfWork>();

    }
}

为了让这个注册生效,我必须添加对 BLL 和 DAL 项目的引用。

UnityFrameworkclass从AppStartup项目program.cs文件调用如下:

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        //Register interfaces and concrete classes
        UnityFramework aUnityFramework = new UnityFramework();

        Application.Run(new MyApp.UI.FormMain());
    }
}

最后一行从 MyApp.UI 项目调用 UI,因此我还必须添加对 MyApp.UI 项目的引用。基本上,AppStartup 项目引用了我解决方案中的所有其他项目。

我希望 BLL 有一个名为 GetProductList() 的服务。接口及实现如下:

// Interface defined in BLL 
public interface IDepartmentDataServices
{
  List<Product> GetProductList();
}

// Interface implemented in BLL
public class DepartmentDataServices : IDepartmentDataServices
{
    Public List<Product> GetProductList()
    {
    //Note: IDepartmentUnitOfWork is defined and implemented in the DAL
    IDepartmentUnitOfWork aIDepartmentUnitOfWork =  
          MyAppUnityContainer.Resolve<IDepartmentUnitOfWork >();

    return aIDepartmentUnitOfWork.GetProductList();
    }
}

这是我做错的地方。我这里的问题是,定义为 MyAppUnityContainer 的 Unity 容器位于 AppStartup 项目中。我试图将 AppStartup 项目添加为对我的 BLL 项目的引用,但我不能,因为它警告循环依赖。也许我不应该使用 MyAppUnityContainer 在 BLL 中创建 aIDepartmentUnitOfWork 实例?

我考虑过将它作为参数传递到 UI 的 BLL 中的 DepartmentDataServices 具体 class 的构造函数中,但这意味着要尝试在 UI 中实例化它这也会导致循环依赖。

这些循环依赖错误是由于在 AppStartup 项目中引用的所有项目为 MyAppUnityContainer 注册类型。我可以在 AppStartup 项目中创建 DepartmentDataServices 实例,并将其作为参数传递给 UI 项目中的启动 WinForm,但这样做将使 UI 无需通过 BLL 即可获取部门数据.

我不喜欢这种尝试的另一件事是我没有在 BLL 和 DAL 中使用相同的 class DepartmentDataServices 名称来获取部门列表。我希望该服务在 BLL 和 DAL 中都被称为 DepartmentDataServices 但在这样做时我不知道 Unity 容器如何知道当我从 BLL 请求它时要使用哪个实现它来自 DAL。这就是为什么我让 BLL 使用 DepartmentDataServices 的名称,然后它使用名称 DepartmentUnitOfWork 再次从 DAL 请求服务。

我该如何实现它才能完成以下操作: 我的 UI 是有一个带有按钮的屏幕,按下该按钮将要求 BLL 使用 BLL 中的 DepartmentDataServices 具体 class 获取产品列表,这反过来又会导致 BLL 询问 DAL对于使用 DepartmentUnitOfWork 的 DAL 实现的产品列表。 DAL 是将 return 结果传给 BLL,后者又 return 将其传给 UI。

提前致谢。

解决方案是使用构造函数注入(这与您认为应该做的类似)。

这是一个例子:

public class DepartmentDataServices : IDepartmentDataServices
{
    private readonly IDepartmentUnitOfWork m_DepartmentUnitOfWork;

    public DepartmentDataServices(IDepartmentUnitOfWork uow)
    {
        m_DepartmentUnitOfWork = uow;
    }

    Public List<Product> GetProductList()
    {
        return m_DepartmentUnitOfWork.GetProductList();
    }
}

现在,关于循环依赖问题:在 Composition Root 中组合你的对象。这意味着您不会在业务层中构造 DAL 对象,并且您不会在 UI 层中构造业务层对象。相反,您在 Composition Root 中构建整个对象图(所有层的所有对象),在您的例子中是 AppStartup 项目。

因此,UI 表单的构造函数应该接受对业务层类型(例如 IDepartmentDataServices)的依赖,并且业务层对象的构造函数(例如 DepartmentDataServices)应该接受对 DAL 类型的依赖(例如 IDepartmentUnitOfWork)。

下面是如何在 AppStartup 项目中手动构造对象图的示例:

var uow = new DepartmentUnitOfWork(connection_string);

var service = new DepartmentDataServices(uow);

var form = MyApp.UI.FormMain(service);

Application.Run(form);

这是一个示例,说明如何在没有容器(称为 Pure DI)的情况下组合对象。我使用它只是为了让您了解构造函数注入的工作原理。但是您仍然可以使用 DI 容器。只需在 Composition Root 中注册这里的类型,然后让 DI 容器解析主要的 UI 对象,然后 DI 容器就会知道如何自动创建所有依赖项(一直到数据访问层对象) .这叫做自动接线。

请注意,Composition Root 是唯一应该使用 DI 容器的地方。使用其他项目中的容器(如您的示例)称为服务位置和 is considered an anti-pattern.

顺便说一句,两个类使用相同的名称是没有问题的,只要它们在不同的命名空间。就系统而言,这些 类 具有完全不同的名称。