Пакетная регистрация зависимостей в asp.net core

200
17 ноября 2018, 01:10

Внимание! Это краткий перевод вопроса Implement dependency injection outside of Startup.cs

Есть проект asp.net core со множеством репозиториев и сервисов, в файле Startup.cs огромная нечитаемая портянка регистрации:

services.AddTransient<IContactRepository, ContactRepository>();
services.AddTransient<ICityRepository, CityRepository>();
//..... and so on
services.AddTransient<ICityService, CityService>();
services.AddTransient<IContactServiceBase, ContactServiceBase>();
//..... and so on

Можно ли каким-то образом зарегистрировать все эти зависимости более коротким способом?

Answer 1

Подобные вещи в англоязычной литературе называются batch registration, иногда применяется ещё термин assembly scanning.

В asp.net core есть возможность заменить встроенный DI на autofac, который умеет сканировать сборки и регистрировать типы:

var assembly = Assembly.GetExecutingAssembly();
builder.RegisterAssemblyTypes(assembly)
       .Where(t => t.Name.EndsWith("Repository"))
       .AsImplementedInterfaces();

А встроенный DI (до сих пор - даже в core 2.1) не поддерживает сканирование сборок :(

Однако вы можете воспользоваться проектом Scrutor: установив его из nuget вы сможете регистрировать зависимости следующим образом:

var collection = new ServiceCollection();
collection.Scan(scan => scan
    .FromAssemblyOf<ITransientService>()
        .AddClasses(classes => classes.AssignableTo<ITransientService>())
            .AsImplementedInterfaces()
            .WithTransientLifetime()
        .AddClasses(classes => classes.AssignableTo<IScopedService>())
            .As<IScopedService>()
            .WithScopedLifetime());

Если вы не хотите подключать дополнительные пакеты, то можете написать небольшой экстеншн:

public static void AddScopedFromAssembly(this IServiceCollection services, Assembly assembly)
{
    var allServices = assembly.GetTypes().Where(p =>
        p.GetTypeInfo().IsClass &&
        !p.GetTypeInfo().IsAbstract);
    foreach (var type in allServices)
    {
        var allInterfaces = type.GetInterfaces();
        var mainInterfaces = allInterfaces.Except
                (allInterfaces.SelectMany(t => t.GetInterfaces()));
        foreach (var itype in mainInterfaces)
        {
            services.AddScoped(itype, type); // if you want you can pass lifetime as a parameter
        }
    }
}

Использовать так:

 services.AddScopedFromAssembly(assembly);
READ ALSO
UnhandledExceptionHandler на C# вне Visual Studio

UnhandledExceptionHandler на C# вне Visual Studio

Есть проектНачальство, спустя более 40к строк кода решило добавить логирование

167
Не открывается .xlsx в datagrid

Не открывается .xlsx в datagrid

Возникла проблема с WPF: в приложении у меня при нажатии кнопки должен открыться файл с расширениемxlsx и его содержимое перенестись в DataGrid,...

173