AutoMapper如何将对象A映射到对象B,这取决于上下文

时间:2010-04-29 00:49:04

标签: c# automapper

调用所有AutoMapper专家!

我希望能够根据运行时的上下文以不同方式将对象A映射到对象B.特别是,我想在一个映射的情况下忽略某些属性,并在另一个案例中映射所有属性。

我所遇到的是Mapper.CreateMap可以在不同的映射情况下成功调用,但是,一旦调用CreateMap,就会设置特定的一对类型的映射,并且随后可能不会被后续的CreateMap调用更改以不同方式描述映射。

我发现了一篇博文,主张使用Mapper.Reset()来解决问题,但是,Mapper类的静态特性意味着碰撞和崩溃只是时间问题。

有办法做到这一点吗?

我认为我需要的是每个appdomain调用Mapper.CreateMap一次,之后,能够调用Mapper.Map,提示应该包含/排除哪些属性。

现在,我正在考虑通过编写一个包含映射配置实例的非静态映射类来更改源代码。性能不佳,但线程安全。

我有什么选择。可以做些什么? Automapper似乎很有希望。

3 个答案:

答案 0 :(得分:16)

只是为了补充Jimmy的答案,这里是在没有静态Mapper的情况下使用AutoMapper所需的代码

从版本4.2.1开始 Automapper有一个受制裁的非静态映射器和配置(感谢Jimmy!)。

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

var mapper = config.CreateMapper();

新版本中还有许多其他有用的选项(如配置文件),用于创建不同的映射器实例。 您可以在official documentation

中获取所有详细信息

(对于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);

答案 1 :(得分:5)

Mapper类只是Configuration和MappingEngine对象之上的一个瘦包装器。您可以创建Configuration / MappingEngine对象的单独实例(仍然使用单例),并根据需要使用您选择的IoC容器加载正确的容器。

最好的选择仍然是使用不同的目的地类型。关于真正支持此功能的真正困难的部分是类型映射的固有层次性。顶级对象可能具有映射配置文件,而较低级别的对象可能没有。介于两者之间可能有或没有等等。

答案 2 :(得分: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的最大优点是能够验证映射,然后知道将填充目标类的每个属性。