如何在Simple Injector中结合开放式通用注册和部分封闭式通用注册

时间:2019-06-05 13:20:17

标签: c# dependency-injection ioc-container simple-injector

我有以下界面

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

具有默认(备用)实现:

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

并已通过Simple Injector将其注册如下:

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

这使我可以为特定情况编写特定的映射器,并且每当缺少显式注册(在MapperAssemblies中)时,都会从容器返回DefaultMapper的实例。太好了!

但是,映射集合或其他开放的通用类中存在很多重叠。我想避免为每个地图(例如从一个集合到另一个集合)编写单独的实现。如何设置/调整我的代码并注册它,以便Simple Injector返回一个:

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

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

根据要求。我尝试应用部分关闭的注册(根据文档),这在某些情况下适用,但是我丢失了通用类型,这使得映射更加困难。

1 个答案:

答案 0 :(得分:1)

对于Simple Injector,这将是定义一个通用的映射器实现的问题,该实现允许从源列表到目标列表的映射,如下所示:

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>的任何 ,而您仍然应该能够创建它。