Introduction
Sometimes in business intelligence projects, programs have needs of converting domain objects in something manipulable using Dataset
s or DataView
. Here is an example of how extension methods in C# 3.0 can help us do this work in very short classes.
Ok let's go, simply download the source code and try the example to see the magic.
I believe that this simple and short example should demonstrate how few lines of code can do big things! My own thoughts are that the software development should always be made by small methods and the classes must interact between themselves using abstract
methods, virtualization extensions and so on. Naturally a correct architectural concern of the project is a must.
Using the Code
The code usage is very simple, and I provide a cut and paste of a unit test method of Visual Studio 2008 Team System that provides a sample of how you can use this piece of code:
[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);
}
And after the usage here is the source of the extension class that does the work. Please notice that this class uses two delegates expressed via lambda in the unit tests method that can transform the source object before the processing by the conversion Algorithm.
The first delegate (transformCallback
) processes the entity transformation and the entity is passed after the callback in the unit test sample, the entity remains the same.
The second delegate (headerCallback
) is used to populate the DataTable
header and in the unit test sample, the field names are rebuilt with an int
incremented.
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; }
}
}
For simplification in the unit test, I made a Mock of the list of the class AgentAndSell
. Here is a simple example. In the source code, you can find a larger set.
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;
}
}
}
Points of Interest
In this example, the reader can easily learn something about expressions and lambda (features of C# 3.0) and also the PropertyDescriptor
of the TypeDescription
. Reflection should be used to reconstruct the entity back with the typeof(T)
and the usage of the Activator
class which can correctly instantiate our domain object.
That's all.
History
- Ver. pre beta, this is a test but it works fine
Any suggestions and/or improvements are really appreciated.