Hi achour,
Here is a set of extension methods[1] I wrote which can be used to turn a List<T> into a DataTable. There is also the facility to turn a JSON string representing an array into a DataTable as well. You can create a custom Data Source by implementing IDataExtension and returning the DataTable in the GetData() method. For example:
var list = new List<Person>
{
new Person {FirstName = "John", LastName = "Doe"},
new Person {FirstName = "Jane", LastName = "Doe"}
};
var datatable = list.ToDataTable();
return datatable;
If you read through this forum question, you will find a discussion on how to set up a custom data source for both the Report Designer and the Report Server:
HTH,
--Michael
[1] Extension Methods
public static class DataTableExtensions
{
private static void AddColumns(this DataTable table, IEnumerable<PropertyInfo> properties)
{
foreach (var propertyInfo in properties)
{
var colType = propertyInfo.PropertyType;
if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
colType = colType.GetGenericArguments()[0];
}
table.Columns.Add(new DataColumn(propertyInfo.Name, colType));
}
}
private static void AddRows<T>(this DataTable table, IEnumerable<T> items, IEnumerable<PropertyInfo> properties)
{
var propGetters = properties.Select(ReflectionUtility.GetGetter).Cast<Func<T, object>>().ToList();
// Add the property values per T as rows to the datatable
foreach (var item in items)
{
var values = new object[propGetters.Count];
for (var i = 0; i < propGetters.Count; i++)
{
values[i] = propGetters[i](item);
}
table.Rows.Add(values);
}
}
public static DataTable ToDataTable<T>(this List<T> items, string tableName = "") where T : class
{
var table = new DataTable(tableName);
var properties = typeof(T).GetProperties();
table.AddColumns(properties);
table.AddRows(items, properties);
return table;
}
public static DataTable ToDataTable(this string json, string tableName = "")
{
var dataTable = JsonConvert.DeserializeObject<DataTable>(json);
dataTable.TableName = tableName;
return dataTable;
}
public static DataTable ToDataTable<T>(this string json, string tableName = "") where T : class
{
var obj = JsonConvert.DeserializeObject<List<T>>(json.EnsureJsonArray());
return obj.ToDataTable(tableName);
}
private static string EnsureJsonArray(this string json)
{
// If the JSON string does not begin with "["
// then insert the string into an array representation.
// This hack is required since the GC reporting API inconsistently
// returns single objects and arrays of objects.
if (!json.StartsWith("["))
{
json = $"[{json}]";
}
return json;
}
}
public class ReflectionUtility
{
internal static Func<object, object> GetGetter(PropertyInfo property)
{
// get the get method for the property
MethodInfo method = property.GetGetMethod(true);
// get the generic get-method generator (ReflectionUtility.GetSetterHelper<TTarget,TValue >)
MethodInfo genericHelper = typeof(ReflectionUtility).GetMethod(
"GetGetterHelper",
BindingFlags.Static | BindingFlags.NonPublic);
// reflection call to the generic get-method generator to generate the type arguments
MethodInfo constructedHelper = genericHelper.MakeGenericMethod(
method.DeclaringType,
method.ReturnType);
// now call it. The null argument is because it's a static method.
object ret = constructedHelper.Invoke(null, new object[] { method });
// cast the result to the action delegate and return it
return (Func<object, object>)ret;
}
static Func<object, object> GetGetterHelper<TTarget, TResult>(MethodInfo method)
where TTarget : class // target must be a class as property sets on structs need a ref param
{
// Convert the slow MethodInfo into a fast, strongly typed, open delegate
Func<TTarget, TResult> func = (Func<TTarget, TResult>)Delegate.CreateDelegate(typeof(Func<TTarget, TResult>), method);
// Now create a more weakly typed delegate which will call the strongly typed one
Func<object, object> ret = (object target) => (TResult)func((TTarget)target);
return ret;
}
}