J'ai récemment eu la question suivante : comment, à partir d'une System.Linq.Table<T> générer une DataTable.
Ma réponse a tout de suite était : par reflection.
Le problème de la reflection c'est que c'est long.
Du coup la meilleure façon d'optimiser le traitement est de générer du code fortement typé à la volée.
Pour ma part, j'ai utilisé la méthode Emit de la class ILGenerator.
namespace ConsoleApplication81
{
class Program
{
static void Main(string[] args)
{
using (var context = new DataClasses1DataContext())
{
DataTable table = LinqTableToDataTableHelper.GetDataTableFromLINQTable(context.Products);
}
}
}
public static class LinqTableToDataTableHelper
{
public static DataTable GetDataTableFromLINQTable<LTT>(Table<LTT> linqTable)
where LTT : class
{
return GetGeneratedType<LTT>().GetMethod("Convert").Invoke(null, new object[]{linqTable}) as DataTable;
}
private static Dictionary<Type, Type> _generatedTypes = new Dictionary<Type, Type>();
private static Type GetGeneratedType<LTT>()
where LTT : class
{
if (_generatedTypes.ContainsKey(typeof(LTT)))
return _generatedTypes[typeof(LTT)];
Type generatedType = GenerateTableConverter<LTT>();
_generatedTypes.Add(typeof(LTT), generatedType);
return generatedType;
}
public static Type GenerateTableConverter<LTT>()
where LTT : class
{
var assemblyName = new AssemblyName { Name = typeof(LTT).Name + "Converter" };
var assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var module = assemblyBuilder.DefineDynamicModule(typeof(LTT).Name + ".dll");
var typeBuilder = module.DefineType("Converter", TypeAttributes.Public | TypeAttributes.Class);
var convertMethodBuilder = typeBuilder.DefineMethod("Convert", MethodAttributes.Public | MethodAttributes.Static, typeof(DataTable), new Type[] { typeof(Table<LTT>) });
var convertIlGenerator = convertMethodBuilder.GetILGenerator();
convertIlGenerator.DeclareLocal(typeof(DataTable));
convertIlGenerator.DeclareLocal(typeof(IEnumerator<LTT>));
convertIlGenerator.DeclareLocal(typeof(LTT));
convertIlGenerator.DeclareLocal(typeof(DataRow));
var propertyValue = convertIlGenerator.DeclareLocal(typeof(object));
convertIlGenerator.Emit(OpCodes.Ldarg_0);
var convertIlGeneratorArgOkLabel = convertIlGenerator.DefineLabel();
convertIlGenerator.Emit(OpCodes.Brtrue_S, convertIlGeneratorArgOkLabel);
convertIlGenerator.Emit(OpCodes.Newobj, typeof(ArgumentException).GetConstructor(new Type[0]));
convertIlGenerator.Emit(OpCodes.Throw);
convertIlGenerator.MarkLabel(convertIlGeneratorArgOkLabel);
convertIlGenerator.Emit(OpCodes.Newobj, typeof(DataTable).GetConstructor(new Type[0]));
convertIlGenerator.Emit(OpCodes.Stloc_0);
var properties = typeof(LTT).GetProperties().Where(p => p.GetAttribute<ColumnAttribute>() != null);
foreach (var pi in properties)
{
convertIlGenerator.Emit(OpCodes.Ldloc_0);
convertIlGenerator.Emit(OpCodes.Callvirt, typeof(DataTable).GetMethod("get_Columns"));
convertIlGenerator.Emit(OpCodes.Ldstr, pi.Name);
if (pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
convertIlGenerator.Emit(OpCodes.Ldtoken, pi.PropertyType.GetGenericArguments()[0]);
else
convertIlGenerator.Emit(OpCodes.Ldtoken, pi.PropertyType);
convertIlGenerator.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) }));
convertIlGenerator.Emit(OpCodes.Callvirt, typeof(DataColumnCollection).GetMethod("Add", new Type[] { typeof(string), typeof(Type) }));
convertIlGenerator.Emit(OpCodes.Pop);
}
var convertIlGeneratorEntitiesLoopLabel = convertIlGenerator.DefineLabel();
convertIlGenerator.Emit(OpCodes.Ldarg_0);
convertIlGenerator.Emit(OpCodes.Callvirt, typeof(IEnumerable<LTT>).GetMethod("GetEnumerator"));
convertIlGenerator.Emit(OpCodes.Stloc_1);
convertIlGenerator.MarkLabel(convertIlGeneratorEntitiesLoopLabel);
convertIlGenerator.Emit(OpCodes.Ldloc_1);
convertIlGenerator.Emit(OpCodes.Callvirt, typeof(IEnumerator).GetMethod("MoveNext"));
var convertIlGeneratorEntitiesEndLoopLable = convertIlGenerator.DefineLabel();
convertIlGenerator.Emit(OpCodes.Brfalse, convertIlGeneratorEntitiesEndLoopLable);
convertIlGenerator.Emit(OpCodes.Ldloc_1);
convertIlGenerator.Emit(OpCodes.Callvirt, typeof(IEnumerator<LTT>).GetMethod("get_Current"));
convertIlGenerator.Emit(OpCodes.Stloc_2);
convertIlGenerator.Emit(OpCodes.Ldloc_0);
convertIlGenerator.Emit(OpCodes.Callvirt, typeof(DataTable).GetMethod("get_Rows"));
convertIlGenerator.Emit(OpCodes.Ldloc_0);
convertIlGenerator.Emit(OpCodes.Callvirt, typeof(DataTable).GetMethod("NewRow"));
convertIlGenerator.Emit(OpCodes.Stloc_3);
foreach (var pi in properties)
{
convertIlGenerator.Emit(OpCodes.Ldloc_2);
convertIlGenerator.Emit(OpCodes.Callvirt, typeof(LTT).GetMethod("get_" + pi.Name, new Type[0]));
if (pi.PropertyType.IsValueType)
convertIlGenerator.Emit(OpCodes.Box, pi.PropertyType);
convertIlGenerator.Emit(OpCodes.Stloc, propertyValue);
convertIlGenerator.Emit(OpCodes.Ldloc, propertyValue);
var convertIlGeneratorNextPropertyLabel = convertIlGenerator.DefineLabel();
convertIlGenerator.Emit(OpCodes.Brfalse_S, convertIlGeneratorNextPropertyLabel);
convertIlGenerator.Emit(OpCodes.Ldloc_3);
convertIlGenerator.Emit(OpCodes.Ldstr, pi.Name);
convertIlGenerator.Emit(OpCodes.Ldloc, propertyValue);
convertIlGenerator.Emit(OpCodes.Callvirt, typeof(DataRow).GetMethod("set_Item", new Type[] { typeof(string), typeof(object) }));
convertIlGenerator.MarkLabel(convertIlGeneratorNextPropertyLabel);
}
convertIlGenerator.Emit(OpCodes.Ldloc_3);
convertIlGenerator.Emit(OpCodes.Callvirt, typeof(DataRowCollection).GetMethod("Add", new Type[] { typeof(DataRow) }));
convertIlGenerator.Emit(OpCodes.Br, convertIlGeneratorEntitiesLoopLabel);
convertIlGenerator.MarkLabel(convertIlGeneratorEntitiesEndLoopLable);
convertIlGenerator.Emit(OpCodes.Ldloc_0);
convertIlGenerator.Emit(OpCodes.Ret);
return typeBuilder.CreateType();
}
}
}
namespace System.Reflection
{
public static class PropertyInfoExtension
{
public static T GetAttribute<T>(this PropertyInfo pi) where T : Attribute
{
object[] attributes = pi.GetCustomAttributes(typeof(T), true);
if (attributes.Length == 0)
return null;
return attributes[0] as T;
}
}
}
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :