LINQ To SQL Table -> DataTable, génération de code à la volée, V2
C'est pas parce que je suis au MVP Summit que ça va m'empêcher de coder la nuit. 
J'avais précédemment blogué sur la génération de code à la volée qui permettait de convertir une Table LINQ To SQL en DataTable.
Suite à
- la remarque de Jean-Baptiste
- le fait que ça m'embêtait de garder un appel par réflection et que je voulais utiliser un expression tree à la place
- le fait que je me suis dit que plutôt que de mettre les méthodes génériques et d'avoir un dictionnaire avec le type en clé, je ferais mieux de mettre la généricité au niveau de la classe
J'ai repris mon code pour ceci :
namespace ConsoleApplication81
{
class Program
{
static void Main(string[] args)
{
using (var context = new DataClasses1DataContext())
{
var dt = LinqTableToDataTableHelper<Category>.GetDataTableFromLINQTable(context.Categories);
}
}
}
public static class LinqTableToDataTableHelper<LTT>
where LTT : class
{
private static DynamicMethod _generatedType;
private static Delegate _lambdaExprCompiled;
public static DataTable GetDataTableFromLINQTable(Table<LTT> linqTable)
{
if (_lambdaExprCompiled == null)
{
var parameterExpression = Expression.Parameter(typeof(Table<LTT>), "linqTable");
var lambdaExpression = Expression.Lambda(
Expression.Call(GetGeneratedMethod(), new ParameterExpression[] { parameterExpression }),
parameterExpression);
_lambdaExprCompiled = lambdaExpression.Compile();
}
return _lambdaExprCompiled.DynamicInvoke(linqTable) as DataTable;
}
private static DynamicMethod GetGeneratedMethod()
{
if (_generatedType != null)
return _generatedType;
return (_generatedType = GenerateTableConverter());
}
public static DynamicMethod GenerateTableConverter()
{
var dynamicMethod = new DynamicMethod("Convert", typeof(DataTable), new Type[] { typeof(Table<LTT>) });
var convertIlGenerator = dynamicMethod.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 dynamicMethod;
}
}
}
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 :