This article shows code how you can create a DataTable from a collection of T (IEnumerable<T>) and defining explicitly the column order.
An extension method for this looks like the following:
public static class DataTableExtensions
{
public static DataTable CreateOrderedDataTable<T>(this IEnumerable<T> data)
{
var dataTable = new DataTable();
var orderedProps = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
.OrderBy(prop => GetColumnOrder(prop)).ToList();
foreach (var prop in orderedProps){
dataTable.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
}
if (data != null)
{
dataTable.BeginLoadData();
var enumerator = data.GetEnumerator();
while (enumerator.MoveNext()){
var item = enumerator.Current;
var rowValues = new List<object>();
foreach (var prop in orderedProps){
rowValues.Add(prop.GetValue(item, null));
}
dataTable.Rows.Add(rowValues.ToArray());
}
dataTable.AcceptChanges();
}
return dataTable;
}
static int GetColumnOrder(PropertyInfo prop)
{
var displayAttribute = prop.GetCustomAttributes(typeof(DisplayAttribute), false).FirstOrDefault() as DisplayAttribute;
int orderKey = displayAttribute?.Order ?? prop.MetadataToken;
return orderKey;
}
}
We order first by
DisplayAttribute and the
Order value, and fallback to property's
MetadataToken. This is an integer value that also returns the order the property was declared,
in case you want to order just by the way properties are defined. We get the enumerator here and fetch the row one by one. We could use a simple foreach loop here too. Note the use of
BeginLoadData
and
AcceptChanges.
Consider the two classes next. One class does not set any explicit order, the other class uses the Display attribute's Order value to define a custom order of columns for the DataTable.
public class Car
{
public int Id { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public string Color { get; set; }
}
public class CarV2
{
[Display(Order = 4)]
public int Id { get; set; }
[Display(Order = 3)]
public string Make { get; set; }
[Display(Order = 2)]
public string Model { get; set; }
[Display(Order = 14)]
public bool IsElectric { get; set; }
[Display(Order = -188865)]
public string Color { get; set; }
}
Next, the following little program in Linqpad tests this extension method and displays the datatables resulting with column ordering set.
void Main()
{
var cars = new List<Car>{
new Car { Id = 1, Make = "Audi", Model = "A5", Color = "Blue" },
new Car { Id = 2, Make = "Volvo", Model = "XC60", Color = "Silver" },
new Car { Id = 3, Make = "Golf", Model = "GTI", Color = "White" },
new Car { Id = 4, Make = "Audi", Model = "A5", Color = "Blue" },
};
var dataTable = cars.CreateOrderedDataTable();
dataTable.Dump("Cars datatable, data type is: Car");
var carV2s = new List<CarV2>{
new CarV2 { Id = 1, Make = "Audi", Model = "A5", Color = "Blue" },
new CarV2 { Id = 2, Make = "Volvo", Model = "XC60", Color = "Silver" },
new CarV2 { Id = 3, Make = "Golf", Model = "GTI", Color = "White" },
new CarV2 { Id = 4, Make = "Audi", Model = "A5", Color = "Blue" },
};
var dataTableV2 = carV2s.CreateOrderedDataTable();
dataTableV2.Dump("Carsv2 datatable, datatype is CarV2");
}
No comments:
Post a Comment