Регистрировать типы из сборок сканирования между autofa c и Microsoft-зависимой инъекцией - PullRequest
0 голосов
/ 26 апреля 2020

В последнее время мы прекращаем использовать Autofa c и начинаем использовать Microsoft DependencyInjection в наших веб-и консольных приложениях netcore3.1. Мы по-прежнему любим, что они оба могли бы достичь 1043 * нашего развязанного дизайна. Просто простое решение использовать нативный пакет Microsoft , если он может делать то же самое со сторонними пакетами. Вот коды, которые мы пытаемся перенести, суть в том, что мы уже получаем преимущества, регистрируя типы из сканирующих сборок , и мы надеемся сохранить этот дизайн для снижения стоимости миграции,

RequiredServices .cs

До DI, чтобы предотвратить взрыв регистрации (да, у нас есть более 100 интерфейсов и сервисов), мы не регистрируем все сервисы, вместо этого предпочитаем использовать сборку сканирования.

public interface IRepository { } 
public interface IUserRepository: IRepository { }
public interface ITodoRepository: IRepository { }
// ...
public interface INum100Repository: IRepository { }

public abstract class RepositoryBase: IRepository { } 
public class UserRepository: RepositoryBase, IUserRepository { } 
public class TodoRepository: RepositoryBase, ITodoRepository { } 
// ...
public class Num100Repository: RepositoryBase, INum100Repository { } 

ServiceModule.cs

Старый (мигрировать из): устаревший контейнерный конструктор от Autofa c

protected override void Load(Autofac.ContainerBuilder builder)
{
    base.Load(builder);

    var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
    assemblies.SelectMany(x => x.GetReferencedAssemblies())
        .Where(t => false == assemblies.Any(a =>a.FullName == t.FullName))
        .Distinct()
        .ToList()
        .ForEach(x => assemblies.Add(AppDomain.CurrentDomain.Load(x)));
    var scanAssemblies = assemblies.ToArray();    

    // Core DAL services
    builder.RegisterAssemblyTypes(scanAssemblies)
        .PublicOnly()
        .Where(t => !t.IsInterface)
        .As<IRepository>()
        .AsImplementedInterfaces()
        .InstancePerDependency();
}               

ServiceCollectionExtension.cs

Новое (переход на): дескрипторы службы IServiceCollection от Microsoft.Extensions.DependencyInjection

public static IServiceCollection RegisterAssemblyTypes<T>(this IServiceCollection services, ServiceLifetime lifetime, List<Func<TypeInfo, bool>> predicates = null)
{
    var scanAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
    scanAssemblies.SelectMany(x => x.GetReferencedAssemblies())
        .Where(t => false == scanAssemblies.Any(a => a.FullName == t.FullName))
        .Distinct()
        .ToList()
        .ForEach(x => scanAssemblies.Add(AppDomain.CurrentDomain.Load(x)));

    var interfaces = scanAssemblies
        .SelectMany(o => o.DefinedTypes
            .Where(x => x.IsInterface)
            .Where(x => x != typeof(T))
            .Where(x => typeof(T).IsAssignableFrom(x))
        );

    foreach (var @interface in interfaces)
    {
        var types = scanAssemblies
            .SelectMany(o => o.DefinedTypes
                .Where(x => x.IsClass)
                .Where(x => @interface.IsAssignableFrom(x))
            );

        if (predicates?.Count() > 0)
        {
            foreach (var predict in predicates)
            {
                types = types.Where(predict);
            }
        }

        foreach (var type in types)
        {
            services.TryAdd(new ServiceDescriptor(
                @interface,
                type,
                lifetime)
            );
        }
    }

    return services;
}

Startup.cs

Итак, наконец, мы можно добавить только одну строку в Startup.cs для регистрации типов из текущих сборок.

public void ConfigureServices(IServiceCollection services)
{
    // Other DI
    // ...

    // Our core DAL services DI
    services.RegisterAssemblyTypes<IRepository>(ServiceLifetime.Transient);
}    

Нужно ли что-то еще, чтобы мы были осторожнее с этим переносом? Спасибо за любую рекомендацию или документацию.

1 Ответ

0 голосов
/ 26 апреля 2020

Что я имел в виду в комментарии под вопросом:

Считайте, что у вас есть типы услуг и тип реализации:

interface IService1 { }
interface IService2 { }

class Service : IService1, IService2
{ }

Autofa c код регистрации:

var containerBuilder = new ContainerBuilder();

containerBuilder.RegisterType<Service>()
    .AsImplementedInterfaces()
    // When you use lifetime
    .SingleInstance();

var autofacContainer = containerBuilder.Build();

Регистрационный код Microsoft Extensions DI (аналогичный тому, который вы получили):

var serviceCollection = new ServiceCollection();

serviceCollection.TryAddSingleton<IService1, Service>();
serviceCollection.TryAddSingleton<IService2, Service>();

var serviceProvider = serviceCollection.BuildServiceProvider();

Если вы разрешаете типы услуг:

// Autofac
var obj1 = autofacContainer.Resolve<IService1>();
var obj2 = autofacContainer.Resolve<IService2>();

// autofacResult = true
var autofacResult = object.ReferenceEquals(obj1, obj2);

// MS DI
var obj3 = serviceProvider.GetRequiredService<IService1>();
var obj4 = serviceProvider.GetRequiredService<IService2>();

// serviceProviderResult = false
var serviceProviderResult = object.ReferenceEquals(obj3, obj4);

Это происходит потому, что Autofa c регистрирует предоставленный тип как singleton и как реализация для всех типов услуг, в то время как MS DI регистрирует пару типа услуга / реализация как singleton.

...