Asp.Net核心中的属性注入

Özg*_*lan 30 dependency-injection asp.net-core

我正在尝试将asp.net应用程序移植到asp.net核心.我在我的UnitOfWork实现上有属性注入(使用ninject).

[Inject]
public IOrderRepository OrderRepository { get; set; }
[Inject]
public ICustomerRepository CustomerRepository { get; set; }
Run Code Online (Sandbox Code Playgroud)

有没有办法在.net核心上使用DI构建来实现相同的功能?是否可以使用基于约定的绑定?

Tse*_*eng 41

不,内置的DI/IoC容器在使用和功能方面都有意保持简单,为其他DI容器插件提供了基础.

因此,没有内置支持:自动发现,自动注册,装饰器或注入器,或基于约定的注册.据我所知,目前还没有计划将其添加到内置容器中.

您必须使用具有属性注入支持的第三方容器.

请注意,在98%的场景中,属性注入被认为是错误的,因为它隐藏了依赖关系,并且无法保证在创建类时将注入对象.

使用构造函数注入,您可以通过构造函数强制执行此操作并检查null,而不是创建类的实例.使用属性注入这是不可能的,并且在单元测试期间,当它们未在构造函数中定义时,不太明显该类需要哪些服务/依赖项,因此容易遗漏和获取NullReferenceExceptions.

我迄今发现的唯一正当理由物业注射到中注入服务由第三方库生成的代理类,从那里你不知道的对象创建控制的接口创建即WCF代理.

其他地方避免它.

  • 相反,您是否希望我有十几个构造函数参数,并且还花费另外十几个初始化代码行,所有这些都是为了避免一些无论如何都不会发生的“NRE”(如果编码正确)? (9认同)
  • @AgentFire:不,不是。当您执行“var sut = new MyDomainService()”时,依赖项并不明显,尤其是如果类位于不同的库中(其源代码可能可供使用它的团队访问,也可能无法访问)。但是,“var sut = new OrderService(orderRepository, customerRepository)”因为您必须将其传递给构造函数,所以您会立即看到它(类无法实例化)。另外 `var sut = new MyService(dep1,dep2, dep3, dep4, dep5, dep6, ... dep10)` 也让你立即显而易见,你的类做了很多事情,你需要重构它 (4认同)
  • @Tseng 不需要闷闷不乐的“大型业务关键应用程序”先生,我们都写这些。我在公司的项目中使用属性注入作为一种有利的方法,以避免必然会发生很大变化的样板代码。如果配置正确,好的 DI 框架应该为您执行空检查。您唯一需要适应的是不要使用构造函数显式创建服务实例。学习这一点比编写具有 5 个参数的构造函数花费的时间更少。 (4认同)
  • @AgentFire:这不是要编写一些额外的行(顺便说一句,可以通过重构助手完成 90%,即创建、初始化和 nullcheck 构造函数参数),它也不利于编写单元测试(因为依赖关系变得不明显,有人测试如果它隐藏在属性后面,那么类将不知道他/她需要提供或模拟哪个依赖项,而在构造函数上则非常明显)。最重要的是,当一个类做了太多事情时,构造函数注入会变得很明显(当你有超过 4-5 个依赖项时,这意味着你的抽象很糟糕,并且类试图做太多事情) (3认同)
  • @Tseng Ctor 注入要求您将每个名称重复四次。这只是意识形态。每隔十年左右,这些时尚就会席卷整个行业,迫使每个人都输入毫无意义的样板文件,这样一些书呆子就会觉得自己很重要,直到工具迎头赶上并再次隐藏混乱。上次是 Java 中的 setter 和 getter,其中本应在一行上的内容被夸大到半屏。 (3认同)
  • @user3608068:不,你和其他发帖者不明白。事情会发生,它不会被注入。或者当你模拟一个服务进行单元测试时,不可能知道哪些服务是需要的,哪些是不需要的。使用属性注入,您需要查看实现以及是否需要该服务以及服务中的哪些调用,以便您可以模拟和分配它。对于构造函数注入,您可以在构造函数签名上清楚地看到它。无需搜索或调试单元测试因空引用异常而失败的原因。开发时节省 10 或 10 万欧元 (2认同)

Fla*_*ien 7

得票最高的答案并不完全正确。虽然内置 DI 容器 ( IServiceProvider) 有意保持简单且功能轻,但它也具有相当的可扩展性,并且可以通过一些管道提供您所需的功能。

这就是Quickwire NuGet 包提供的开箱即用的功能。这正如您所期望的那样工作。

首先用 和 来装饰类[RegisterService],并使用 attribute 来注入属性[InjectService]

[RegisterService(ServiceLifetime.Scoped)]
public class BusinessLogic
{
    [InjectService]
    public IOrderRepository OrderRepository { get; set; }
    [InjectService]
    public ICustomerRepository CustomerRepository { get; set; }

    // ...
}
Run Code Online (Sandbox Code Playgroud)

然后使用ScanCurrentAssembly来自ConfigureServices

public void ConfigureServices(IServiceCollection services)
{
    services.ScanCurrentAssembly();

    // Register other services...
}
Run Code Online (Sandbox Code Playgroud)

这将扫描当前程序集以查找用 修饰的类RegisterService并将它们注册到 中IServiceCollection,同时确保将依赖项注入到用 修饰的 setter 中[InjectService]

再次强调,此方法不需要您使用 Autofac 等第三方依赖项注入容器。您的所有现有代码将继续工作,无需任何修改。


B12*_*ter 5

是否有办法在.net核心上使用内置DI实现相同的功能?

不,但是这里是您可以[inject]autofac的属性注入机制的帮助下创建自己的属性的方法

首先创建自己的InjectAttribute

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class InjectAttribute : Attribute
{
  public InjectAttribute() : base() { }
}
Run Code Online (Sandbox Code Playgroud)

然后创建自己的InjectPropertySelector,使用反射来检查标记为的属性[inject]

public class InjectPropertySelector : DefaultPropertySelector
{
  public InjectPropertySelector(bool preserveSetValues) : base(preserveSetValues)
  { }

  public override bool InjectProperty(PropertyInfo propertyInfo, object instance)
  {
    var attr = propertyInfo.GetCustomAttribute<InjectAttribute>(inherit: true);
    return attr != null && propertyInfo.CanWrite
            && (!PreserveSetValues
            || (propertyInfo.CanRead && propertyInfo.GetValue(instance, null) == null));
  }
}
Run Code Online (Sandbox Code Playgroud)

然后在你使用你的选择ConfigureServices ,你线了AutofacServiceProvider

public class Startup
{
  public IServiceProvider ConfigureServices(IServiceCollection services)
  {
    var builder = new ContainerBuilder();
    builder.Populate(services);

    // use your property selector to discover the properties marked with [inject]
    builder.RegisterType<MyServiceX>().PropertiesAutowired((new InjectablePropertySelector(true)););

    this.ApplicationContainer = builder.Build();
    return new AutofacServiceProvider(this.ApplicationContainer);
  }
}
Run Code Online (Sandbox Code Playgroud)

最后,您现在可以使用[inject]

public class MyServiceX 
{
    [Inject]
    public IOrderRepository OrderRepository { get; set; }
    [Inject]
    public ICustomerRepository CustomerRepository { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

您肯定可以进一步采用此解决方案,例如,通过在服务的类定义上方使用属性来指定服务的生命周期...

[Injectable(LifetimeScope.SingleInstance)]
public class IOrderRepository
Run Code Online (Sandbox Code Playgroud)

...然后在通过autofac配置服务时检查此属性。但这将超出此答案的范围。


归档时间:

查看次数:

18070 次

最近记录:

5 年,11 月 前