【问题标题】:Property / method injection using Autofac in filter attributes在过滤器属性中使用 Autofac 进行属性/方法注入
【发布时间】:2018-11-22 06:22:00
【问题描述】:

尝试使用 autofac 通过属性进行依赖注入。

实例始终为空,并且没有注入依赖项。 下面是需要注入属性的类。

public class UserAccount
{
    public IAccountService AccountService { get; set; }

    public string Message()
    {
        return AccountService.Message();
    }
}

我尝试了三种不同的方法来注入属性,但都没有成功

方法一:

builder.Register(c => {
                var result = new UserAccount();
                var dep = c.Resolve<IAccountService>();
                result.SetDependency(dep);
                return result;
            });

方法二:

builder.RegisterType<UserAccount>().PropertiesAutowired();

方法三:

builder.Register(c => new UserAccount { AccountService = c.Resolve<IAccountService>()});

PS : 欢迎上述方法注入。

【问题讨论】:

  • UserAccount 对象也是 Autofac 创建的吗?如果您手动创建它(通过使用new 运算符),它将不起作用,因为 Autofac 不参与对象创建。
  • @KBO 是的,我正在使用 new 手动创建它,那我该怎么办?
  • 请出示说明您如何解析和使用UserAccount的代码。
  • 问题标题中提到的filter属性在哪里? filter属性是什么框架?

标签: c# dependency-injection autofac property-injection


【解决方案1】:

您应该防止让您的容器创建以数据为中心的对象,例如您的UserAccount 实体。这会导致复杂的场景,例如您现在所处的场景。

一般来说,您的 DI 容器应该只解析组件——这些是您系统中包含应用程序行为的类,没有任何有趣的状态。这些类型的类通常寿命长,或者至少比以数据为中心的对象寿命更长。

以数据为中心的对象(如实体)最好手动创建。不这样做会导致实体具有大型构造函数,这很容易导致构造函数过度注入代码异味。作为补救措施,您可能会重新使用属性注入,但这会导致其自身的代码异味,导致 Temporal Coupling

相反,更好的解决方案是:

  1. 手工创建实体,而不是使用 DI 容器
  2. 使用方法注入向实体提供依赖项,而不是使用属性注入

使用方法注入,您的UserAccount 将如下所示:

// This answer assumes that this class is an domain entity.
public class UserAccount
{
    public Guid Id { get; set; }
    public byte[] PasswordHash { get; set; }

    public string Message(IAccountService accountService)
    {
        if (accountService == null)
            throw new ArgumentNullException(nameof(accountService));

        return accountService.Message();
    }
}

不过,这确实将提供依赖关系的责任从 Composition Root 转移到了实体的直接消费者。但如上所述,这是有意为之,因为一般来说,Composition Root,尤其是 DI Container,不应该负责创建实体和其他以数据为中心的短期对象。

然而,这确实意味着UserAccount 的直接消费者应该注入该依赖项,并且知道该依赖项的存在。但由于该消费者将是一个以行为为中心的类,典型的解决方案是在该阶段使用构造函数注入:

public class UserService : IUserService
{
    private readonly IAccountService accountService;
    private readonly IUserAccountRepository repo;

    public UserService(IAccountService accountService, IUserAccountRepository repo)
    {
        this.accountService = accountService;
        this.repo = repo
    }

    public void DoSomething(Guid id)
    {
        UserAccount entity = this.repo.GetById(id);
        var message = entity.Message(this.accountService);
    }
}

【讨论】:

  • 但这意味着我必须在表示层中引用业务层才能访问该存储库...可以吗?我的意思是这不会违反层分离吗?目前表现层只知道服务层..
  • 这可能意味着您将抽象放在错误的位置,因为您的表示层不应该依赖于业务层,如果它只需要使用它的抽象。您的评论似乎还暗示您的表示层直接调用UserAccount.Message,这将违反层分离,因为表示层通常不应直接调用域方法,而只能通过交互层这样做。
  • 到目前为止,我已经成功使用 DI 并在我的控制器中使用它,但是有一种情况我必须在我的自定义 mvc 过滤器中使用服务,为此我必须保留过滤器参数更少,这就是为什么我创建了这个名为 UserAccount 的类来处理我的过滤器的服务。我们试图为该项目创建一个基础。
  • 啊,好吧。所以你说UserAccount 不是 一个实体,而是一个组件。在那种情况下,我的回答没有帮助。相反,您的问题似乎与 this q/a 重复。
  • 所以没有属性的 DI 看起来像是一个无形的死胡同?但是还有其他几篇文章提到了各种过滤器注入方法,尤其是ninject。我想我会在这里上老学校没什么特别的:)
【解决方案2】:

使用方法3,需要注册AccountService,即

        builder.RegisterType<AccountService>().As<IAccountService>();
        builder.Register(c => new UserAccount { AccountService = c.Resolve<IAccountService>()});

当您使用 UserAccount 时,请确保它是使用 Autofac 创建的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-01
    • 1970-01-01
    • 2017-01-26
    相关资源
    最近更新 更多