C# 使用泛型委托与反射将 DataTable 高效转换为实体集合

键盘旁的猫
键盘旁的猫
发布时间:2026-10-15| 5人浏览过
原创

在 .NET 开发中,将数据库查询结果(如 DataSet/DataTable)转换为业务实体对象是常见需求。传统的反射直接赋值往往需要处理繁琐的类型转换问题,尤其是当属性类型为整数或布尔值时,直接调用 Set 方法容易抛出异常。本文将演示如何利用 C# 的泛型委托(Generic Delegate)与反射机制,动态创建匹配的 Setter 代理,从而优雅地解决多类型数据的自动映射难题。

实现目标与核心原理

我们的目标是编写一个通用静态方法 ToModel<T>.GetDelegate_ToModelList(DataTable),能够遍历 DataTable 的每一行,利用反射找到实体类对应属性的 Set 方法,并通过委托执行赋值操作。核心难点在于:Delegate.CreateDelegate 要求委托类型必须与方法签名严格匹配。如果委托定义为接收 string 参数,则无法赋值给 int 或 bool 类型的属性。

解决方案是利用 Type.MakeGenericType() 在运行时动态生成泛型委托类型,例如 SetValue<int> 或 SetValue<bool>,并确保传入的数据经过 Convert.ChangeType 预处理后,再通过 DynamicInvoke 进行调用。

准备工作:实体定义与依赖

假设我们有一个名为 TestModel 的实体类,包含不同数据类型:

public class TestModel 
{
    public int ID { get; set; }
    public string Name { get; set; }
    public bool IsDeleted { get; set; }
}

我们需要确保项目中引用了 System.Data、System.Reflection 和 System.Collections.Generic 命名空间。

第1步:定义泛型委托类型

首先,定义一个接受任意类型参数的泛型委托。这为我们后续动态实例化特定类型的委托奠定了基础:

public delegate void SetValue<TValue>(TValue value);

第2步:封装委托创建逻辑

创建一个辅助方法,根据属性名称获取其数据类型,并动态创建匹配的委托实例。这里使用 MakeGenericType 填充泛型参数:

/// <summary>
/// 创建指定属性 Setter 的动态委托
/// </summary>
private static Delegate CreateSetDelegate(T model, string propertyName) 
{
    // 1. 获取属性信息及 Set 方法
    PropertyInfo propInfo = typeof(T).GetProperty(propertyName);
    MethodInfo mi = propInfo.GetSetMethod();
    
    if (mi == null) return null;

    // 2. 获取属性的实际数据类型
    Type propertyType = propInfo.PropertyType;
    
    // 3. 动态构建泛型委托类型,例如 SetValue<int>
    Type genericDelType = typeof(SetValue<>); 
    Type specificDelType = genericDelType.MakeGenericType(propertyType);
    
    // 4. 创建委托实例
    return Delegate.CreateDelegate(specificDelType, model, mi);
}

/// <summary>
/// 获取属性对应的 Type 对象
/// </summary>
private static Type GetPropertyType(string propertyName) 
{
    return typeof(T).GetProperty(propertyName).PropertyType;
}

第3步:实现批量转换主函数

在主转换逻辑中,遍历 DataTable,为每一行创建实体,并利用上一步创建的委托进行赋值。关键点在于使用 DynamicInvoke 代替强类型委托调用,以便传递经过类型转换后的参数:

public static IList<T> GetDelegate_ToModelList(DataTable dt)
{
    IList<T> list = new List<T>;
    
    if (dt == null || dt.Rows.Count < 1) 
        return list;

    foreach (DataRow dr in dt.Rows)
    {
        T model = new T();
        
        foreach (DataColumn dc in dt.Columns)
        {
            string propName = dc.ColumnName;
            
            // 获取该列对应的委托
            Delegate setDelegate = CreateSetDelegate(model, propName);
            
            if (setDelegate != null)
            {
                // 核心步骤:将 DataRow 中的值转换为目标属性所需的类型,然后动态调用
                object convertedValue = Convert.ChangeType(dr[dc.ColumnName], GetPropertyType(propName));
                
                try
                {
                    setDelegate.DynamicInvoke(convertedValue);
                }
                catch (TargetInvocationException ex)
                {
                    // 在实际生产环境中,建议记录日志而非静默失败
                    // 通常因属性不可写或类型不兼容导致
                    Console.WriteLine($"Failed to set {propName}: {ex.InnerException?.Message}");
                }
            }
        }
        
        list.Add(model);
    }
    
    return list;
}

第4步:测试验证

构建一个简单的测试用例,模拟从数据库返回的表结构:

static DataTable BuildSampleTable()
{
    DataTable result = new DataTable();
    result.Columns.Add("ID", typeof(int));
    result.Columns.Add("Name", typeof(string));
    result.Columns.Add("IsDeleted", typeof(bool));
    
    result.Rows.Add(new object[] { 1, "Alice", false });
    result.Rows.Add(new object[] { 2, "Bob", true });
    
    return result;
}

// 调用示例
var models = ToModel<TestModel>.GetDelegate_ToModelList(BuildSampleTable());
foreach (var m in models) 
{
    Console.WriteLine(m.ToString());
}

性能考量与总结

此方案通过 Delegate.DynamicInvoke 解决了多类型赋值问题,同时利用 Cache 机制(本例中未展示缓存,但实际应用中建议对 CreateSetDelegate 的结果进行字典缓存)可以极大提升性能。相比于每次解析 Expression Tree 或直接 SetValue 反射,这种方式代码更清晰,且利用了委托调用的相对高性能特性。

以上就是利用委托反射将DataTable转换为实体集的详细内容,更多关于C#数据映射技巧的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。