2014-01-28 54 views
34

转换比方说,我有两个模型类:AutoMapper从多个来源

public class People { 
    public string FirstName {get;set;} 
    public string LastName {get;set;} 
} 

也有一类电话:

public class Phone { 
    public string Number {get;set;} 
} 

我想转换为PeoplePhoneDto这样的:

public class PeoplePhoneDto { 
    public string FirstName {get;set;} 
    public string LastName {get;set;} 
    public string PhoneNumber {get;set;} 
} 

让我们说在我的控制器我有:

var people = repository.GetPeople(1); 
var phone = repository.GetPhone(4); 

// normally, without automapper I would made 
return new PeoplePhoneDto(people, phone) ; 

我似乎无法找到此场景的任何示例。这可能吗 ?

注意:示例不是真实的,只是针对此问题。

+0

@Andrei虽然我同意这似乎相似,它是在它试图解决问题的差异解决。从这个问题中很难理解它将如何应用于这个问题。 –

+0

为什么不让'PeoplePhoneDto'拥有'People'和'Phone'成员? – crush

+0

因为这不是我想要公开的。 –

回答

56

你不能直接映射许多来源单一目的地 - 你应该逐个应用映射,如Andrew Whitaker答案描述。所以,你必须定义所有映射:

Mapper.CreateMap<People, PeoplePhoneDto>(); 
Mapper.CreateMap<Phone, PeoplePhoneDto>() 
     .ForMember(d => d.PhoneNumber, a => a.MapFrom(s => s.Number)); 

然后通过这些映射的创建目标对象,并应用其他映射到创建的对象。这一步可以用非常简单的扩展方法进行简化:

public static TDestination Map<TSource, TDestination>(
    this TDestination destination, TSource source) 
{ 
    return Mapper.Map(source, destination); 
} 

用法很简单:

var dto = Mapper.Map<PeoplePhoneDto>(people) 
       .Map(phone); 
+1

正是我所期待的!谢谢 ! –

+0

在AutoMapper上有一个抽象[IMapper](https://gist.github.com/ilyapalkin/8822638)将多个源映射到我使用的单个目标。 –

+0

@Sergey Berezovskiy,我创建了映射,在PeoplePhoneDto类中添加了扩展方法,并复制粘贴了您的用法(即,我复制粘贴了所需的所有内容),但是我得到“方法Map没有重载1个参数”错误。我错过了什么?我正在使用Automapper 4.2.1。 – HeyJude

8

你可以使用这个Tuple

Mapper.CreateMap<Tuple<People, Phone>, PeoplePhoneDto>() 
    .ForMember(d => d.FirstName, opt => opt.MapFrom(s => s.Item1.FirstName)) 
    .ForMember(d => d.LastName, opt => opt.MapFrom(s => s.Item1.LastName)) 
    .ForMember(d => d.Number, opt => opt.MapFrom(s => s.Item2.Number)); 

如果你希望你可以使用不同的表示(列表,字典或别的东西),将所有这些模型聚集在一起的多个源模型资源。

上面的代码应该优先放置在某个AutoMapperConfiguration文件中,一次全局设置,然后在适用时使用。

默认情况下,AutoMapper仅支持单个数据源。所以不可能直接设置多个数据源(没有将数据包封装在一个集合中),因为那么我们如何知道如果两个源模型具有相同名称的属性?

有,虽然一些变通方法来实现这一目标:

public static class EntityMapper 
{ 
    public static T Map<T>(params object[] sources) where T : class 
    { 
     if (!sources.Any()) 
     { 
      return default(T); 
     } 

     var initialSource = sources[0]; 

     var mappingResult = Map<T>(initialSource); 

     // Now map the remaining source objects 
     if (sources.Count() > 1) 
     { 
      Map(mappingResult, sources.Skip(1).ToArray()); 
     } 

     return mappingResult; 
    } 

    private static void Map(object destination, params object[] sources) 
    { 
     if (!sources.Any()) 
     { 
      return; 
     } 

     var destinationType = destination.GetType(); 

     foreach (var source in sources) 
     { 
      var sourceType = source.GetType(); 
      Mapper.Map(source, destination, sourceType, destinationType); 
     } 
    } 

    private static T Map<T>(object source) where T : class 
    { 
     var destinationType = typeof(T); 
     var sourceType = source.GetType(); 

     var mappingResult = Mapper.Map(source, sourceType, destinationType); 

     return mappingResult as T; 
    } 
} 

然后:

var peoplePhoneDto = EntityMapper.Map<PeoplePhoneDto>(people, phone); 

但相当诚实,即使我使用AutoMapper对于已经几年我从来没有有需要使用来自多个来源的映射。 例如,在我单视图模型中需要多个业务模型的情况下,我只是将这些模型嵌入到视图模型类中。

因此,在你的情况是这样的:

public class PeoplePhoneDto { 
    public People People { get; set; } 
    public Phone Phone { get; set; } 
} 
+1

所以我必须在做映射之前创建一个元组,我想知道什么是automapper的真正好处......听起来有点矫枉过正。有什么办法可以避免创建另一种类型(元组,dic等)? –

+0

感谢您的回答,这让我对automapper有了很多了解。事情是,当你公开API的时候,你需要仔细地模拟数据,这种方式有时不需要嵌入式模型,因为你将与'领域相关'的问题转移给了消费者,我试图让客户端不需要嵌套类型。如果这是为了我自己的内部使用,我肯定会继续使用嵌入式选项。 –

+1

您建议的'PeoplePhoneDto'看起来不错,但我仍然认为从多个来源映射是有用的,最显着的是映射视图模型。我认为大多数真实世界的场景需要多个来源来构建视图模型。我想你可以创建视图模型来解决问题,但我认为创建视图模型并不关心业务模式的外观是个好主意。 –