簡體   English   中英

具有約定的內置依賴項注入

[英]Built-in dependency injection with conventions

如何在不注冊服務的情況下注入服務? 我的意思是,過去有些DI框架自動為IService注冊了Service

我處於一種狀態,那里我有很多服務,而基本上每一個服務都很難注冊。 所以在asp.net核心默認DI框架中支持此功能嗎?

我想您喜歡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"));

可以隨意自定義這些擴展以適應您的需求。 例如,如果您找不到某些服務的實現,則可以引發異常,如果這樣做會使您感到更安全。

開箱即用的DI不支持它,也不打算這樣做。 內置的IoC容器在設計上保持簡單,從而允許基本的依賴項注入在大多數情況下都有效。

如果您需要高級功能,例如按約定注冊,程序集掃描或裝飾器支持,則必須使用第三方IoC容器,例如Autofac,SimpleInjector,Castle Windsor。

我想提到Scrutor nuget ,它使您可以使用ASP.NET Core依賴注入,但在它上面添加了一個流暢的接口以允許基於約定的配置。

閱讀本文以獲取更多信息。

如果您想添加到@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"));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM