依赖注入和项目参考 [英] Dependency Injection and project references

查看:128
本文介绍了依赖注入和项目参考的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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



我现在要添加接口。所以我去我的DataAccess,并为每个类添加一个接口,并用他们的方法来填充它们。我为业务逻辑层做同样的事情。



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



如果我尝试添加Unity作为我的容器在一天中,一旦我了解了一切如何工作),并希望在UI项目中初始化我的接口/类关系 - 同样的问题。



也许界面必须去一些共享的项目?还是一个项目?如何处理?

解决方案

如果您不希望项目之间的参考,您可以查看工厂/抽象工厂。



您的UI了解您的业务层,因此您希望在业务层中定义一个了解如何使用数据层的工厂。然后你处理你的组合根目录中的所有DI(在这个例子中是UI项目)。



下面是一个简单的例子,使用控制台应用程序作为UI,坚持引用你在你的问题中说过



数据层

  public interface IDataAccess 
{
string GetData();
}

public class XmlDataAccess:IDataAccess
{
public string GetData()
{
返回一些数据;
}
}

业务层 p>

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

UI

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

这是一个有创意的例子,所以它看起来像没有什么抽象,但是真正的世界的例子会更有意义。



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






使用抽象工厂



工厂可能包含更多关于数据层的粗糙度或抽象工厂的逻辑为业务逻辑层提供一套个别工厂。



业务层



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

  public interface IPlatformFactory 
{
IDataAccessFactory GetDataAccessFactory();
IPricingFactory GetPricingFactory(); //可能在商业项目中,或其引用的另一个项目
}

与一个具体的工厂

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

IPricingFactory GetPricingFactory()
{
返回新的WebPricingFactory(); //在示例中没有显示
}
}

(您可能会额外的具体工厂,如 RetailPlatformFactory 等。)



您的 BusinessLogic class现在看起来像是一个类似

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

数据层 p>

您的业务层不再需要向您的UI提供 IDataAccessFactory ,以便您可以将其移动到您的数据层中例。所以数据层类将是

  public interface IDataAccess 
{
string GetData();
}

public class XmlDataAccess:IDataAccess
{
public string GetData()
{
返回一些数据;
}
}

public interface IDataAccessFactory
{
IDataAccess GetDataAccess();
}

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

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

UI然后不了解数据层/访问,它只是移交工厂创建到业务层,它拥有数据(和定价)参考。



有些建议阅读:



组成根



实施抽象工厂



自信地撰写对象图表


I am trying to learn about DI, to have a better understanding of IoC, and the other benefits.

Pre DI, I have a project that has a UI project (MVC), a BusinessLogic project and a DataAccess project. I also have a SharedLib project. All projects have a reference to SharedLib. UI has a reference to BusinessLogic, and BusinessLogic has a reference to DataAccess.

I want to add the Interfaces now. So I go to my DataAccess, and add an Interface for each class, and populat them with their methods. I do the same for the business logic layer.

But in order to inject the DataAccess class, which I instantiate in the BusinessLogic class in the UI project, I need a reference to my Data project, because the UI project (correctly, I think) has no idea what an 'IDataAccess' interface is. The only fix I can see is to add a project reference in my UI to my DA project - which seems wrong.

And if I try add Unity as my container (One day in the future, once i work out how that all works), and want to initialise my Interface/Class relationships in the UI project - same issue.

Maybe the interfaces must go in some shared project? Or one project up? How should this be handled?

解决方案

If you don't want the references between projects you could look into factories/abstract factories.

Your UI knows about your business layer, so you want to define a factory in your business layer which knows how to use the data layer. Then you handle all your DI in your composition root (the UI project in this example).

A simple example below using a console app as the UI, sticking to the references you stated in your question

Data layer

public interface IDataAccess
{
    string GetData();
}

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

Business layer

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

UI

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

It's a contrived example so it looks like abstraction for nothing, but with a real world example it would make more sense.

e.g. you might have a bunch of different data access classes in your data layer database, xml files, etc. so you might define a factory for each in your business layer.


Using abstract factories

The factory could contain a lot more logic about the nitty gritty of the data layer, or as an abstract factory provide a set of individual factories to the business logic layer.

Business layer

You might instead have an abstract factory in the business layer such as

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

with a concrete factory

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

    IPricingFactory GetPricingFactory()
    {
        return new WebPricingFactory(); // not shown in the example
    }
}

(You might have additional concrete factories such as RetailPlatformFactory, etc.)

Your BusinessLogic class would now look something like

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

Data layer

Your business layer no longer needs to provide an IDataAccessFactory to your UI so you can move it into your data layer in this example. So the data layer classes would be

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

UI

Now you'd in the UI you'd configure the container and perform similar actions as

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

The UI then knows nothing about the data layer/access, it's just handing off the factory creation to the business layer, which holds the data (and pricing) references.

Some recommended reading:

Composition Root

Implementing an Abstract Factory

Compose object graphs with confidence

这篇关于依赖注入和项目参考的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆