AutoMapper 配置
创建一个 MapperConfiguration
实例并通过构造函数初始化配置:
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Foo, Bar>();
cfg.AddProfile<FooProfile>();
});
MapperConfiguration
实例可以静态存储,放在静态字段中或依赖注入容器中。一旦创建,它就不能被更改/修改。
警告
从 9.0 版本开始,静态 API 不再可用。
配置文件实例
组织映射配置的一个好方法是使用配置文件(Profiles)。创建继承自 Profile
的类,并将配置放入构造函数中:
// 这种方式从版本 5 开始
public class OrganizationProfile : Profile
{
public OrganizationProfile()
{
CreateMap<Foo, FooDto>();
// 在这里使用 CreateMap... 等方法(配置文件的方法与配置方法相同)
}
}
// 在 4.x 版本中这样做 - 从 5.0 起已过时:
// public class OrganizationProfile : Profile
// {
// protected override void Configure()
// {
// CreateMap<Foo, FooDto>();
// }
// }
在早期版本中,使用了 Configure
方法而不是构造函数。从版本 5 开始,Configure()
已过时,将在 6.0 中移除。
配置文件内部的配置仅适用于该文件内的映射。应用于根配置的配置适用于 所有 创建的映射。
自动配置程序集扫描
配置文件可以以多种方式添加到主映射器配置中,可以直接:
cfg.AddProfile<OrganizationProfile>();
cfg.AddProfile(new OrganizationProfile());
或通过自动扫描配置文件:
// 扫描程序集中的所有配置文件
// ... 使用实例方法:
var config = new MapperConfiguration(cfg => {
cfg.AddMaps(myAssembly);
});
var configuration = new MapperConfiguration(cfg => cfg.AddMaps(myAssembly));
// 也可以使用程序集名称:
var configuration = new MapperConfiguration(cfg =>
cfg.AddMaps(new [] {
"Foo.UI",
"Foo.Core"
});
);
// 或使用标记类型表示程序集:
var configuration = new MapperConfiguration(cfg =>
cfg.AddMaps(new [] {
typeof(HomeController),
typeof(Entity)
});
);
AutoMapper
将扫描指定程序集中的所有继承自 Profile
的类并将其添加到配置中。
命名约定
您可以设置源和目标成员的命名约定:
var configuration = new MapperConfiguration(cfg => {
cfg.SourceMemberNamingConvention = LowerUnderscoreNamingConvention.Instance;
cfg.DestinationMemberNamingConvention = PascalCaseNamingConvention.Instance;
});
这将把以下属性相互映射:property_name -> PropertyName
您也可以针对每个配置文件设置这个:
public class OrganizationProfile : Profile
{
public OrganizationProfile()
{
SourceMemberNamingConvention = LowerUnderscoreNamingConvention.Instance;
DestinationMemberNamingConvention = PascalCaseNamingConvention.Instance;
// 放置您的 CreateMap... 等配置
}
}
如果您不需要命名约定,可以使用 ExactMatchNamingConvention
。
替换字符
您还可以在源成员的成员名称匹配期间替换个别字符或整个单词:
public class Source
{
public int Value { get; set; }
public int Ävíator { get; set; }
public int SubAirlinaFlight { get; set; }
}
public class Destination
{
public int Value { get; set; }
public int Aviator { get; set; }
public int SubAirlineFlight { get; set; }
}
我们想替换个别字符,并可能翻译一个单词:
var configuration = new MapperConfiguration(c =>
{
c.ReplaceMemberName("Ä", "A");
c.ReplaceMemberName("í", "i");
c.ReplaceMemberName("Airlina", "Airline");
});
识别前后缀
有时您的源/目标属性会有常见的前后缀,导致您必须做大量自定义成员映射,因为名称不匹配。为了解决这个问题,您可以识别前后缀:
public class Source {
public int frmValue { get; set; }
public int frmValue2 { get; set; }
}
public class Dest {
public int Value { get; set; }
public int Value2 { get; set; }
}
var configuration = new MapperConfiguration(cfg => {
cfg.RecognizePrefixes("frm");
cfg.CreateMap<Source, Dest>();
});
configuration.AssertConfigurationIsValid();
默认情况下,AutoMapper
能识别以 "Get"
为前缀的成员。如果你需要清除这个前缀:
var configuration = new MapperConfiguration(cfg => {
cfg.ClearPrefixes();
cfg.RecognizePrefixes("tmp"); // 示例:添加新的前缀"tmp"
});
全局属性/字段过滤
默认情况下,AutoMapper
会尝试映射所有公开的属性/字段。你可以使用属性/字段过滤器来筛选出需要映射的属性/字段:
var configuration = new MapperConfiguration(cfg =>
{
// 不映射任何字段
cfg.ShouldMapField = fi => false;
// 只映射具有公有或私有getter的方法的属性
cfg.ShouldMapProperty = pi =>
pi.GetMethod != null && (pi.GetMethod.IsPublic || pi.GetMethod.IsPrivate);
});
配置可见性
默认情况下,AutoMapper
仅识别公共成员。它可以映射到具有私有 setter
的属性,但如果整个属性是私有的或内部的,则会跳过内部/私有方法和属性。要指示 AutoMapper
识别其他可见性的成员,请覆盖默认的过滤器 ShouldMapField
和/或 ShouldMapProperty
:
var configuration = new MapperConfiguration(cfg =>
{
// 映射具有公有或内部getter的属性
cfg.ShouldMapProperty = p => p.GetMethod.IsPublic || p.GetMethod.IsAssembly;
cfg.CreateMap<Source, Destination>();
});
现在,映射配置将识别内部/私有成员。
配置编译
由于表达式编译可能比较消耗资源,AutoMapper
会在首次映射时延迟编译类型映射计划。然而,这种行为并非总是理想状态,因此你可以告诉 AutoMapper
直接编译其映射:
var configuration = new MapperConfiguration(cfg => {});
configuration.CompileMappings();
对于几百个映射,这可能需要几秒钟的时间。如果时间远超于此,你可能有一些非常大的执行计划。
编译时间长
编译时间随着执行计划的大小而增加,而这取决于属性的数量及其复杂性。理想情况下,你应该调整你的模型,使每个特定用例都有许多小的 DTO
。但你也可以不改变类结构的情况下减小执行计划的大小。
你可以针对每个成员设置 MapAtRuntime
或全局设置 MaxExecutionPlanDepth
(默认为 1,可以设为 0)。
这些设置会通过替换子对象的执行计划为方法调用来减小执行计划的大小。编译会更快,但映射本身可能会变慢。更多详情可搜索仓库,并使用性能分析工具更好地理解影响。避免使用 PreserveReferences
和 MaxDepth
也有助于提高效率。