轻量级 Fluent 风格对象映射器(Fluent Object Mapper),支持多层级映射、链式 API、AOT、DI 注入、运行时映射规则配置。
$ dotnet add package Ling.Mapper轻量级、高性能的 .NET 对象映射库
Ling.Mapper 是一个基于 Expression Tree 的对象映射库,提供简洁的 Adapt API 和高效的性能。支持复杂对象映射、集合转换、循环引用检测,以及灵活的运行时配置选项。
source.Adapt<Target>()| 场景 | 吞吐量 | 说明 |
|---|---|---|
| 简单对象映射 | 975K ops/sec | 属性较少的对象 |
| 复杂对象映射 | 148K ops/sec | 嵌套对象、集合 |
| 集合映射 | 8.5M elements/sec | 集合元素处理速度 |
测试环境: .NET 10.0, 8核 CPU, 支持 .NET 6.0/8.0/9.0/10.0
dotnet add package Ling.Mapper
或
Install-Package Ling.Mapper
using Ling.Mapper.Extensions;
public class UserSource
{
public int Id { get; set; }
public string Name { get; set; }
}
public class UserTarget
{
public int Id { get; set; }
public string Name { get; set; }
}
// 基础映射
var source = new UserSource { Id = 1, Name = "张三" };
var target = source.Adapt<UserTarget>();
Console.WriteLine($"{target.Id} - {target.Name}");
// 输出: 1 - 张三
// List 映射
var sourceList = new List<UserSource>
{
new UserSource { Id = 1, Name = "张三" },
new UserSource { Id = 2, Name = "李四" }
};
var targetList = sourceList.Adapt<List<UserTarget>>();
Console.WriteLine($"映射了 {targetList.Count} 条记录");
// 输出: 映射了 2 条记录
public class UserSource
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
public class UserTarget
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName { get; set; }
public bool IsAdult { get; set; }
}
// 使用回调处理映射后的逻辑
var source = new UserSource
{
Id = 1,
FirstName = "John",
LastName = "Doe",
Age = 30
};
var target = source.Adapt<UserTarget, UserSource>((dest, src) =>
{
// dest: 映射后的目标对象
// src: 原始源对象
dest.FullName = $"{src.FirstName} {src.LastName}";
dest.IsAdult = src.Age >= 18;
});
Console.WriteLine($"{target.FullName}, IsAdult: {target.IsAdult}");
// 输出: John Doe, IsAdult: True
var sourceList = new List<UserSource>
{
new UserSource { Id = 1, FirstName = "John", LastName = "Doe", Age = 30 },
new UserSource { Id = 2, FirstName = "Jane", LastName = "Smith", Age = 25 }
};
// 推荐:使用项级回调,在一次循环内完成映射和回调
int rowNumber = 0;
var targetList = sourceList.Adapt<UserTarget, UserSource>((dest, src) =>
{
// 每个元素映射完成后立即执行
dest.FullName = $"{src.FirstName} {src.LastName}";
dest.IsAdult = src.Age >= 18;
dest.RowNumber = ++rowNumber;
});
// 输出结果
foreach (var item in targetList)
{
Console.WriteLine($"{item.RowNumber}. {item.FullName}, IsAdult: {item.IsAdult}");
}
// 输出:
// 1. John Doe, IsAdult: True
// 2. Jane Smith, IsAdult: True
var sourceList = new List<UserSource>
{
new UserSource { Id = 1, FirstName = "John", LastName = "Doe" },
new UserSource { Id = 2, FirstName = "Jane", LastName = "Smith" }
};
// 使用整体回调,在整个集合映射完成后执行
var targetList = sourceList.Adapt<List<UserTarget>>(list =>
{
// list: 已经映射完成的列表
for (int i = 0; i < list.Count; i++)
{
list[i].FullName += " (Verified)";
}
});
foreach (var item in targetList)
{
Console.WriteLine(item.FullName);
}
// 输出:
// John Doe (Verified)
// Jane Smith (Verified)
// 处理命名差异
var source = new
{
id = 1,
user_name = "张三",
AGE = 30
};
// 使用 FlexibleOption(忽略大小写 + 忽略下划线)
var target = source.Adapt<UserTarget>(AdaptOptions.FlexibleOption);
可用的选项:
AdaptOptions.Strict // 严格匹配(默认)
AdaptOptions.IgnoreCase // 忽略大小写
AdaptOptions.IgnoreUnderscore // 忽略下划线
AdaptOptions.IgnoreNullValues // null 值不覆盖
AdaptOptions.Default // 忽略大小写 + 下划线
AdaptOptions.FlexibleOption // 别名,同 Default
// 组合使用
var result = source.Adapt<Target>(
AdaptOptions.IgnoreCase | AdaptOptions.IgnoreNullValues
);
public class UserSource
{
public string Name { get; set; }
public string Password { get; set; }
public string CreditCard { get; set; }
public int Age { get; set; }
}
public class UserTarget
{
public string Name { get; set; }
public string Password { get; set; }
public string CreditCard { get; set; }
public int Age { get; set; }
}
var source = new UserSource
{
Name = "张三",
Password = "secret123",
CreditCard = "1111-2222-3333-4444",
Age = 30
};
// 忽略 Password 和 CreditCard 字段
var target = source.Adapt<UserTarget>("Password", "CreditCard");
Console.WriteLine($"Name: {target.Name}");
Console.WriteLine($"Password: {target.Password}"); // null
Console.WriteLine($"CreditCard: {target.CreditCard}"); // null
Console.WriteLine($"Age: {target.Age}"); // 30
特性说明:
public class OrderSource
{
public int Id { get; set; }
public UserSource User { get; set; }
}
public class OrderTarget
{
public int Id { get; set; }
public UserTarget User { get; set; }
}
// 自动处理嵌套对象
var order = new OrderSource
{
Id = 101,
User = new UserSource { Id = 1, Name = "张三" }
};
var orderTarget = order.Adapt<OrderTarget>();
Console.WriteLine($"Order: {orderTarget.Id}, User: {orderTarget.User.Name}");
// 输出: Order: 101, User: 张三
public class OrderSource
{
public int Id { get; set; }
public List<UserSource> Users { get; set; }
}
public class OrderTarget
{
public int Id { get; set; }
public List<UserTarget> Users { get; set; }
}
// 自动处理集合属性
var order = new OrderSource
{
Id = 101,
Users = new List<UserSource>
{
new UserSource { Id = 1, Name = "张三" },
new UserSource { Id = 2, Name = "李四" }
}
};
var orderTarget = order.Adapt<OrderTarget>();
Console.WriteLine($"Order: {orderTarget.Id}, Users: {orderTarget.Users.Count}");
// 输出: Order: 101, Users: 2
public class NodeSource
{
public int Id { get; set; }
public string Name { get; set; }
public NodeSource Related { get; set; }
}
public class NodeTarget
{
public int Id { get; set; }
public string Name { get; set; }
public NodeTarget Related { get; set; }
}
// 创建循环引用
var nodeA = new NodeSource { Id = 1, Name = "Node A" };
var nodeB = new NodeSource { Id = 2, Name = "Node B" };
nodeA.Related = nodeB;
nodeB.Related = nodeA;
// Mapper 自动检测并处理循环引用
var targetA = nodeA.Adapt<NodeTarget>();
Console.WriteLine($"{targetA.Name} -> {targetA.Related.Name}");
// 输出: Node A -> Node B
public class UserSource
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
public class UserTarget
{
public int Id { get; set; }
public string Name { get; set; } = "Default";
public string Description { get; set; }
}
var source = new UserSource
{
Id = 1,
Name = null, // null 值
Description = "Test"
};
// 使用 IgnoreNullValues 选项
var target = source.Adapt<UserTarget>(AdaptOptions.IgnoreNullValues);
Console.WriteLine($"Id: {target.Id}");
Console.WriteLine($"Name: {target.Name}"); // 输出: Default (保留原值)
Console.WriteLine($"Description: {target.Description}"); // 输出: Test
// 基础映射
public static TDestination Adapt<TDestination>(this object? source)
// 带选项映射
public static TDestination Adapt<TDestination>(this object? source, AdaptOptions options)
// 忽略字段映射
public static TDestination Adapt<TDestination>(this object? source, string firstIgnore, params string[] otherIgnores)
// 单个对象带回调
public static TDestination Adapt<TDestination, TSource>(this TSource source, Action<TDestination, TSource> afterMapItem)
// 集合项级回调(推荐)
public static List<TTargetItem> Adapt<TTargetItem, TSourceItem>(this IEnumerable<TSourceItem> source, Action<TTargetItem, TSourceItem> afterMapItem)
where TTargetItem : class, new()
// 集合整体回调
public static TDestination Adapt<TDestination>(this object? source, Action<TDestination> afterMap)
[Flags]
public enum AdaptOptions
{
Strict = 0, // 严格匹配
IgnoreCase = 1 << 0, // 忽略大小写
IgnoreUnderscore = 1 << 1, // 忽略下划线
IgnoreNullValues = 1 << 2, // 忽略 null 值
Default = IgnoreCase | IgnoreUnderscore, // 默认选项
FlexibleOption = IgnoreCase | IgnoreUnderscore // 灵活选项(别名)
}
运行完整测试套件:
cd tests/Ling.Mapper.Tests
dotnet run
测试涵盖:
source.Adapt<Target>("Field1", "Field2")本项目采用 MIT 许可证 - 详见 LICENSE 文件
如果这个项目对你有帮助,请给个 Star!