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

大憨熊 提交于 2019-12-11 10:29:38

问题


I am trying to use the Microsoft Unity Container in a Winforms C# solution using Visual Studio 2015 with .net 4.5.2. and Entity Framework 6.

I have a layered WinForms solution with the following projects:

  • AppStartup (Only sets up Unity Container and registrations. No UI here)
  • UI (User Interface)
  • BLL (Business Logic Layer)
  • DAL (Data Access Layer)

I created the AppStartup project initially as a WinForms application then deleted all Winform files. The AppStartup project acts as the application startup which has the single purpose of registering all my Unity container types as follows:

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>();

    }
}

For this registration to work, I had to add references to the projects for BLL and DAL.

The UnityFramework class is invoked from the AppStartup project program.cs file as follows:

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());
    }
}

The final line invokes a UI from the MyApp.UI project so I also had to add a reference to the MyApp.UI project. Basically, the AppStartup project has references to all other projects in my solution.

I’d like for the BLL to have a service called GetProductList(). The interface and implementation are as follows:

// 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();
    }
}

Here is where I am doing something wrong. My problem here is that the Unity Container, defined as MyAppUnityContainer, is in the AppStartup project. I tried to add the AppStartup project as a reference to my BLL project but I can't because it warns of a circular dependency. Maybe I'm not supposed to create an instance of aIDepartmentUnitOfWork in the BLL using the MyAppUnityContainer?

I thought about passing it in as a parameter in the constructor for DepartmentDataServices concrete class in the BLL from the UI but that would mean trying to instantiate it in the UI which would also cause a circular dependency.

These circular dependency errors are due to all the projects being referenced in the AppStartup project to register types for the MyAppUnityContainer. I could create the DepartmentDataServices instance in the AppStartup project and pass it as a parameter to the startup WinForm in the UI project but doing that would give the UI the power to get department data without going through the BLL.

Another thing I don't like about this attempt is that I am not using the same class name of DepartmentDataServices in both the BLL and DAL to get the department list. I would like for the service to be called DepartmentDataServices in both the BLL and DAL but in doing this I don't know how the Unity container is going to know which implementation to use when I ask for it from the BLL which is to ask for it from the DAL. This is why I have the BLL using the name of DepartmentDataServices and then it asks for the service again from the DAL using the name DepartmentUnitOfWork.

How can I implement this so the following can be done: My UI is to have a screen with a button which when pressed will ask the BLL to get a product listing using the DepartmentDataServices concrete class in the BLL which in turn is to cause the BLL to ask the DAL for the product list using the DAL implementation of DepartmentUnitOfWork. The DAL is to return the results to the BLL which in turn returns it to the UI.

Thanks in advance.


回答1:


The solution is to use Constructor Injection (which is similar to what you thought you should do).

Here is an example:

public class DepartmentDataServices : IDepartmentDataServices
{
    private readonly IDepartmentUnitOfWork m_DepartmentUnitOfWork;

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

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

Now, regarding the circular dependency issue: Compose your objects in the Composition Root. This means that you don't constructor the DAL objects in the Business Layer, and you don't constructor the Business Layer objects in the UI layer. Instead, you construct the whole object graph (all the objects from all the layers) in the Composition Root which in your case is the AppStartup project.

So, the UI Form's contructor should accept a dependency on a business layer type (e.g. IDepartmentDataServices), and the constructor of the business layer objects (e.g. DepartmentDataServices) should accept a dependency on a DAL type (e.g. IDepartmentUnitOfWork).

Here is an example of how to manually construct the object graph in the AppStartup project:

var uow = new DepartmentUnitOfWork(connection_string);

var service = new DepartmentDataServices(uow);

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

Application.Run(form);

This is an example of how you can compose your objects without a container (which is called Pure DI). I used this just to give you a sense of how Constructor Injection works. But you can still use a DI container. Simply register the types here in the Composition Root, and then ask the DI container to resolve the main UI object, and then the DI container would know how to create all the dependencies automatically (down to the data access layer objects). This is called auto-wiring.

Please note that the Composition Root is the only place where you should use the DI container. Using the container from the other projects (like in your example) is called Service Location and is considered an anti-pattern.

By the way, there is no problem with using the same name for two classes as long as they are in different namespaces. As far as the system is concerned, these classes have totally different names.



来源:https://stackoverflow.com/questions/33976371/how-do-i-use-unity-container-to-instantiate-a-dal-class-from-a-bll-class-in-a-wi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!