简体   繁体   English

具有约定的内置依赖项注入

[英]Built-in dependency injection with conventions

How to inject services without registering them? 如何在不注册服务的情况下注入服务? I mean in the past some DI frameworks automatically registered Service for IService . 我的意思是,过去有些DI框架自动为IService注册了Service

I'm in a situation where I have a dozen of services and basically registering every single one is a pain in the ass. 我处于一种状态,那里我有很多服务,而基本上每一个服务都很难注册。 so is this supported in asp.net core default DI framework? 所以在asp.net核心默认DI框架中支持此功能吗?

I guess you like the way it works with Autofac: 我想您喜欢Autofac的工作方式:

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

But you don't want to switch to Autofac for some reasons (for example you want use extensions from external libraries to register their dependencies). 但是由于某些原因,您不想切换到Autofac(例如,您想使用来自外部库的扩展来注册其依赖项)。 So my suggestion is to create some extensions that use reflection like these ones: 所以我的建议是创建一些使用反射的扩展,例如:

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);

Now you can register all your services by simple code like this: 现在,您可以通过以下简单代码注册所有服务:

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

Feel free to customize these extensions to match your needs. 可以随意自定义这些扩展以适应您的需求。 For example you can fire an exception if you don't find implementations for some services if that will make you feel a bit safer. 例如,如果您找不到某些服务的实现,则可以引发异常,如果这样做会使您感到更安全。

The out-of-the-box DI doesn't support it and do not intend to do so. 开箱即用的DI不支持它,也不打算这样做。 The built-in IoC Container is kept simple by design, to allow basic dependency injection which works for most cases. 内置的IoC容器在设计上保持简单,从而允许基本的依赖项注入在大多数情况下都有效。

If you want advanced features like registering by convention, assembly scanning or decorator support, you have to use 3rd party IoC container like Autofac, SimpleInjector, Castle Windsor. 如果您需要高级功能,例如按约定注册,程序集扫描或装饰器支持,则必须使用第三方IoC容器,例如Autofac,SimpleInjector,Castle Windsor。

I would like to mention the Scrutor nuget which let's you use the ASP.NET Core Dependency Injection but adds a fluent interface on top of it to allow for convention-based configuration. 我想提到Scrutor nuget ,它使您可以使用ASP.NET Core依赖注入,但在它上面添加了一个流畅的接口以允许基于约定的配置。

Read this article for more information. 阅读本文以获取更多信息。

Adding to @cherepets' answer, if you would like to: 如果您想添加到@cherepets的答案,请执行以下操作:

  • register transient services by convention 按照约定注册临时服务
  • search in different assemblies for interface and implementation 在不同的程序集中搜索接口和实现
  • only look at those that begin with a particular namespace (eg, company name or app name) 只查看那些以特定名称空间开头的名称(例如,公司名称或应用名称)
  • only look at implementation names names that "match" the interface name (eg, IMyEmail and MyEmail) 只查看与接口名称“匹配”的实现名称(例如,IMyEmail和MyEmail)
  • throw an exception upon startup if any implementations are not found 如果找不到任何实现,则在启动时引发异常

You can use this function: 您可以使用此功能:

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);
    }
}

And then call it like this in your ConfigureServicescs: 然后在您的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"));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM