J'ai passé énormément de temps à adapter l'Entity Framework à mes besoins et je peux donc dire qu'il remplit la plupart des conditions que vous attendez d'un ORM. Mais certains aspects sont beaucoup trop complexes, car d'autres ORM ont montré qu'il est possible de les rendre plus faciles.
Par exemple, il est assez facile de démarrer avec Entity Framework, puisqu'il suffit de lancer le Designer dans Visual Studio pour disposer d'un ORM fonctionnel en quelques minutes. Mais on se retrouve avec des classes d'entités liées au contexte d'objet créé par le concepteur (on peut éviter cela en utilisant un modèle T4 personnalisé). Ce n'est pas nécessairement une mauvaise chose, mais c'est le genre d'approches Microsoft "Getting Started" que vous ne voulez pas utiliser dans une application réelle.
Mais si vous vous plongez plus profondément dans l'Entity Framework, vous verrez comment vous pouvez éviter la plupart de ses pièges : Le Designer génère un fichier EDMX, qui (si vous le regardez dans un éditeur XML) n'est rien de plus qu'une combinaison des trois aspects principaux d'un ORM, le stockage physique (votre base de données), le modèle conceptuel (vos classes d'entités) et le mapping entre les deux. L'action de construction personnalisée appliquée aux fichiers .edmx dans Visual Studio divise ces trois parties en trois fichiers distincts et les ajoute à l'assemblage en tant que ressources intégrées. Lors de la création d'un ObjectContext, le chemin vers ces trois fichiers est utilisé dans la ConnectionString (ce qui me semble toujours un peu confus). Ce que vous pouvez faire ici, c'est faire tout cela vous-même. Cela signifie écrire le schéma de stockage, le modèle conceptuel et le mappage dans un éditeur XML (un peu comme NHibernate) et les intégrer à l'assemblage contenant votre modèle.
La classe de base d'Entity Framework "ObjectContext" peut être construite à partir de ces trois fichiers (elle prend un MetadataWorkspace et une EntityConnection), mais le fait est que vous avez un contrôle total sur la façon dont l'ObjectContext est créé. Cela ouvre la porte à de nombreuses fonctionnalités que vous n'attendiez pas de l'Entity Framework. Par exemple : vous pouvez intégrer plusieurs schémas de stockage SSDL dans le même assembly pour correspondre à un type de base de données spécifique (j'en ajoute généralement un pour SQL Server et un pour SQL Server CE 4.0). Et créer une surcharge de constructeur qui choisit le schéma de stockage approprié pour un type spécifique de DbConnection.
Puisque vous avez maintenant votre propre implémentation d'ObjectContext, vous pouvez implémenter diverses interfaces sur celui-ci. Comme votre propre IRepository, mais comme j'aime l'approche ObjectContext, je crée quelque chose comme :
interface ICatalog
{
IEntitySet<Article> { get; }
void Save();
}
interface IEntitySet<T> : IQueryable<T>
{
void Add(T);
void Remove(T);
}
class EntityFrameworkCatalog : ICatalog
{
...
}
Mais créer un Repository si vous avez un ObjectContext d'Entity Framework est très facile, et vous obtenez en plus un IQueryable. Sur la base de ces informations, vous pouvez éviter d'avoir un fort couplage de classes entre vos services et l'ORM et éliminer complètement l'Entity Framework dans les tests. De plus, lorsque vous testez l'implémentation de votre Entity Framework, vous pouvez utiliser une base de données SQL Server CE pendant les tests unitaires pour vous assurer que vos mappings sont corrects (généralement, la différence entre le schéma de stockage pour CE et le SQL Server complet se limite à quelques types de données). Vous pouvez donc tester tous les comportements de votre implémentation Entity Framework sans problème.
Ainsi, Entity Framework s'inscrit parfaitement dans les concepts des logiciels modernes, mais il ne vous impose pas de telles pratiques, ce qui facilite la "prise en main".
Passons maintenant aux éléments complexes : L'Entity Framework dispose d'un petit ensemble de types CLR pris en charge, qui ne comprend essentiellement que les types primitifs, comme les ints, les chaînes de caractères et les tableaux d'octets. Il fournit également un certain niveau de types complexes, qui suivent les mêmes règles. Mais que se passe-t-il si vous avez une propriété d'entité complexe telle que la représentation DOM d'un document, que vous souhaitez sérialiser en XML dans la base de données. Pour autant que je sache, NHibernate fournit une fonctionnalité appelée IUserType, qui vous permet de définir un tel mappage pour vous. Dans Entity Framework, cela devient beaucoup plus compliqué, mais c'est toujours joli à sa façon. Le modèle conceptuel vous permet d'inclure des types complexes internes à l'assemblage (pour autant que vous en informiez l'ObjectContext (ObjectContext.CreateProxyTypes(Type[])). Ainsi, vous pouvez créer un wrapper pour votre type original, qui n'est connu que de l'Entity Framework, comme suit :
class Document : IXmlSerializable { }
class Article
{
public virtual Document Content { get; set; }
}
internal class EntityFrameworkDocument : Document
{
public string Xml
{
get
{
// Use XmlSerializer to generate the XML-string for this instance.
}
set
{
// Use XmlSerializer to read the XML-string for this instance.
}
}
}
Bien que EF puisse maintenant retourner ces documents sérialisés depuis le stockage, les écrire dans le stockage nécessite d'intercepter le stockage d'un article et de remplacer un simple document par un EntityFrameworkDocument, pour s'assurer que EF peut le sérialiser. Je suis sûr que d'autres ORMs font cela assez facilement, et c'est encore pire. Actuellement, il n'y a aucun moyen de faire la même chose avec la classe System.Uri (qui est immuable, mais qui fonctionnerait autrement) ou une Enum. En dehors de ces restrictions, vous pouvez adapter l'EF à la plupart de vos besoins. Mais vous y passerez beaucoup de temps (comme je l'ai fait).
Comme mon expérience des autres ORM est limitée, je résumerai :
- Entity Framework est dans le GAC, même dans le profil du client.
- Entity Framework peut être personnalisé pour représenter des types d'entités même complexes (y compris des entités many-to-many auto-référencées, par exemple, ou la sérialisation XML ci-dessus).
- Il peut être "abstrait", de sorte que vous pouvez vous en tenir à IRepository, etc.
- Implémentation de IQueryable (bien qu'elle ne soit pas aussi complète que DataObjects.Net)
-
Elle ne nécessite que System.Data et System.Data.Entity. Vous pouvez même inclure plusieurs schémas de stockage pour d'autres fournisseurs, ce qui nécessiterait normalement une référence, mais si vous vous en tenez à DbConnection, vous pouvez simplement le faire :
ICatalog Create(DbConnection connection, string storageSchemaPath) ICatalog CreateMySql(DbConnection mySqlConnection) { return Create(connection, "res://Assembly/Path.To.Embedded.MySql.Storage.ssdl"); }
Modifier J'ai récemment découvert que si vos entités et votre implémentation "catalogue" se trouvent dans la même assemblée, vous pouvez utiliser les propriétés internes pour un processus de sérialisation XML. Ainsi, au lieu de dériver une propriété interne EntityFrameworkDocument
de Document
vous pourriez ajouter une propriété interne appelée Xml
à la Document
elle-même. Cela ne s'applique toujours que si vous avez le contrôle total de vos entités, mais cela supprime la nécessité d'intercepter toute modification du catalogue, pour s'assurer que votre classe dérivée est utilisée. La CSDL se présente de la même manière, EF permet simplement à la propriété mappée d'être interne. Je dois encore m'assurer que cela fonctionne dans les environnements de confiance moyenne.