Регистрация. NET Базовый синглтон сервис с аннотацией - PullRequest
1 голос
/ 25 апреля 2020

Есть ли способ автоматической передачи (автоматической регистрации) одноэлементных сервисов в C# DI-контейнер ( Microsoft.Extensions.DependencyInjection ) с помощью аннотации?

Например, что-то вроде Параметр ProvidedIn в аннотации @Injectable () в Angular, аннотация @injectable () в Inversify JS (Node.js) , автоматическое подключение в пружина (Java) или автоматическое подключение в Symfony каркас (PHP) ?

См. Angular пример ниже:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class UserService {
}

В настоящее время я должен вручную добавить каждую одиночную службу в ServiceCollection (и иногда забыть сделать это):

internal static ServiceProvider SetupDi()
{
    return new ServiceCollection() // Microsoft.Extensions.DependencyInjection.ServiceCollection.ServiceCollection()
        .AddDbContext<DbContext>()
        .AddSingleton<ServiceA>()
        .AddSingleton<ServiceB>();
}

Требуемое эквивалентное решение будет как то так:

internal static ServiceProvider SetupDi()
{
    return new ServiceCollection() // Microsoft.Extensions.DependencyInjection.ServiceCollection.ServiceCollection()
        .AddDbContext<DbContext>();
}

[Injectable()]
public class ServiceA
{
}

[Injectable()]
public class ServiceB
{
}

Ответы [ 2 ]

1 голос
/ 25 апреля 2020

Вы можете использовать Scrutor для добавления возможностей сканирования сборок в ASP. NET Базовый DI-контейнер.

Подробнее см. В этом блоге https://andrewlock.net/using-scrutor-to-automatically-register-your-services-with-the-asp-net-core-di-container/

1 голос
/ 25 апреля 2020

Нет, и это на самом деле просто дизайн.

Весь смысл DI в том, что сюрпризов в том, как настроена ваша программа: все, что вы настраиваете в ConfigureServices ( в вашем случае, SetupDi) - это именно то, что вы получаете во время выполнения. Используя атрибуты для настройки DI, можно было бы использовать «нелокальные эффекты», и было бы намного сложнее отследить ошибки, вызванные неправильными или неправильно настроенными зависимостями, вызванными ошибочным атрибутом.

(В этом смысле Я не согласен с дизайном Angular - но это не так c).

(я тоже так чувствую. NET Система DI в Core также несовершенна - слишком много необходимых деталей скрыто за DI Методы расширения инъекций, которые вам необходимо использовать ILSpy или Reflector для однорангового подключения.

В качестве обходного пути вы можете"протестировать" ваши службы DI при запуске приложения, чтобы убедиться, что все настроено с помощью размышляя над каждым IService в вашем проекте и пытаясь создать экземпляр реализации.

Вот код, который я использую в моих ASP. NET и ASP. NET Базовых проектах для проверки DI Тщательно сконфигурированный:

internal static void TestAllServices( IServiceProvider sp )
{
    Assembly[] mySolutionAssemblies = new[]
    {
        typeof(FromAssemblyX.Foobar).Assemby,
        typeof(FromAssemblyY.Foobar).Assemby,
        typeof(FromAssemblyZ.Foobar).Assemby,
    };

    List<Type> allServiceInterfaceTypes = mySolutionAssemblies
        .SelectMany( ass => ass.GetTypes() )
        .Where( t => t.IsInterface && t.IsPublic )
        .Where( /* Filter out interfaces you don't want to verify here */ )
        .ToList();

    foreach( Type serviceInterfaceType in serviceInterfaceTypes )
    {
        try
        {
            Object implementation = sp.GetRequiredService( serviceInterfaceType );
            if( implementation is IDisposable disp ) disp.Dispose();
        }
        catch( Exception ex )
        {
            // Log an error or throw or set a breakpoint here
        }
    }
}

Примечания:

  • Не передавайте свои "настоящие" IServiceProvider в TestAllServices - вместо этого создайте отдельный экземпляр IServiceProvider, потому что это Метод будет использовать любую реализацию, которая реализует IDisposable, даже если они одиночные.
  • Я рекомендую поместить этот код в #if DEBUG, чтобы он не go в производстве.
  • Существенный недостаток. NET Система DI по умолчанию в Core состоит в том, что невозможно статически отличить реализации "определенных" сервисов от одиночных и переходных сервисов - вы можете обойти это, добавив маркерные интерфейсы в реализации сервисов и обрабатывая их. соответственно.

Еще одно примечание:

Если бы вы действительно хотели, вы можете использовать ту же самую технику allServiceInterfaceTypes, описанную выше, внутри вашего метода ConfigureServices перечислять по всему интерфейсу и обнаруживать все типы, реализующие каждый интерфейс, а также обнаруживать любые пользовательские атрибуты этих типов и автоматически регистрировать их в контейнере DI - но я не рекомендую делать это, потому что отражение медленнее, чем при использовании DI по назначению - и конфигурировать такие вещи, как фабричные методы, было бы намного сложнее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...