jk's notes
  • 配置 Configuration

配置 Configuration

通过构造函数来创建 MapperConfiguration 实例, 以及初始化配置.

var config = new MapperConfiguration(cfg => {
  cfg.CreateMap<Foo, Bar>();
  cfg.AddProfile<FooProfile>();
});

MapperConfiguration 实例可以存储到静态字段中, 或存储在依赖注入容器中. 一旦创建不允许修改.

从 9.0 开始不再支持静态 API.

Profile 实例

有一个管理配置的好方法, 就是使用 Profile 类, 派生它, 然后将配置写在构造函数中.

// 该方法从第 5 版开始被支持
public class OrganizationProfile: Profile {
  public OrganizationProfile() {
    CreateMap<Foo, FooDto>();
    // 这里使用 `CreateMap` 等方法. Profile 的方法与 Configuration 方法一样.
  }
}
// 4.x 的使用办法, 已废除
// public class OrganizationProfile: Profile {
//   protected override void Configure() {
//     CreateMap<Foo, FooDto>();
//   }
// }

早期版本中使用 Configure 方法, 在第 5 版中不再推荐使用, 第 6 版中将会被移除.

profile 中的 configuration 只会作用 profile 中的映射. 全局 configuration 中的配置, 会作用所有创建的 map.

这一句翻译云云不知何谓. 原文如下:

Configuration inside a profile only applies to maps inside the profile. Configuration applied to the root configuration applies to all maps created.

好像了解什么意思了.

搜素程序集与自动配置

有很多种方式加载 Profile, 例如直接法:

cfg.AddProfile<OrganizationProfile>();
cdg.AddProfile(new OrganizationProfile());

或者使用自动搜素

// 搜素程序集中的所有 profile
// 使用实例方法
var config = new MapperConfiluration(cfg => {
  cfg.AddMaps(myAssembly);
});

// 也可以使用程序集名字
var configuration = new MapperConfiguration(
  cfg => cfg.AddMaps(new [] {
    "Foo.UI",
    "Foo.Core"
  })
);

// 或使用程序集类型
var config = new MapperConfiguration(cfg => {
  cfg.AddMaps(new [] {
    typeof(HomeController),
    typeof(Entity)
  })
});

AutoMapper 会自动扫描程序集中, 派生自 Profile 的类, 并加载他们的配置.

命名约定

你可以设置源类型与目标类型的命名约定

var configuration = new MapperConfiguration(cfg => {
  cfg.SourceMemberConvention = LowerUnderscoreNamingConvention.Instance;
  cfg.DestinationMemberConvention = PascalCaseNamingConvention.Instance;
});

这样会构造如此映射: property_name -> PropertyName.

你也可以为每一个 profile 来设置:

public class ...Profile: Profile {
  public ...Profile() {
    SourceMemberConvention = LowerUnderscoreNamingConvention.Instance;
  	DestinationMemberConvention = PascalCaseNamingConvention.Instance;
  }
}

如果不需要命名约定, 可以使用 ExactMatchNamingConvention.

这些 Convention 的具体含义有待确认. 以及还有多少 Convention.

替换字符 (简略)

定义属性名构成的字符集的映射. 例如, 将带有重音符号的属性名, 映射到无中音符号的.

C# 采用 Unicode 字符集进行编码, 可以支持很多字符集.

其核心便是, 调用 ReplaceMemberName("源字符串", "目标字符串") 来实现转换. 例如:

var configuration = new MapperConfiguration(c => {
  c.ReplaceMemberName("Ä", "A");
  c.ReplaceMemberName("í", "i");
  c.ReplaceMemberName("Airlina", "Airline");
});

配置前后缀(简略)

与字符串替换类似. 有些属性名, 带有一些前后缀, 必要时需要将其移除或替换.

例如需要: frmValue -> Value, 则可以

var config = new MapperConfiguration(cfg => {
  cfg.RecognizePrefixes("frm");
  cfg.CreateMap<Source, Dest>();
});

AutoMapper 会识别 Get 前缀, 如果要清除可以使用:

var conf = new MapperConfiguration(cfg => {
  cfg.ClearPrefixes();
  cfg.RecognizePrefixes("frm");
});

全局属性/字段过滤

默认情况下, 映射所有 public 属性/字段. 可以自行配置是否映射:

var config = new MapperConfiguration(cfg => {
  // 不映射所有的字段
  cfg.ShouldMapField = fi => false;
  
  // 映射含有 public, private gatter 的属性
  cfg.ShouldMapProperty = pi => 
    pi.GetMethod != null && (pi.GetMethod.IsPublic || pi.GetMethod.IsPrivate);
})

配置可见性

默认仅仅映射 public 的属性与字段, 如果需要调整可见性, 可以进行配置. 例如:

var conf = new MapperConfiguration(cfg => {
  // 映射具有 public 和 internal getter 的属性
  cfg.ShouldMapProperty = p => p.GetMethod.IsPublic || p.GetMethod.IsAssembly    
});

配置编译

由于表达式编译需要占用一定资源, 因此 AutoMapper 默认采用延迟编译, 在使用的时候才会编译. 可以配置其直接编译:

var conf = new MapperConfiguration(cfg => {});
conf.CompileMappings();

对于数以百计的映射, 这个过程会耗费一定时间.

编译时长

随时间与映射数量的变化, 编译会消耗的时间也会逐步增长. 一般建议调整 DTO 的大小, 尽可能让其更小, 适应某一特定厂家. 也可以配置执行计划的大小, 而不需修改类.

你可以设置每一个成员的 MapAtRuntime 或全局的 MaxExecutionPlanDepth (默认是1, 修改为0).

如此编译会更快, 但映射会变慢.

Last Updated:
Contributors: jk