c# AutoMapper - 在多个位置定义地图

问题描述 投票:0回答:1

我正在开发的一个项目(仍然在 .NET Framework 中,以防万一)将 AutoMapper 用于两个单独的项目,这两个项目使用相同的数据库并共享公共类库项目中的大量相同视图模型类,例如:

// in Project 1
Mapper.Initialize(cfg =>
{
    cfg.CreateMap<A, AModel>()
        .ForMember(dest => dest.Prop1, opt => opt.MapFrom(src => src.PropertyName))
        .ForMember(dest => dest.Prop2, opt => opt.MapFrom(src => src.OtherPropertyName));

    cfg.CreateMap<B2, BModel>()
        .ForMember(dest => dest.Prop1, opt => opt.MapFrom(src => src.PropertyName))
        .ForMember(dest => dest.Prop2, opt => opt.MapFrom(src => src.OtherPropertyName))
        .ForMember(dest => dest.Prop3, opt => opt.MapFrom(src => src.OtherOTHERPropertyName));
}

// in Project 2
Mapper.Initialize(cfg =>
{
    cfg.CreateMap<A, AModel>()
        .ForMember(dest => dest.Prop1, opt => opt.MapFrom(src => src.PropertyName))
        .ForMember(dest => dest.Prop2, opt => opt.MapFrom(src => src.OtherPropertyName));

    cfg.CreateMap<B2, BModel>()
        .ForMember(dest => dest.Prop1, opt => opt.MapFrom(src => src.PropertyName))
        .ForMember(dest => dest.Prop2, opt => opt.MapFrom(src => src.OtherPropertyName))
        .ForMember(dest => dest.Prop3, opt => opt.Ignore());
}

我想将所有重复的映射(其中大部分)移动到配置文件或公共项目中的其他内容中:

public class CommonAutoMapperProfile : Profile
{
    public CommonAutoMapperProfile()
    {
        CreateMap<A, AModel>()
            .ForMember(dest => dest.Prop1, opt => opt.MapFrom(src => src.PropertyName))
            .ForMember(dest => dest.Prop2, opt => opt.MapFrom(src => src.OtherPropertyName));

        CreateMap<B, BModel>()
            .ForMember(dest => dest.Prop1, opt => opt.MapFrom(src => src.PropertyName))
            .ForMember(dest => dest.Prop2, opt => opt.MapFrom(src => src.OtherPropertyName));
    }
}

对于像 A->AModel 这样的完全相同的映射来说这不是问题,但是我正在努力处理像 B->BModel 这样的映射,这些映射甚至有一个

ForMember
调用(在本例中为 Prop3),项目 1 设置和项目2 忽略。它确实应用了该规则,但似乎当调用第二个
CreateMap
时,由
CommonAutoMapperProfile
设置的所有映射都会被完全丢弃:

// in project 1
Mapper.Initialize(cfg =>
{
    cfg.AddProfile<CommonAutoMapperProfile>();
    cfg.CreateMap<B, BModel>()
        .ForMember(dest => dest.Prop3, opt => opt.MapFrom(src => src.OtherOTHERPropertyName));
    // BModel.Prop1 and BModel.Prop2 are still null; BModel.Prop3 is the only one being set
}

我希望它将映射“合并”在一起,即保留

CommonAutoMapperProfile
中定义的属性映射并在顶部添加特定于项目的映射。 这是 AutoMapper 无法做到的事情吗?

我还尝试创建另一个特定于每个项目的 Profile 类,不幸的是,结果相同,无论我是否将其添加到另一个

cfg.AddProfile
调用中:

public class Project1AutoMapperProfile : Profile
{
    public Project1AutoMapperProfile()
    {
        CreateMap<B, BModel>()
            .ForMember(dest => dest.Prop3, opt => opt.MapFrom(src => src.OtherOTHERPropertyName));
    }
}

// elsewhere in Project 1...
Mapper.Initialize(cfg =>
{
    cfg.AddProfile<CommonAutoMapperProfile>();
    cfg.AddProfile<Project1AutoMapperProfile>();
}

或者让它继承自

CommonAutoMapperProfile
:

public class Project1AutoMapperProfile : CommonAutoMapperProfile
{
    public Project1AutoMapperProfile()
    {
        CreateMap<B, BModel>()
            .ForMember(dest => dest.Prop3, opt => opt.MapFrom(src => src.OtherOTHERPropertyName));
    }
}

// elsewhere in Project 1...
Mapper.Initialize(cfg =>
{
    cfg.AddProfile<Project1AutoMapperProfile>();
}

两种情况的结果与第一种情况完全相同:仅应用了

Project1AutoMapperProfile
中的映射

c# .net mapping automapper
1个回答
0
投票

正如评论中提到的,您所拥有的并不是理想的设计。但我明白,有时我们必须处理我们收到的牌(例如,您无权修改模型)。

顾名思义,对同一个映射多次调用

CreateMap
只会让 creating 成为一个新映射。如果您想从现有的基础上进行构建,则必须附加/修改。没有内置的工具来获取现有地图并对其进行修改,但我们可以偷偷摸摸地自己实现它。


注意:我仍然不建议这样做。如果您可以分解模型,请这样做并使用推荐的模型继承


也就是说,下面的代码可以工作。对于当前版本的 AutoMapper。对于这个具体案例。确保您有良好的测试覆盖率,以防将来出现故障,或者对于某些更复杂的模型。

public class B { public int Prop1; public int Prop2; }
public class BModel { public int PropB1; public int PropB2; }

public class CommonProfile : Profile
{
  public CommonProfile() 
  {
    // Map Prop1, ignore Prop2
    CreateMap<B, BModel>()
      .ForMember(d => d.PropB1, o => o.MapFrom(s => s.Prop1))
      .ForMember(d => d.PropB2, o => o.Ignore());
  }
  
  protected IMappingExpression<T,TT> CreateOrGetMap<T, TT>() 
  {
    // Sneaky access the un-exposed existing configs.
    var existing = ((IProfileConfiguration)this).TypeMapConfigs
      .Where(c => c.SourceType == typeof(T) && c.DestinationType == typeof(TT))
      .FirstOrDefault();
      
    // If found existing map, return it. Otherwise, create new one.
    return (existing as IMappingExpression<T,TT>) ?? CreateMap<T, TT>();
  }
}

然后我们创建一个派生配置文件,它将修改公共配置文件中的映射。

public class Proj2Profile : CommonProfile {
  public Proj2Profile() 
  {
    // Since there's already a map configured in the parent profile, 
    // this will modify it.
    CreateOrGetMap<B, BModel>()
      .ForMember(d => d.PropB2, o => o.MapFrom(s => s.Prop2));
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.