【问题标题】:Where to inject a dependent component which has already a constructor在哪里注入已经有构造函数的依赖组件
【发布时间】:2024-11-21 04:05:01
【问题描述】:

我有一些业务对象必须一起使用才能获得特定的结果。请看以下非常简单的示例以供参考。

我的问题是:如何在 Station 类中使用 DI 获得对代理的引用?我最喜欢构造函数注入,但这不可能是 Station 类已经有一个 stationCode 作为必需项。

有什么想法吗?

用法:

var station1 = new Station("xx");
var station2 = new Station("yy");
var route = new Route(station1, station2);

var length = route.GetLength();

public class Location
{
    public int Position {get; set;}
}

public interface IAgent
{
    Location GetLocation(string stationCode);
}

public class Station
{
    private string _stationCode;

    public Station(string stationCode)
    {
        _stationCode = stationCode;
    }

    public Location GetLocation()
    {
        // issue here: how to get a reference to the agent instance using DI
        _agent.GetLocation(_stationCode);
    }
}

public class Route
{
    private Station _station1;
    private Station _station2;

    public Route(Station station1, Station station2)
    {
        _station1 = station1;
        _station2 = station2;
    }

    public int GetLength()
    {
        var location1 = _station1.GetLocation();
        var location2 = _station2.GetLocation();

        result = location2.Position - location1.Position;

        return result;
    }
}

【问题讨论】:

  • 为什么不想在 Station 构造函数中添加第二个参数并使用 DI 解析它或添加新的构造函数?您如何看待立即解决方法“Station.GetLocation 中的 IAgent”?

标签: c# dependency-injection business-logic


【解决方案1】:

您的班级似乎遇到了身份危机。使用 DI 时,您应该只处理 2 种类型的类 - injectables and newables。您的 Station 类看起来像一个杂物,因为它既提供服务(具有依赖关系)又具有状态。为了使您的类对 DI 友好,您应该设计对状态(服务)做某事的类提供状态的类。

路线

这个类是可注入的——也就是说,它应该从 DI 容器连接。

public interface IRoute
{
    int GetLength(Station station1, Station station2);
}

public class Route : IRoute
{
    private readonly IAgent _agent;

    public Route(IAgent agent)
    {
        if (agent == null) throw new ArgumentNullException("agent");
        _agent = agent;
    }

    public int GetLength(Station station1, Station station2)
    {
        var location1 = _agent.GetLocation(station1.StationCode);
        var location2 = _agent.GetLocation(station2.StationCode);

        result = location2.Position - location1.Position;

        return result;
    }
}

车站

这个类是可新的——也就是说,你应该总是使用 new 关键字来实例化它。

public class Station
{
    private string _stationCode;

    public Station(string stationCode)
    {
        _stationCode = stationCode;
    }

    public string StationCode
    {
        get { return _stationCode; }
        // Optional: provide setter here
    }
}

用法

var station1 = new Station("xx");
var station2 = new Station("yy");

// IRoute is injected where you need to make the calculation
var length = _route.GetLength(station1, station2);

也许将Route重命名为更合适的名称会更好,因为它不提供路线,它会计算路线长度。

坦率地说,如果您的 Station 类除了单个字符串变量之外没有任何其他状态,那么消除该类并仅对 station1station2 使用字符串可能更有意义。但这主要只是个人喜好问题。

【讨论】:

    【解决方案2】:

    处理两种类型的类,injectables 和 newables 的概念是个好主意。但是当newables 应该只包含有限的业务逻辑时,你就偏离了纯粹的面向对象的概念。当您为复杂的域模型编写代码时,您的新业务类包含业务逻辑和数据。我想这也是这个问题的意图。 Station 和 Route 类是简单的示例,实际上包含更多的逻辑和数据。

    所以我建议通过将存储代码与业务逻辑分离来更好地分离关注点。我看到了两种常见的解决方案。

    1. 当数据加载到内存中时,一个单独的 StationStore 类是一个可注入的类,它加载站并将它们存储在业务域的上下文中,例如。在静态属性中:

      public IEnumarable<Station> Station.Store { get; internal set; }
      
    2. 所有 DI 代码都可以隐藏在业务基类中。将 DI 依赖项放在那里并不那么令人不安。因此可以根据提供的模板类型在通用基类中解析代理。

      public class Station : BusinessClass<Station, StationAgent>
      {
          public string StationCode { get; internal set; }
          public Location Location { get; internal set; }
      
          public Station(string stationCode)
          {
              base.Load(stationCode, this);
          }
      }
      

    【讨论】: