AutoMapper如何根据上下文将对象A映射到不同的对象B

12

寻找所有AutoMapper高手!

我希望能够在运行时根据上下文将对象A映射到对象B,但映射方式会因情况不同而异。特别是,在一种映射情况下忽略某些属性,在另一种映射情况下映射所有属性。

我现在遇到的问题是,Mapper.CreateMap可以在不同的映射情况下成功调用,但一旦调用了CreateMap,就设置了特定类型对的映射,并且后续的CreateMap调用不会改变映射。

我发现一篇博客文章提倡使用Mapper.Reset()来解决这个问题,但Mapper类的静态性质意味着很快就会发生冲突和崩溃。

有没有办法做到这一点?

我认为我需要在每个应用程序域中调用Mapper.CreateMap一次,并稍后能够使用关于包括/排除哪些属性的提示调用Mapper.Map。

现在,我正在考虑通过编写一个保存基于映射配置实例的非静态映射类来更改源代码。性能较差,但线程安全。

我的选择是什么?还能做些什么?Automapper似乎很有前途。


2
@Omu:你和你的“ValueInjecter”开始变得很烦人了。你不必用你的ValueInjecter插件回答每个AutoMapper问题(应该是ValueInjector吧)。我个人因此感到反感,不会考虑使用它,因为这种做法不够专业。 - epitka
3个回答

17

为了补充Jimmy的回答,以下是使用AutoMapper而不使用静态Mapper所需的代码。

自版本4.2.1起,AutoMapper拥有官方支持的非静态映射器和配置(感谢Jimmy!)。

var config = new MapperConfiguration(cfg => {
    cfg.CreateMap<ClassA, ClassB>();
});

var mapper = config.CreateMapper();

在新版本中,有许多其他实用选项(例如配置文件)可用于创建不同的映射器实例。 您可以在官方文档中获取所有详细信息。

(适用于版本4.1.1)

// Configuration
AutoMapper.Mappers.MapperRegistry.Reset();
var autoMapperCfg = new AutoMapper.ConfigurationStore(new TypeMapFactory(), AutoMapper.Mappers.MapperRegistry.Mappers);
var mappingEngine = new AutoMapper.MappingEngine(autoMapperCfg);
autoMapperCfg.Seal();

//Usage example
autoMapperCfg.CreateMap<ClassA, ClassB>();

var b = mappingEngine.Map<ClassB>(a);

(适用于版本 3.2.1)

// Configuration
var platformSpecificRegistry = AutoMapper.Internal.PlatformAdapter.Resolve<IPlatformSpecificMapperRegistry>();
platformSpecificRegistry.Initialize();

var autoMapperCfg = new AutoMapper.ConfigurationStore(new TypeMapFactory(), AutoMapper.Mappers.MapperRegistry.Mappers);
var mappingEngine = new AutoMapper.MappingEngine(autoMapperCfg);

//Usage example
autoMapperCfg.CreateMap<ClassA, ClassB>();

var b = mappingEngine.Map<ClassB>(a);

(适用于2.2.1版本)

// Configuration
var autoMapperCfg = new AutoMapper.ConfigurationStore(new AutoMapper.TypeMapFactory(), AutoMapper.Mappers.MapperRegistry.AllMappers());
var mappingEngine = new AutoMapper.MappingEngine(autoMapperCfg);

//Usage example
autoMapperCfg.CreateMap<ClassA, ClassB>();

var b = mappingEngine.Map<ClassB>(a);

6
Mapper类仅是Configuration和MappingEngine对象的薄包装器。您可以创建Configuration / MappingEngine对象的单独实例(仍使用单例),并使用所选的IoC容器根据需要加载正确的实例。
最好的选择仍然是使用不同的目标类型。真正支持此功能的困难部分在于类型映射的固有层次结构。顶级对象可能具有映射配置文件,而较低级别的对象则没有。中间的一些可能有或没有,等等。

1

对我来说,更好的设计可能是拥有多个目标类(可能继承自一个共同的基类或实现一个共同的接口)。

如果未映射的属性永远不会在其中一种变体中使用,您可以完全省略它们(在编译时保证它们不会被错误地使用),当访问它们时抛出异常(不如编译时保证好,但有时需要实现完整的接口),甚至使用替代值。

例如:

public class Source
{
    public string Name {get;set;}
    public BigEntity {get;set;}

    /* other members */
}

public class SourceDTO
{
    public string Name {get;set;}
    public BigEntity {get;set;}
}

public class SourceSummaryDTO
{
    public string Name {get;set;}
}

或者,你可以这样做:

public class SourceSummaryDTO : SourceDTO
{
    public string Name {get;set;}
    public BigEntity 
    {
        get{throw new NotSupportedException();}
        set{throw new NotSupportedException();}
    }
}

这样,您可以将SourceSummaryDTO传递,就像它是SourceDTO一样。

有条件地填充属性听起来对我来说很麻烦 - 我更喜欢类明确它们包含什么,特别是在数据传输对象方面。

对我来说,Automapper最好的事情是能够验证映射,然后知道目标类上的每个属性都将被填充。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接