Как объединить открытые общие и частично закрытые общие регистрации в Simple Injector - PullRequest
2 голосов
/ 05 июня 2019

У меня есть следующий интерфейс

public interface IMapper<in TSource, out TDestination>
{
    TDestination Map(TSource source);
}

Со стандартной (резервной) реализацией:

public class DefaultMapper<TSource, TDestination> : IMapper<TSource, TDestination>
{
   ...
}

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

container.Register(typeof(IMapper<,>), MapperAssemblies);
container.RegisterConditional(typeof(IMapper<,>), typeof(DefaultMapper<,>),
    Lifestyle.Singleton,
    c => !c.Handled);

Это позволяет мне писать конкретные сопоставители для конкретных случаев, и это приводит к тому, что экземпляр DefaultMapper возвращается из контейнера всякий раз, когда отсутствует явная регистрация (в MapperAssemblies).Отлично!

Тем не менее, существует много совпадений в коллекциях отображений или других открытых универсальных классах.Я хотел бы избежать написания отдельной реализации для каждой карты, например, из коллекции в коллекцию.Как я могу настроить / настроить свой код и зарегистрировать его, чтобы Simple Injector возвращал:

IMapper<List<TSource>, List<TDestination>> 

или

IMapper<Source<T>, Destination<T>>

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

1 Ответ

1 голос
/ 05 июня 2019

С помощью простого инжектора это может быть связано с определением реализации универсального преобразователя, которая позволяет отображать из списка источников в список назначений, например:

public class ListMapper<TSource, TDestination>
    : IMapper<List<TSource>, List<TDestination>>
{
    private readonly IMapper<TSource, TDestination> mapper;

    public ListMapper(IMapper<TSource, TDestination> mapper) => this.mapper = mapper;

    public List<TDestination> Map(List<TSource> source) =>
        source.Select(this.mapper.Map).ToList();
}

Вы можете зарегистрировать этот преобразователь следующим образом:

container.Register(typeof(IMapper<,>), MapperAssemblies);
container.Register(typeof(IMapper<,>), typeof(ListMapper<,>));

// Register last
container.RegisterConditional(typeof(IMapper<,>), typeof(DefaultMapper<,>),
    Lifestyle.Singleton,
    c => !c.Handled);

Обратите внимание, как ListMapper<,> реализует IMapper<List<TSource>, List<TDestination>>?Это имеет тот же эффект, что и ограничение универсального типа, что позволяет Simple Injector применять условно маппер.

И если вы действительно хотите проявить фантазию и хотите иметь возможность сопоставить любой произвольный тип коллекции с любым другимДля произвольного типа коллекции можно определить следующий универсальный сопоставитель:

public class EnumerableMapper<TIn, TInCollection, TOut, TOutCollection>
    : IMapper<TInCollection, TOutCollection>
    where TInCollection : IEnumerable<TIn>
    where TOutCollection : IEnumerable<TOut>, new()
{
    private readonly IMapper<TIn, TOut> mapper;

    public EnumerableMapper(IMapper<TIn, TOut> mapper) => this.mapper = mapper;

    public TOutCollection Map(TInCollection source) => ...;
}

Несмотря на то, что этот класс содержит два дополнительных универсальных типа поверх абстракции IMapper, Simple Injector по-прежнему может выяснить, что всетипы должны быть.Вы можете зарегистрировать этот тип следующим образом:

container.Register(typeof(IMapper<,>), typeof(EnumerableMapper<,,,>));

Единственная сложная задача - правильно реализовать метод Map, что может быть пугающим, поскольку тип возвращаемого значения может быть что угодно , которое реализуетIEnumerable<T>, хотя вы все еще сможете его создать.

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