内置依赖注入与约定

时间:2017-02-11 19:52:57

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

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

在我有十几项服务的情况下,基本上每一项服务都是痛苦的。这是否支持在asp.net核心默认DI框架中?

4 个答案:

答案 0 :(得分:4)

我想你喜欢它与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"));

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

答案 1 :(得分:1)

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

如果您需要按惯例注册,装配扫描或装饰器支持等高级功能,则必须使用第三方IoC容器,如Autofac,SimpleInjector,Castle Windsor。

答案 2 :(得分:1)

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

阅读此article以获得更多信息。

答案 3 :(得分:0)

如果愿意,可添加到@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"));