avril
2007
Je me suis retrouvé confronté à un problème auquel je ne m’attendais pas dans mon mapping objet relationnel.
Comme vous le savez sûrement, les enums peuvent être castés en int et inversement.
En revanche, on a un problème avec les nullable d’enum. En base, j’utilise une colonne de type int facultative pour représenter un nullable d’enum. J’ai fait un mapping objet relationnel générique (au sens général du terme) qui s’appuie sur la reflection pour renseigner les propriétés. Mon problème est qu’on ne peut pas affecter un int à une variable (propriété dans mon cas) de type nullable d’enum.
J’ai donc du faire un test et, dans le cas d’un nullable, récupérer le type de l’enum, caster mon int en mon enum et ensuite affecter ma propriété.
Le problème qui s’est alors posé est le suivant : comment caster dynamiquement un int en enum. La solution que j’ai trouvée passe par la reflection et la génération de code à la volée :
{
if (propertyType.IsGenericType && (propertyType.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
AssemblyName assemblyName = new AssemblyName { Name = "DynamicCastEmitAssembly" };
AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder module = assemblyBuilder.DefineDynamicModule("DynamicCastModule.dll");
TypeBuilder typeBuilder = module.DefineType("CastType", TypeAttributes.Public | TypeAttributes.Class);
MethodBuilder castMethodbuilder = typeBuilder.DefineMethod("Cast", MethodAttributes.Public | MethodAttributes.Static,
propertyType.GetGenericArguments()[0], new Type[] { columnType });
ILGenerator ilGenerator = castMethodbuilder.GetILGenerator();
Label endOfMethod = ilGenerator.DefineLabel();
ilGenerator.DeclareLocal(propertyType.GetGenericArguments()[0]);
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Stloc_0);
ilGenerator.Emit(OpCodes.Br_S, endOfMethod);
ilGenerator.MarkLabel(endOfMethod);
ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Ret);
Type castType = typeBuilder.CreateType();
value = castType.GetMethod("Cast").Invoke(null, new object[] { value });
}
propertyColumnAssociation.Property.SetValue(c, value, null);
}
Pour plus d’info sur la reflection, je vous conseille de nouveau l’article de Mathieu Kempé.
Question avec les DataGridView:
J’utilise une DataGridView et les objets que je veux mettre dedans contiennent des nullable types.
En créant à la main mes DataColumn, si la propriété est de type nullable alors j’utilise Nullable.GetUnderlyingType(field.FieldType) car la DataGridView (ou DataColumn) n’aime pas les nullable (même si intrinsèquement c’est géré vu qu’on n’est pas obligé de remplir tous les champs dans une DataGridView) au lieu de donner le directement le type avec field.FieldType.
Pour un int? ça marche très bien puisque si je rentre un int puis que je supprime ce qu’il y a dans la cellule, la DataGridView accèpte bien la nouvelle valeur null.
Par contre pour une Nullable, j’ai une évidemment une ComboBox qui s’affiche, mais si je selectionne une valeur puis que je supprime ce qu’il y a dans la cellule, j’ai une erreur « Formatted value of the cell has a wrong type »…
Je ne sais pas si quelqu’un a déjà rencontré ce genre de problème…
Je ne connaissais pas Nullable.GetUnderlyingType.
Merci pour l’info
Je suis en train d’écrire également une petite dll pour me faire du mapping O/R (pas hyper évoluée mais juste un truc qui me convient).
Je suis également tombé sur cette erreur, mais je trouve ta méthode assez … excessive.
Pour ma part j’utilise Nullable.GetUnderlyingType sur le FieldInfo (ou le PropertyInfo) que je dois renseigner. Ca me permet d’une part de savoir si le champ destination est Nullable et d’autre part je connais le type paramètre qui en découle. Il ne me reste alors qu’a parser la valeur obtenu de ma base avec Enum :
if (value != DBNull.Value)
{
Type parameterType = Nullable.GetUnderlyingType(field.FieldType);
value = Enum.Parse(parameterType == null ? field.FieldType :
parameterType, value.ToString());
field.SetValue(t, value);
}
Ouais je sais
En même temps, je ne sais pas comment faire autrement !
t un gros malade toi