Built-in dependency injection with conventions

断了今生、忘了曾经 提交于 2021-02-07 06:34:25

问题


How to inject services without registering them? I mean in the past some DI frameworks automatically registered Service for IService.

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?


回答1:


The out-of-the-box DI doesn't support it and do not intend to do so. The built-in IoC Container is kept simple by design, to allow basic dependency injection which works for most cases.

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.




回答2:


I guess you like the way it works with 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). 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.




回答3:


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.

Read this article for more information.




回答4:


Adding to @cherepets' answer, if you would like to:

  • 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)
  • 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:

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


来源:https://stackoverflow.com/questions/42180526/built-in-dependency-injection-with-conventions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!