依赖注入和项目参考

Cra*_*aig 5 c# dependency-injection

我正在尝试学习DI,以更好地了解IoC和其他好处。

在DI之前,我有一个项目,其中有一个UI项目(MVC),一个BusinessLogic项目和一个DataAccess项目。我也有一个SharedLib项目。所有项目都引用SharedLib。UI引用了BusinessLogic,而BusinessLogic引用了DataAccess。

我现在要添加接口。因此,我进入DataAccess,为每个类添加一个接口,并使用其方法填充它们。我对业务逻辑层也做同样的事情。

但是为了注入DataAccess类(我在UI项目的BusinessLogic类中实例化),我需要引用我的Data项目,因为UI项目(我认为正确)不知道“ IDataAccess”接口是什么。我能看到的唯一解决方法是在UI中向我的DA项目添加项目引用-这似乎是错误的。

而且,如果我尝试将Unity添加为容器(以后的一天,一旦我确定了所有的工作原理),并且想在UI项目中初始化我的Interface / Class关系-同样的问题。

也许接口必须进入某个共享项目中?还是有一个项目?应该如何处理?

Ton*_*one 5

如果您不希望在项目之间进行引用,则可以研究工厂/抽象工厂。

您的UI知道您的业务层,因此您想在业务层中定义一个工厂,该工厂知道如何使用数据层。然后,您可以在合成根目录(此示例中的UI项目)中处理所有DI。

下面是一个使用控制台应用程序作为UI的简单示例,坚持您在问题中所述的引用

资料层

public interface IDataAccess
{
    string GetData();
}

public class XmlDataAccess : IDataAccess
{
    public string GetData()
    {
        return "some data";
    }
}
Run Code Online (Sandbox Code Playgroud)

业务层

public interface IDataAccessFactory
{
    IDataAccess GetDataAccess();
}

public class XmlDataAccessFactory : IDataAccessFactory
{
    public IDataAccess GetDataAccess()
    {
        return new XmlDataAccess();
    }
}

public class BusinessLogic
{
    IDataAccessFactory dataAccessFactory;

    public BusinessLogic(IDataAccessFactory dataAccessFactory)
    {
        this.dataAccessFactory = dataAccessFactory;
    }

    public void DoSomethingWithData()
    {
        IDataAccess dataAccess = dataAccessFactory.GetDataAccess();
        Console.WriteLine(dataAccess.GetData());
    }

    public string GetSomeData()
    {
        IDataAccess dataAccess = dataAccessFactory.GetDataAccess();
        return dataAccess.GetData();
    }
}
Run Code Online (Sandbox Code Playgroud)

用户界面

static void Main(string[] args)
{
    IUnityContainer container = new UnityContainer();
    container.RegisterType<IDataAccessFactory, XmlDataAccessFactory>();

    var logic = container.Resolve<BusinessLogic>();
    logic.DoSomethingWithData();

    string useDataInUI = logic.GetSomeData();
    Console.WriteLine("UI " + useDataInUI);

    Console.ReadKey();
}
Run Code Online (Sandbox Code Playgroud)

这是一个人为的示例,因此看起来像是一无是处的抽象,但是对于真实世界的示例,它将更有意义。

例如,您在数据层数据库,xml文件等中可能有很多不同的数据访问类,因此您可以为业务层中的每个工厂定义一个工厂。


使用抽象工厂

工厂可以包含有关数据层实质的更多逻辑,或者作为抽象工厂向业务逻辑层提供一组单独的工厂。

业务层

您可能会在业务层中拥有一个抽象工厂,例如

public interface IPlatformFactory
{
    IDataAccessFactory GetDataAccessFactory();
    IPricingFactory GetPricingFactory(); // might be in the business project, or another project referenced by it
}
Run Code Online (Sandbox Code Playgroud)

与混凝土厂

public class WebPlatformFactory : IPlatformFactory
{
    IDataAccessFactory GetDataAccessFactory()
    {
        return new XmlDataAccessFactory();
    }

    IPricingFactory GetPricingFactory()
    {
        return new WebPricingFactory(); // not shown in the example
    }
}
Run Code Online (Sandbox Code Playgroud)

(您可能还有其他的混凝土工厂,例如RetailPlatformFactory等等)

您的BusinessLogic班级现在看起来像

public class BusinessLogic
{
    IPlatformFactory platformFactory;

    public BusinessLogic(IPlatformFactory platformFactory)
    {
        this.platformFactory = platformFactory;
    }

    public void DoSomethingWithData()
    {
        IDataAccessFactory dataAccessFactory = platformFactory.GetDataAccessFactory();
        IDataAccess dataAccess = dataAccessFactory.GetDataAccess();
        Console.WriteLine(dataAccess.GetData());
    }

    public string GetSomeData()
    {
        IDataAccessFactory dataAccessFactory = platformFactory.GetDataAccessFactory();
        IDataAccess dataAccess = dataAccessFactory.GetDataAccess();
        return dataAccess.GetData();
    }
}
Run Code Online (Sandbox Code Playgroud)

资料层

您的业​​务层不再需要为IDataAccessFactoryUI 提供一个,因此在此示例中,您可以将其移入数据层。因此,数据层类将是

public interface IDataAccess
{
    string GetData();
}

public class XmlDataAccess : IDataAccess
{
    public string GetData()
    {
        return "some data";
    }
}

public interface IDataAccessFactory
{
    IDataAccess GetDataAccess();
}

public class XmlDataAccessFactory : IDataAccessFactory
{
    public IDataAccess GetDataAccess()
    {
        return new XmlDataAccess();
    }
}
Run Code Online (Sandbox Code Playgroud)

用户界面

现在,您将在UI中配置容器并执行与

static void Main(string[] args)
{
    IUnityContainer container = new UnityContainer();
    container.RegisterType<IPlatformFactory, WebPlatformFactory>();

    var logic = container.Resolve<BusinessLogic>();
    logic.DoSomethingWithData();

    string useDataInUI = logic.GetSomeData();
    Console.WriteLine("UI " + useDataInUI);

    Console.ReadKey();
}
Run Code Online (Sandbox Code Playgroud)

然后,UI对数据层/访问一无所知,只是将工厂创建移交给业务层,该业务层保存了数据(和定价)参考。

一些推荐的读物:

组成根

实施抽象工厂

自信地组成对象图