如何在 Simple Injector 中组合开放通用和部分封闭通用注册
How to combine open generic and partially closed generic registrations in 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 中)时,它会导致从容器中 return 编辑 DefaultMapper 实例。太棒了!
但是,在映射集合或其他开放通用 类 中存在很多重叠。我想避免为每个映射编写单独的实现,例如,从一个集合到另一个集合。我怎样才能 setup/adjust 我的代码并注册它,这样 Simple Injector 就会 return a:
IMapper<List<TSource>, List<TDestination>>
或
IMapper<Source<T>, Destination<T>>
请求时。我尝试应用部分关闭的注册(根据文档),这适用于某些场景,但我丢失了通用类型,这使得映射变得更加困难。
使用简单注入器,这将是定义一个通用映射器实现的问题,该实现允许从源列表映射到目标列表,如下所示:
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>>
的?这与泛型类型约束具有相同的效果,允许简单注入器有条件地应用映射器。
如果您真的想变得更有趣,并且希望能够将任意集合类型映射到任意其他任意集合类型,您可以定义以下通用映射器:
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) => ...;
}
即使这个 class 在 IMapper
抽象之上包含两个额外的泛型类型,Simple Injector 仍然能够确定所有类型应该是什么。您可以按如下方式注册此类型:
container.Register(typeof(IMapper<,>), typeof(EnumerableMapper<,,,>));
唯一困难的部分是正确实现其 Map
方法,这可能令人望而生畏,因为 return 类型可以是 任何实现 的东西 IEnumerable<T>
,而您应该仍然能够创建它。
我有如下界面
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 中)时,它会导致从容器中 return 编辑 DefaultMapper 实例。太棒了!
但是,在映射集合或其他开放通用 类 中存在很多重叠。我想避免为每个映射编写单独的实现,例如,从一个集合到另一个集合。我怎样才能 setup/adjust 我的代码并注册它,这样 Simple Injector 就会 return a:
IMapper<List<TSource>, List<TDestination>>
或
IMapper<Source<T>, Destination<T>>
请求时。我尝试应用部分关闭的注册(根据文档),这适用于某些场景,但我丢失了通用类型,这使得映射变得更加困难。
使用简单注入器,这将是定义一个通用映射器实现的问题,该实现允许从源列表映射到目标列表,如下所示:
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>>
的?这与泛型类型约束具有相同的效果,允许简单注入器有条件地应用映射器。
如果您真的想变得更有趣,并且希望能够将任意集合类型映射到任意其他任意集合类型,您可以定义以下通用映射器:
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) => ...;
}
即使这个 class 在 IMapper
抽象之上包含两个额外的泛型类型,Simple Injector 仍然能够确定所有类型应该是什么。您可以按如下方式注册此类型:
container.Register(typeof(IMapper<,>), typeof(EnumerableMapper<,,,>));
唯一困难的部分是正确实现其 Map
方法,这可能令人望而生畏,因为 return 类型可以是 任何实现 的东西 IEnumerable<T>
,而您应该仍然能够创建它。