如何将强类型领域对象的通用列表转换为 DataTable






4.16/5 (5投票s)
一个有用的示例,展示如何扩展通用列表并将其转换为 DataTable。
引言
在商业智能项目中,程序有时需要将领域对象转换为可以使用Dataset
或DataView
进行操作的形式。以下是一个示例,展示了C# 3.0中的扩展方法如何帮助我们用非常简洁的类完成这项工作。
好的,让我们开始吧,只需下载源代码并尝试示例即可看到神奇之处。
我相信这个简单而简洁的示例应该展示了少量的代码可以完成很多事情! 我个人的想法是,软件开发应该始终由小方法完成,并且类应该使用abstract
方法、虚拟化扩展等方式相互交互。 当然,对项目进行正确的架构设计是必不可少的。
Using the Code
代码的使用非常简单,我提供了一个Visual Studio 2008 Team System单元测试方法的剪切和粘贴,它提供了一个如何使用这段代码的示例。
[TestMethod] public void costruzione_datatable_con_callback() { var list = PivotTestData.GetMockAgents(); Assert.IsNotNull(list); var i = 0; var table = list.ConvertDomainListToDataTable((agent => agent), (s => (++i).ToString("n"))); Assert.AreEqual(list.Count, table.Rows.Count); }
在使用方法之后,这里是执行这项工作的扩展类的源代码。 请注意,这个类使用了通过lambda表达式在单元测试方法中表达的两个委托,它们可以在转换算法处理之前转换源对象。
第一个委托 (transformCallback
) 处理实体转换,并且实体在单元测试示例中的回调之后传递,实体保持不变。
第二个委托 (headerCallback
) 用于填充DataTable
标题,并且在单元测试示例中,字段名称使用递增的int
值重新构建。
namespace Domain
{
public abstract class DomainBase
{
}
public class AgentAndSell: DomainBase
{
public string Name { get; set; }
public double CashAmount { get; set; }
public decimal Discount { get; set; }
public DateTime OrderDate { get; set; }
}
}
为了简化单元测试,我对类AgentAndSell
的列表进行了模拟。 这里是一个简单的例子。 在源代码中,您可以找到更大的集合。
public static class PivotTestData
{
public static IList<AgentAndSell> GetMockAgents()
{
IList<AgentAndSell> ret = new List<AgentAndSell>
{
new AgentAndSell { CashAmount = 10, Discount = 0,
Name ="Mario Rossi", OrderDate = new DateTime(2009,1,1)},
new AgentAndSell { CashAmount = 10, Discount = 0,
Name ="Mario Rossi", OrderDate = new DateTime(2009,1,2)},
new AgentAndSell { CashAmount = 10, Discount = 0,
Name ="Mario Rossi", OrderDate = new DateTime(2009,1,3)},
new AgentAndSell { CashAmount = 10, Discount = 0,
Name ="Mario Rossi", OrderDate = new DateTime(2009,1,4)},
new AgentAndSell { CashAmount = 10, Discount = 0,
Name ="Mario Rossi", OrderDate = new DateTime(2009,1,5)},
new AgentAndSell { CashAmount = 10, Discount = 0,
Name ="Mario Rossi", OrderDate = new DateTime(2009,1,6)},
new AgentAndSell { CashAmount = 10, Discount = 0,
Name ="Mario Rossi", OrderDate = new DateTime(2009,2,1)},
new AgentAndSell { CashAmount = 10, Discount = 0,
Name ="Mario Rossi", OrderDate = new DateTime(2009,2,2)},
new AgentAndSell { CashAmount = 10, Discount = 0,
Name ="Mario Rossi", OrderDate = new DateTime(2009,2,3)},
new AgentAndSell { CashAmount = 10, Discount = 0,
Name ="Mario Rossi", OrderDate = new DateTime(2009,2,4)},
new AgentAndSell { CashAmount = 10, Discount = 0,
Name ="Mario Rossi", OrderDate = new DateTime(2009,2,5)},
new AgentAndSell { CashAmount = 10, Discount = 0,
Name ="Mario Rossi", OrderDate = new DateTime(2009,2,6)},
};
return ret;
}
}
using System.Collections;
using System.Data;
using System.Collections.Generic;
using System.ComponentModel;
using System;
using Model;
namespace Extensions
{
public static class DataTableExtensions
{
public static DataTable ConvertDomainListToDataTable<T>(
this IList<T> list, Func<T, T> transformCallback,
Func<string, string> headerCallback)
where T : DomainBase
{
var entityType = typeof(T);
var table = entityType.BuildTableFromType(headerCallback);
var properties = TypeDescriptor.GetProperties(entityType);
foreach (var item in list)
{
var tableRow = table.NewRow();
var item2 = transformCallback == null ?
item : transformCallback(item);
if (transformCallback != null) item2 = transformCallback(item);
var i = 0;
foreach (PropertyDescriptor property in properties)
{ tableRow[i++] = property.GetValue(item2); }
table.Rows.Add(tableRow);
}
return table;
}
public static DataTable BuildTableFromType<T>
(this T entityType, Func<string, string> headerCallback)
where T: Type
{
var table = new DataTable(entityType.Name);
var properties = TypeDescriptor.GetProperties(entityType);
foreach (PropertyDescriptor property in properties)
{
var headerItem = (headerCallback == null) ?
property.Name : headerCallback(property.Name);
table.Columns.Add(headerItem, property.PropertyType);
}
return table;
}
}
}
关注点
在这个例子中,读者可以轻松地了解有关表达式和lambda (C# 3.0 的特性) 以及 TypeDescription
的 PropertyDescriptor
的一些知识。 应该使用反射来使用 typeof(T)
和 Activator
类重新构造实体,后者可以正确地实例化我们的领域对象。
就是这样。
历史
- 预发布beta版,这是一个测试,但它工作正常
任何建议和/或改进都将不胜感激。