Lbxy.ListTableBridge
1.0.2
dotnet add package Lbxy.ListTableBridge --version 1.0.2
NuGet\Install-Package Lbxy.ListTableBridge -Version 1.0.2
<PackageReference Include="Lbxy.ListTableBridge" Version="1.0.2" />
<PackageVersion Include="Lbxy.ListTableBridge" Version="1.0.2" />
<PackageReference Include="Lbxy.ListTableBridge" />
paket add Lbxy.ListTableBridge --version 1.0.2
#r "nuget: Lbxy.ListTableBridge, 1.0.2"
#:package Lbxy.ListTableBridge@1.0.2
#addin nuget:?package=Lbxy.ListTableBridge&version=1.0.2
#tool nuget:?package=Lbxy.ListTableBridge&version=1.0.2
ListTableBridge
高性能、强类型的 DataTable ↔ List<T> 双向转换工具库。
支持基于特性(XafDisplayName / DisplayName / Display)的列名映射、丰富的数据类型转换以及枚举特性解析,适用于 DevExpress XAF 等场景下的实体与 DataTable 交互。
特性概览
🔁 双向转换
List<T> → DataTableDataTable → List<T>
🏷 特性驱动的列名映射(属性级)
- 优先级:
XafDisplayName>DisplayName>Display> 属性原始名称 - 自动处理属性上的显示名特性(无需引用 DevExpress 程序集,只依赖运行时已有特性)
- 优先级:
🧩 丰富的数据类型支持
- 基础类型:
int,long,short,byte,float,double,decimal,bool,char,string,DateTime - 可空类型:
int?,double?,decimal?等 - 枚举类型:支持字符串与数值双向转换
- 特殊类型:
Guid,DateTimeOffset,TimeSpan - 集合/复杂对象(通过序列化等策略封装在测试中验证)
- 基础类型:
🧠 枚举元素特性解析(可控开关)
- 支持从枚举元素上的
XafDisplayName/DisplayName/Display中读取显示名 - 支持从显示名或名称反向解析为枚举值
- 通过
enableElementFeatureReading参数控制是否开启,默认 false 以保持向后兼容性
- 支持从枚举元素上的
🚀 性能优化
- 基于表达式树与缓存的反射访问(
PropertyInfo→ Getter/Setter 委托) - 类型元数据缓存,避免重复反射
- 测试中对 10,000 条数据转换的性能进行了校验
- 基于表达式树与缓存的反射访问(
✅ 测试完备
- 使用 xUnit 单元测试
- 覆盖基础类型、数值子类型、可空组合、枚举特性、字符串边界等多种场景
安装
从 NuGet 安装:
dotnet add package ListTableBridge
或在 .csproj 中添加:
<ItemGroup>
<PackageReference Include="ListTableBridge" Version="1.0.0" />
</ItemGroup>
目标框架支持:
- .NET Framework 4.5+
- .NET Standard 2.0+
快速开始
1. List<T> → DataTable(静态方法)
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using DevExpress.Persistent.Base; // 示例,实际由你的项目提供
using ListTableBridge;
public class Customer
{
[XafDisplayName("客户编号")]
public int Id { get; set; }
[DisplayName("客户名称")]
public string Name { get; set; }
[Display(Name = "状态")]
public CustomerStatus Status { get; set; }
}
public enum CustomerStatus
{
[XafDisplayName("启用")]
Active = 1,
[XafDisplayName("停用")]
Inactive = 2
}
var customers = new List<Customer>
{
new Customer { Id = 1, Name = "张三", Status = CustomerStatus.Active },
new Customer { Id = 2, Name = "李四", Status = CustomerStatus.Inactive }
};
// 默认:属性列名使用 XafDisplayName / DisplayName / Display / 属性名
var table = ListTableBridge.ListTableBridge.ToDataTable(customers);
生成 DataTable 的列名示例:
Id列:"客户编号"(来自XafDisplayName)Name列:"客户名称"(来自DisplayName)Status列:"状态"(来自Display)
2. DataTable → List<T>(静态方法)
using System.Data;
using ListTableBridge;
DataTable table = /* 来自数据库或其他来源 */;
// 列名可以是属性名或特性定义的显示名
var customers = ListTableBridge.ListTableBridge.ToList<Customer>(table);
列名匹配规则:
- 列名在以下集合内任意一个即可匹配到属性:
XafDisplayName值DisplayName值Display值- 属性原始名称
- 匹配大小写不敏感。
3. 扩展方法:ToDataTable / ToList / IDataReader.ToList
除了直接调用 ListTableBridge.ListTableBridge 上的静态方法之外,你也可以使用扩展方法,让调用更自然:
using System.Collections.Generic;
using System.Data;
using ListTableBridge;
var list = new List<Customer>
{
new Customer { Id = 1, Name = "张三", Status = CustomerStatus.Active },
new Customer { Id = 2, Name = "李四", Status = CustomerStatus.Inactive }
};
// 1) 集合 → DataTable
DataTable table = list.ToDataTable();
// 2) DataTable → List<T>
List<Customer> customers = table.ToList<Customer>();
// 3) IDataReader → List<T>(流式读取,不需要中间 DataTable)
using (var reader = table.CreateDataReader())
{
List<Customer> customersFromReader = reader.ToList<Customer>();
}
// 4) 异步版本(基于 Task 封装)
DataTable tableAsync = await list.ToDataTableAsync();
List<Customer> customersAsync = await tableAsync.ToListAsync<Customer>();
using (var reader = table.CreateDataReader())
{
List<Customer> customersFromReaderAsync = await reader.ToListAsync<Customer>();
}
所有扩展方法都支持可选参数:
TypeMappingConfig config:自定义类型转换规则bool enableElementFeatureReading:控制是否从枚举元素特性中读取显示名
4. 部分列映射示例(includeColumns / columnFilter)
在某些场景下,你只希望映射实体中的部分属性,可以使用以下两种方式:
using System.Collections.Generic;
using System.Data;
using ListTableBridge;
var list = new List<Customer>
{
new Customer { Id = 1, Name = "张三", Status = CustomerStatus.Active },
new Customer { Id = 2, Name = "李四", Status = CustomerStatus.Inactive }
};
// 1) includeColumns:按列名(属性名或显示名)精确指定
DataTable onlyIdAndName = ListTableBridge.ListTableBridge.ToDataTable(
list,
new[] { "Id", "Name" } // 也可以使用显示名,如 "客户编号", "客户名称"
);
// 2) columnFilter:按属性元数据动态筛选
DataTable onlyId = ListTableBridge.ListTableBridge.ToDataTable(
list,
property => property.Name == nameof(Customer.Id)
);
// 3) DataTable → List<T> 时同样支持部分列映射
List<Customer> fromIncludeColumns = ListTableBridge.ListTableBridge.ToList<Customer>(
onlyIdAndName,
new[] { "Id", "Name" }
);
List<Customer> fromFilter = ListTableBridge.ListTableBridge.ToList<Customer>(
onlyIdAndName,
property => property.Name == nameof(Customer.Id)
);
说明:
includeColumns支持属性名、XafDisplayName、DisplayName、Display以及ColumnMappingAttribute指定的列名,大小写不敏感;- 当
includeColumns中包含在类型或 DataTable 中不存在的列名时,将抛出ArgumentException,并在消息中标明缺失列名; columnFilter通过PropertyInfo进行筛选,便于结合自定义特性做按标签导出;- 原有不带
includeColumns/columnFilter的重载保持行为不变,仍然映射所有可映射属性。
枚举元素特性读取(enableElementFeatureReading)
对于 枚举字段本身(即列类型为字符串时),可以控制是否使用枚举元素上的特性作为字符串值。
List<T> → DataTable
var table = ListTableBridge.ListTableBridge.ToDataTable(
customers,
enableElementFeatureReading: true
);
行为:
当
enableElementFeatureReading == true时:- 如果枚举元素上存在
XafDisplayName/DisplayName/Display,写入其显示名 - 否则写入枚举名称(
Active,Inactive)
- 如果枚举元素上存在
当
enableElementFeatureReading == false(默认):- 始终写入枚举名称(
Active,Inactive),不读取枚举元素特性
- 始终写入枚举名称(
DataTable → List<T>
var customers = ListTableBridge.ListTableBridge.ToList<Customer>(
table,
enableElementFeatureReading: true
);
行为:
枚举列为字符串时,解析顺序:
- 尝试按枚举 名称 解析(如
"Active") - 失败时,按
XafDisplayName/DisplayName/Display定义的显示名匹配 - 如果仍失败,最后再交给
Enum.Parse抛出一致的异常
- 尝试按枚举 名称 解析(如
当
enableElementFeatureReading == false:- 仅按名称或数值解析,不使用枚举元素特性。
类型映射与空值处理
内部通过 TypeMappingConfig 提供统一的类型转换:
DBNull.Value→null(对于引用类型和可空值类型)- 非可空值类型在遇到
null/DBNull时使用默认值(例如0,false) - 枚举:
- 字符串 →
Enum.Parse - 数值 → 转为枚举底层类型,再通过
Enum.ToObject
- 字符串 →
- 特殊类型:
Guid:支持Guid或字符串DateTimeOffset:支持DateTimeOffset,DateTime, 字符串TimeSpan:支持TimeSpan, 字符串
你可以通过注册自定义转换器扩展 TypeMappingConfig:
TypeMappingConfig.Default.Register(
typeof(MyCustomType),
v => MyCustomType.Parse(v.ToString())
);
性能说明
项目中包含性能测试用例(基于 xUnit):
- 使用 10,000 条记录进行
List<T> → DataTable → List<T>循环测试 - 内部使用表达式树编译 Getter/Setter 并缓存
TypeMetadata - 典型场景下在普通开发机上可满足毫秒级转换需求(具体耗时取决于对象复杂度和运行环境)
测试
运行全部单元测试:
dotnet test .\ListTableBridge.sln
当前测试覆盖的主要内容:
- 基础类型与可空类型双向转换
- 各数值子类型(
int,long,short,byte,float,double,decimal)与可空组合 DBNull与可空类型映射- 枚举字符串与数值转换
XafDisplayName与DisplayName/Display的优先级验证- 枚举元素特性读取开关
enableElementFeatureReading的开启/关闭行为 - 特殊字符串(Unicode、长文本等)场景
版本与兼容性建议
- 初始版本建议:
1.0.0 - 后续版本遵循 Semantic Versioning:
- BREAKING CHANGE(破坏性变更,如签名变更)→ 主版本号
MAJOR+1 - 新特性、兼容性扩展 → 次版本号
MINOR+1 - Bug 修复、内部实现优化 → 修订号
PATCH+1
- BREAKING CHANGE(破坏性变更,如签名变更)→ 主版本号
许可协议
This project is licensed under the MIT License.
You are free to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the software, subject to the standard MIT terms.
See the full license text in the LICENSE file (or MIT template) in your distribution.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net45 is compatible. net451 was computed. net452 was computed. net46 was computed. net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.5
- No dependencies.
-
.NETStandard 2.0
- System.ComponentModel.Annotations (>= 5.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.