Quiconque à voulu un jour utiliser des Guid comme clé primaire avec Entity Framework a constaté que celui-ci ne savait pas obtenir du servir SQL un valeur par défaut. Souci qui peut sembler relativement anodin mais qui deviens vite très pénalisant quand votre BDD n’utilise que de Guid. On peut trouver un peu partout sur le web des méthodes consistant à modifier l’EDM afin d’utiliser des tableaux de bits, mais en soit rien de vraiment léger et parfois même un peu hasardeux.
Pour palier à ce souci j’ai réalisé une méthode d’extension qui a pour mission d’ajouter une entité nouvellement créé à un contexte de données et dans le cas où celle-ci contient une clé primaire sous forme de Guid, elle créé un Guid en testant que celui-ci n’est pas déjà utilisé. On se retrouve alors avec un Guid généré automatiquement et ceci sans modifier ou altérer notre model.
Voila donc un code qui va certainement servir à plus d’un ;)
Note : seule limitation existante (réalisée en fait par précaution pour mon usage), le jeu d’entité du context doit avoir un nom commençant par le nom du type de l’entité manipulée. Cette limitation peu bien entendu sauter si vous modifiez le code.
Vb
Imports System Imports System.Collections.Generic Imports System.Linq Imports System.Text Imports System.Data.EntityClient Imports System.Data.Objects Imports System.Data.Objects.DataClasses Imports System.Reflection Imports System.Runtime.CompilerServices Namespace mlEntity Public Module EntityTypeExtension ''' <summary> ''' Ajout d'un objet à un context et création automatique d'un Guid pour une PK ''' </summary> ''' <typeparam name="T"></typeparam> ''' <param name="context"></param> ''' <param name="entity"></param> ''' <remarks></remarks> <Extension()> _ Public Sub Add(Of T As EntityObject)(ByVal context As ObjectContext, ByVal entity As T) ' Déterminer de la propriété qui correspond au jeux d'entités de type T Dim jeuProperty As PropertyInfo = context.GetType().GetProperties() _ .FirstOrDefault(Function(c) c.PropertyType Is GetType(ObjectQuery(Of T)) _ AndAlso c.Name.StartsWith(GetType(T).Name)) If jeuProperty IsNot Nothing Then ' Déterminer la propriété du Type T qui est la clé GUID (si il y en a une) Dim keyProperty As PropertyInfo = GetType(T).GetProperties() _ .FirstOrDefault(Function(c) _ c.PropertyType Is GetType(Guid) _ AndAlso (CType(c.GetCustomAttributes(GetType(EdmScalarPropertyAttribute), True).FirstOrDefault(), EdmScalarPropertyAttribute).EntityKeyProperty)) ' Déterminer si le type T a un clé de type Guid If keyProperty IsNot Nothing Then ' Recherche du jeu d'entités qui serra manipuler pour tester le id Dim jeu As ObjectQuery(Of T) = CType(jeuProperty.GetValue(context, Nothing), ObjectQuery(Of T)) ' Id qui serragénéré Dim id As Guid ' Méthode utilisée pour tester la validité de l'ID Dim test As Func(Of T, Boolean) Do ' Créer un Guid id = Guid.NewGuid() ' Préparation du test test = Function(c) New Guid(keyProperty.GetValue(c, Nothing).ToString()) = id ' Tester si le guid est déjà utilisé Loop While (jeu.FirstOrDefault(test) IsNot Nothing) ' Utilisation de l'Id keyProperty.SetValue(entity, id, Nothing) End If ' Ajout de l'objet au context context.AddObject(jeuProperty.Name, entity) Else Throw New Exception("Jeu d'entités introuvable dans le context de données.") End If End Sub End Module End Namespace
C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.Entity; using System.Data.Objects; using System.Data.Objects.DataClasses; using System.Reflection; namespace mlEntity { public static class EntityTypeExtension { /// <summary> /// Ajout d'un objet à un context et création automatique d'un Guid pour une PK /// </summary> /// <typeparam name="T"></typeparam> /// <param name="context"></param> /// <param name="entity"></param> public static void Add<T>(this ObjectContext context, T entity) where T : EntityObject { // Déterminer de la propriété qui correspond au jeux d'entités de type T PropertyInfo jeuProperty = context.GetType().GetProperties() .FirstOrDefault(c => c.PropertyType == typeof(ObjectQuery<T>) & c.Name.StartsWith(typeof(T).Name)); if (jeuProperty != null) { // Déterminer la propriété du Type T qui est la clé GUID (si il y en a une) PropertyInfo keyProperty = typeof(T).GetProperties() .FirstOrDefault(c => c.PropertyType == typeof(Guid) & (c.GetCustomAttributes(typeof(EdmScalarPropertyAttribute), true).FirstOrDefault() as EdmScalarPropertyAttribute).EntityKeyProperty ); // Déterminer si le type T a un clé de type Guid if (keyProperty != null) { // Recherche du jeu d'entités qui serra manipuler pour tester le id ObjectQuery<T> jeu = jeuProperty.GetValue(context, null) as ObjectQuery<T>; // Id qui serragénéré Guid id; // Méthode utilisée pour tester la validité de l'ID Func<T, Boolean> test; do { // Créer un Guid id = Guid.NewGuid(); // Préparation du test test = c => new Guid(keyProperty.GetValue(c, null).ToString()) == id; // Tester si le guid est déjà utilisé } while (jeu.FirstOrDefault(test) != null); // Utilisation de l'Id keyProperty.SetValue(entity, id, null); } // Ajout de l'objet au context context.AddObject(jeuProperty.Name, entity); } else { throw new Exception("Jeu d'entités introuvable dans le context de données."); } } } }