【问题标题】:Setting up Dependency Injection using Unity to pass instance of class into a constructor使用 Unity 设置依赖注入以将类的实例传递给构造函数
【发布时间】:2014-12-03 10:35:51
【问题描述】:

我确信这里有我的问题的答案,虽然我只是不太明白我在寻找什么或我正在阅读什么......另外,不是 100% 肯定 DI 是我应该做的在这种情况下使用。

我正在尝试将 Unity 用于我的 DI。我正在将依赖项注入 ASP.Net Web API 控制器。到目前为止一切顺利。

我将控制器需要的服务注入到我的控制器中。例如,在我的一个控制器中,我有:

private TransactionService _transactionService;

public TransactionsController(TransactionService transactionService)
{
    _transactionService = transactionService;
}

在这种情况下,我将 TransactionService 的一个实例注入到控制器中。这一切正常。

我的 WebApiConfig.cs 包含以下内容,据我了解,它实际上执行了注入:

   var container = new UnityContainer();
   container.RegisterType<ServiceBase>(new HierarchicalLifetimeManager());
   config.DependencyResolver = new UnityResolver(container);

如果需要,我可以告诉你我的UnityResolver

现在我正在努力的部分......

我的服务都继承自抽象类 ServiceBase - 最简单的形式如下:

public abstract class ServiceBase : IDisposable
{

    internal Account _account;

    public ServiceBase(){}
} 

我想做的是在我的注入过程中创建Account 并以某种方式传递它,这样我注入的服务就不需要处理实例化帐户。

当我尝试将任何构造函数添加到我的服务以接受帐户时,我会收到消息以确保我的控制器具有无参数构造函数。

那么.. 我如何设置我的 DI 以便我可以将服务注入到我的控制器中,并将一个预先实例化的 Account 实例注入到我的服务中?

更新

我忘了提(抱歉 Wiktor)——如果可能的话,我不想使用属性。在我看来很奇怪,我们可以使用 DI 来解开依赖关系,然后到处放一堆依赖属性。我很可能只是没有正确理解 DI 概念,尽管这对我来说似乎不正确

【问题讨论】:

  • 在使用 DI 时使用接口,因为它在单元测试时非常容易。在我的项目中,我最喜欢接口,因此它不会紧密耦合。此外,在进行 DI 时,只需了解松散耦合的概念并牢记单元测试。
  • 您能否清楚地解释一下您在 DI 期间尝试使用帐户属性做什么?
  • 解释我正在尝试什么有点困难,因为我不知道我应该尝试什么 - 因此在这里问。我已经解释了我想要实现的目标,即我想实例化我的帐户对象并将其注入到任何其他需要它的类中——而不是让 Unity 为我实例化帐户对象。
  • 另外 - 我如何针对接口编写统一配置,因为它们不允许构造函数?也许您可以发布有关如何执行此操作的答案?
  • 同意您对属性的更新。

标签: c# asp.net dependency-injection


【解决方案1】:

这应该很简单,只需将附加属性公开并在其上添加依赖属性即可。

 [Dependency]  
 public Account _account { get; set; }

Unity 应该能够注入属性,你甚至可以通过普通方式注册Account 类(例如使用注入工厂)。

【讨论】:

  • 使用公共属性(如您在回答中所述)而不是公共字段(如您在代码中所示)会更好吗?
  • 啊 - 抱歉,我应该提出我的问题 - 我正在尝试在没有属性的情况下执行此操作 - 这可能吗?对我来说,通过把它放在课堂上,我的课堂现在依赖于 DI(某种程度上)——这有点违背我心中的整个想法。另外 - 如果您无法访问“帐户”代码,该怎么办?
  • 另外,这不是让 Unity 创建我的 Account 对象,从而为它看到的每个 [Dependency] 创建一个实例吗?我想告诉 Unity 使用 Account 的特定实例 - 这意味着我只需创建一次,而不是每个需要它的实例 - 如果这有意义的话。
  • @Darren:如果你先注册一个特定的实例,它总是会被注入。或者你甚至可以有一个注塑厂,就像我说的那样。工厂会返回任何东西,它可能总是相同的实例。
  • @WiktorZychla - 谢谢,我就是这么想的。只是无法让它工作——对于接下来要实际编码的内容感到困惑:)
【解决方案2】:

我希望这会给你一些想法。

为服务基础创建一个接口并将Account声明为公共属性。

您的服务接口:

you can also inherit other interfaces.
public interface ITransactionService : IDisposable
{
  IAccount Account{get;set;}
}

在你的课堂上实现它:

public class TransactionService : ITransactionService{
 private IAccount _account;
 public IAccount Account
 {
   get
     {if(_account==null)_account=new Account(); return _account;}
   set
     {_account=value;}
 }
}

现在在 API 控制器上使用构造函数,如下所示:

private ITransactionService _transactionService;

public TransactionsController(ITransactionService transactionService)
{
    _transactionService = transactionService;
}

统一解析器:

var container = new UnityContainer();
   container.RegisterType<ITransactionService,TransactionService>(new HierarchicalLifetimeManager());
   config.DependencyResolver = new UnityResolver(container);

所以在这种方式中使用属性注入和构造函数注入来减少紧密耦合的依赖关系。

模拟单元测试界面将非常容易。

您还可以为 Account 类创建一个接口,以便根据您的使用情况轻松模拟。

【讨论】:

  • 我可能在这里遗漏了一些东西,但是如何注入 IAccount 实例?您的代码示例似乎总是会创建一个 Account 实例 - 如果他们想要一个不同的 IAccount 实例怎么办?
  • @DanielKelley 这正是我阅读此答案时的想法。我仍然不知道......我的另一个问题是我不知道我需要在运行时之前更新的帐户的 ID,所以 webapiconfig 中的设置将毫无意义吗?
【解决方案3】:

如果您不想使用我完全同意的属性,最好的方法是让 ServiceBase 接受帐户参数,例如:

public abstract class ServiceBase {
  private Account _account;

  public ServiceBase(Account account) {
    this._account = account;
  }
}

您还必须在派生服务上声明相同的构造函数TransactionService。当控制器被实例化时,Unity 将首先正确创建Account 类的实例,然后通过构造函数注入将其传递给您的TransactionService 对象。您应该使用生命周期管理器注册Account 对象,将其范围限定为单例。

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 2015-12-17
    相关资源
    最近更新 更多