【问题标题】:Automapper : how to eleminate repetitive mapping?Automapper:如何消除重复映射?
【发布时间】:2018-02-23 20:57:21
【问题描述】:

我有以下表格结构。这两个表有很多共同的属性超过 20 我只是列出两个。我也有10张类似的桌子。这就是表在数据库中的样子。有超过 10 个具有相似属性的混凝土表,并且没有以任何方式相互连接。我正在使用 POCO 生成器从我的数据库中生成类。

public class A
 {
    public string name {get;set;}
    public string address {get;set;}
    public string AId {get;set;}
  }

public class B
 {
    public string name {get;set;}
    public string address {get;set;}
    public string BId {get;set;}
  } 

我有以下视图模型:

public class BaseViewModel
 {
    public string Fullname {get;set;}
    public string Fulladdress {get;set;}
   }

public class AviewModel : BaseViewModel
 {
    public string AId {get;set;}
  }

public class BViewModel : BaseViewModel
 {
     public string BId {get;set;}
  } 

当我创建映射时,我必须为我创建的每个视图模型重复所有这些。

config.CreateMap<A, AviewModel>()
.ForMember(dest => Fulladdress, opt => opt.MapFrom(src =>.address))
.ForMember(dest => Fullname, opt => opt.MapFrom(src =>.name)).ReverseMap();


config.CreateMap<B, BviewModel>()
.ForMember(dest => Fulladdress, opt => opt.MapFrom(src =>.address))
.ForMember(dest => Fullname, opt => opt.MapFrom(src =>.name)).ReverseMap();

是否有可能减少我可能不得不做的重复映射?

【问题讨论】:

  • 为什么不为实体提取和使用基类,就像为视图模型所做的一样。
  • @IvanStoev 所以我正在使用 POCO 生成的类,我不想手动更改类中的任何内容。所以这意味着每次有人更新 .tt 文件时,我的构造就消失了。
  • 其实你是对的似乎是最好的解决方案
  • 大声笑,很高兴在研究非基类解决方案之后听到这个消息。
  • 大声笑我刚刚看了你的新答案,这可能是一个更简单的解决方案。非常感谢:)

标签: c# asp.net entity-framework automapper poco


【解决方案1】:

您可以将通用映射代码移至辅助泛型方法。您将TDestination 类型限制为从BaseViewModel 派生的类,从而允许在ForMember 方法中访问目标成员。对于源映射,您将使用MapFrom 重载接受string 属性名称:

public static class CommonMappings
{
    public static IMappingExpression<TSource, TDestination> MapToBaseViewModel<TSource, TDestination>(this IMappingExpression<TSource, TDestination> map)
        where TDestination : BaseViewModel
    {
        return map
            .ForMember(dest => dest.Fulladdress, opt => opt.MapFrom("address"))
            .ForMember(dest => dest.Fullname, opt => opt.MapFrom("name"));
    }
}

那么你可以这样使用它:

config.CreateMap<A, AViewModel>()
    .MapToBaseViewModel()
    // ... specific mappings
    .ReverseMap();

config.CreateMap<B, BViewModel>()
    .MapToBaseViewModel()
    // ... specific mappings
    .ReverseMap();

更新: 事实证明,AutoMapper 6.1.1 最新的自动反向映射适用于 MapFrom 的 lambda 重载,但不适用于 string 重载(在 AutoMapper 5它根本不起作用)。因此,在它得到修复之前,您可以使用以下 MapFrom(string) 替换:

public static class AMExtensions
{
    public static void From<TSource, TDestination, TMember>(this IMemberConfigurationExpression<TSource, TDestination, TMember> opt, string name)
    {
        var parameter = Expression.Parameter(typeof(TSource), "src");
        var body = Expression.PropertyOrField(parameter, name);
        var selector = Expression.Lambda(body, parameter);
        opt.MapFrom((dynamic)selector);
    }
}

这意味着您需要将原始解决方案中的MapFrom 调用替换为From,因为我们不能为扩展方法赋予相同的名称,因为它的优先级低于具体的接口方法。

与基类方法相比,可能需要付出太多努力。但在您无法控制实体类的设计时很有用。

【讨论】:

  • 这也有效,所以我在 POCO 生成的类之外创建了一个基类,并将其配置为忽略公共字段并从我的基类继承。这实际上真的很好,我这样做了。对于其他小的重复映射,我仍然会使用这个 commonMapping 类。非常感谢!!
  • 虽然很聪明,但这更像是一种 hack :) 我只会在确实无法创建源基类/接口时使用它。
  • 是的,我也只是对它进行了更多测试,似乎 reverseMap() 不起作用?它获取数据但不为我发布?
  • @hamadkh 你是对的。但是您的原始(重复)代码也会发生同样的情况,因此这种方法不会破坏任何东西。
  • 哇,前面的评论适用于 AM 版本 5,但在 AM 6 中,自动反向映射适用于使用 lambda MapFrom 的其他方法,不适用于 string "MapFrom` :( 嗯,吉米,吉米-为什么...
【解决方案2】:

您需要源类的基类。您在基本源类和目标类之间创建一个映射。您将该映射包含在派生类的映射中。这将允许您重用配置。 The docs。对于简单的情况,您可以使用 As 而不是 Include。

【讨论】:

    【解决方案3】:

    您在模型的属性上设置了一个属性。 这包含它在源对象上映射的属性的名称。

    然后您创建一个接受目标对象和源对象的通用方法,该方法在设置属性的属性上查找 customattribute,以及目标对象上的属性(反之亦然),然后设置值。

    您甚至可以通过询问它是否是类属性来处理嵌套对象。

    【讨论】:

    • 因此,您建议的不是重复的流畅配置,而是重复的数据注释(自定义属性)。有什么区别——你仍然需要做很多重复的工作。
    • 不是吗?您只需为类的属性定义一次属性。到源类型,反之亦然。与通用类为您处理映射相比,您发送给映射器的类更少。不涉及重复任务。
    猜你喜欢
    • 2012-04-19
    • 2017-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-16
    • 2011-01-12
    • 2022-01-18
    • 1970-01-01
    相关资源
    最近更新 更多