【问题标题】:Built-in dependency injection with conventions带有约定的内置依赖注入
【发布时间】:2019-10-15 10:26:24
【问题描述】:

如何在不注册的情况下注入服务? 我的意思是在过去,一些 DI 框架会自动为 IService 注册 Service

我的情况是,我有十几个服务,基本上注册每一个服务都是一件很痛苦的事情。那么在 asp.net core 默认 DI 框架中是否支持?

【问题讨论】:

  • 注册十几个服务很痛苦?真的吗?
  • 好吧,我考虑承受这种痛苦并手动注册。当许多其他开发人员偶然发现一些反射巫术魔法时,他们会杀死你,什么注册他们编写的所有内容。
  • @eocron Reflection 如果您想从您在项目中不直接引用的程序集(插件等)中注册,则可能非常有用,但我同意,手动进行。
  • 根据我的经验,这种行为在类似插件的系统中确实很有用,但不是每次分叉。
  • @eocron 它确实使 startup.cs 成为一个巨大的类,也很丑陋。

标签: c# dependency-injection asp.net-core


【解决方案1】:

我猜你喜欢它使用 Autofac 的方式:

var assembly = typeof(MyModule).Assembly;
builder.RegisterAssemblyTypes(assembly)
    .Where(t => t.Name.EndsWith("Service"))
    .AsImplementedInterfaces()
    .InstancePerLifetimeScope();

但由于某些原因,您不想切换到 Autofac(例如,您想使用外部库的扩展来注册它们的依赖项)。 所以我的建议是创建一些使用反射的扩展:

public static IServiceCollection AddSingletonsByConvention(this IServiceCollection services, Assembly assembly, Func<Type, bool> interfacePredicate, Func<Type, bool> implementationPredicate)
{
    var interfaces = assembly.ExportedTypes
        .Where(x => x.IsInterface && interfacePredicate(x))
        .ToList();
    var implementations = assembly.ExportedTypes
        .Where(x => !x.IsInterface && !x.IsAbstract && implementationPredicate(x))
        .ToList();
    foreach (var @interface in interfaces)
    {
        var implementation = implementations.FirstOrDefault(x => @interface.IsAssignableFrom(x));
        if (implementation == null) continue;
        services.AddSingleton(@interface, implementation);
    }
    return services;
}

public static IServiceCollection AddSingletonsByConvention(this IServiceCollection services, Assembly assembly, Func<Type, bool> predicate)
    => services.AddSingletonsByConvention(assembly, predicate, predicate);

现在您可以通过如下简单代码注册所有服务:

var assembly = typeof(MyType).Assembly;
services.AddSingletonsByConvention(assembly, x => x.Name.EndsWith("Service"));

随意自定义这些扩展程序以满足您的需求。 例如,如果您找不到某些服务的实现,则可以触发异常,如果这会让您感觉更安全一些。

【讨论】:

    【解决方案2】:

    开箱即用的 DI 不支持并且不打算这样做。内置的 IoC 容器在设计上保持简单,允许在大多数情况下工作的基本依赖注入。

    如果您想要按约定注册、程序集扫描或装饰器支持等高级功能,则必须使用 Autofac、SimpleInjector、Castle Windsor 等第 3 方 IoC 容器。

    【讨论】:

    • 对。 StructureMap 用一行来支持这一点。 Autofaq 也可以通过约定轻松完成。
    【解决方案3】:

    我想提一下Scrutornuget,它让您可以使用 ASP.NET Core 依赖注入,但在其之上添加了一个流畅的接口以允许基于约定的配置。

    阅读此article 了解更多信息。

    【讨论】:

      【解决方案4】:

      如果您愿意,可以添加到@cherepets 的答案中:

      • 按惯例注册临时服务
      • 在不同的程序集中搜索接口和实现
      • 仅查看以特定命名空间(例如,公司名称或应用名称)开头的那些
      • 只查看与接口名称“匹配”的实现名称(例如,IMyEmail 和 MyEmail)
      • 如果未找到任何实现,则在启动时引发异常

      你可以使用这个功能:

      public void AddTransientsByConvention(IServiceCollection services, Assembly[] assemblies, Func<Type, bool> myPredicate)
      {
          List<Type> interfaces = new List<Type>();
          List<Type> implementations = new List<Type>();
      
          foreach (var assembly in assemblies)
          {
              interfaces.AddRange(assembly.ExportedTypes.Where(x => x.IsInterface && myPredicate(x)));
              implementations.AddRange(assembly.ExportedTypes.Where(x => !x.IsInterface && !x.IsAbstract && myPredicate(x)));
          }
      
          foreach (var @interface in interfaces)
          {
              var implementation = implementations
                  .FirstOrDefault(x => @interface.IsAssignableFrom(x) &&
                      $"I{x.Name}" == @interface.Name );
      
              if (implementation == null)
                  throw new Exception($"Couldn't find implementation for interface {@interface}");
      
              services.AddTransient(@interface, implementation);
          }
      }
      

      然后在你的 ConfigureServicescs 中这样调用它:

      var assemblyOne = Assembly.GetAssembly(typeof(IMyRepository));
      var assemblyTwo = Assembly.GetAssembly(typeof(IMyBusinessLogic));
      var assemblyThree = Assembly.GetExecutingAssembly();
      
      AddTransientsByConvention(services,
          new [] { assemblyOne, assemblyTwo , assemblyThree },
          x => x.Namespace.StartsWith("CompanyName"));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-10-25
        • 1970-01-01
        • 2017-04-26
        • 2016-07-21
        • 2023-03-09
        • 1970-01-01
        • 2021-11-05
        • 2015-09-03
        相关资源
        最近更新 更多