Quelles sont les bonnes pratiques de conception lorsque vous travaillez avec Entity Framework?

Cela s’appliquera principalement pour une application asp.net où les données ne sont pas accessibles via soa. Cela signifie que vous avez access aux objects chargés depuis la structure, pas aux objects de transfert, bien que certaines recommandations s’appliquent toujours.

Ceci est un article de la communauté, alors s’il vous plaît, ajoutez-le comme bon vous semble.

S’applique à : Entity Framework 1.0 fourni avec Visual Studio 2008 sp1.

Pourquoi choisir EF en premier lieu?

Considérant qu’il s’agit d’une technologie jeune avec beaucoup de problèmes (voir ci-dessous), il peut être difficile de prendre le train EF pour votre projet. Cependant, c’est la technologie que Microsoft met en avant (au désortingment de Linq2Sql, qui est un sous-ensemble de EF). De plus, vous ne serez peut-être pas satisfait de NHibernate ou d’autres solutions. Quelles que soient les raisons, il y a des gens (y compris moi) qui travaillent avec EF et la vie n’est pas mauvaise.

EF et inheritance

Le premier grand sujet est l’inheritance. EF prend en charge le mappage des classes héritées qui sont conservées de deux manières: table par classe et table de la hiérarchie. La modélisation est facile et il n’y a pas de problèmes de programmation avec cette partie.

(Ce qui suit s’applique au modèle de table par classe car je n’ai pas d’expérience avec la table par hiérarchie, qui est, de toute façon, limitée.) Le vrai problème vient lorsque vous essayez d’exécuter des requêtes qui incluent un ou plusieurs objects faisant partie de un arbre d’inheritance: le fichier SQL généré est incroyablement horrible, il faut beaucoup de temps pour être analysé par le EF et il faut beaucoup de temps pour s’exécuter. Ceci est un vrai bouchon de spectacle. Assez que EF ne devrait probablement pas être utilisé avec inheritance ou aussi peu que possible.

Voici un exemple de la gravité de la situation. Mon modèle EF avait ~ 30 classes, dont 10 faisaient partie d’un arbre d’inheritance. Lors de l’exécution d’une requête pour obtenir un élément de la classe Base, quelque chose d’aussi simple que Base.Get (id), le code SQL généré contenait plus de 50 000 caractères. Ensuite, lorsque vous essayez de renvoyer certaines associations, cela dégénère encore plus, allant jusqu’à lancer des exceptions SQL pour ne pas pouvoir interroger plus de 256 tables à la fois.

Ok, c’est mauvais, le concept EF est de vous permettre de créer votre structure d’object sans (ou avec le moins possible) de considération sur l’implémentation réelle de la firebase database de votre table. Cela échoue complètement à cela.

Alors, des recommandations? Évitez l’inheritance si vous le pouvez, la performance sera tellement meilleure. Utilisez-le avec parcimonie là où vous devez. À mon avis, cela fait d’EF un outil de génération de requête en SQL amélioré, mais il ya encore des avantages à l’utiliser. Et des moyens de mettre en œuvre des mécanismes similaires à l’inheritance.

Contourner l’inheritance avec des interfaces

La première chose à savoir pour essayer d’obtenir une sorte d’inheritance avec EF est que vous ne pouvez pas affecter une classe de base à une classe non modélisée par EF. Ne l’essayez même pas, il sera écrasé par le modélisateur. Alors que faire?

Vous pouvez utiliser des interfaces pour imposer que les classes implémentent certaines fonctionnalités. Par exemple, voici une interface IEntity qui vous permet de définir des associations entre des entités EF lorsque vous ne connaissez pas le type de l’entité au moment de la conception.

public enum EntityTypes{ Unknown = -1, Dog = 0, Cat } public interface IEntity { int EntityID { get; } ssortingng Name { get; } Type EntityType { get; } } public partial class Dog : IEntity { // implement EntityID and Name which could actually be fields // from your EF model Type EntityType{ get{ return EntityTypes.Dog; } } } 

En utilisant cette IEntity, vous pouvez alors travailler avec des associations non définies dans d’autres classes

 // lets take a class that you defined in your model. // that class has a mapping to the columns: PetID, PetType public partial class Person { public IEntity GetPet() { return IEntityController.Get(PetID,PetType); } } 

qui utilise certaines fonctions d’extension:

 public class IEntityController { static public IEntity Get(int id, EntityTypes type) { switch (type) { case EntityTypes.Dog: return Dog.Get(id); case EntityTypes.Cat: return Cat.Get(id); default: throw new Exception("Invalid EntityType"); } } } 

Pas aussi simple que d’avoir un inheritance clair, d’autant plus que vous devez stocker le PetType dans un champ de firebase database supplémentaire, mais compte tenu des gains de performances, je ne regarderais pas en arrière.

Il ne peut pas non plus modéliser la relation un-à-plusieurs, plusieurs-à-plusieurs, mais avec des utilisations créatives de «l’union», cela pourrait être fait pour fonctionner. Enfin, cela crée l’effet secondaire du chargement de données dans une propriété / fonction de l’object, dont il faut faire attention. Utiliser une convention de nommage claire comme GetXYZ () aide à cet égard.

Requêtes compilées

Les performances d’Entity Framework ne sont pas aussi bonnes que l’access direct à la firebase database avec ADO (évidemment) ou Linq2SQL. Il existe toutefois des moyens de l’améliorer, l’un d’entre eux consistant à comstackr vos requêtes. Les performances d’une requête compilée sont similaires à celles de Linq2Sql.

Qu’est-ce qu’une requête compilée? Il s’agit simplement d’une requête pour laquelle vous indiquez au framework de conserver l’arborescence analysée en mémoire afin de ne pas avoir à la régénérer lors de sa prochaine exécution. La prochaine fois, vous économiserez le temps nécessaire pour parsingr l’arborescence. Ne négligez pas cela car il s’agit d’une opération très coûteuse qui devient encore pire avec des requêtes plus complexes.

Il existe deux façons de comstackr une requête: créer un object ObjectQuery avec EntitySQL et utiliser la fonction ComstackdQuery.Comstack (). (Notez qu’en utilisant EntityDataSource dans votre page, vous utiliserez en fait ObjectQuery avec EntitySQL, ce qui signifie qu’il sera compilé et mis en cache).

Un côté ici si vous ne savez pas ce qu’est EntitySQL. C’est une méthode basée sur des chaînes pour écrire des requêtes sur le EF. Voici un exemple: “select value dog from Entities.DogSet as dog où dog.ID = @ID”. La syntaxe est assez similaire à la syntaxe SQL. Vous pouvez également faire une manipulation d’object assez complexe, ce qui est bien expliqué [ici] [1].

Ok, voici comment le faire en utilisant ObjectQuery

  ssortingng query = "select value dog " + "from Entities.DogSet as dog " + "where dog.ID = @ID"; ObjectQuery oQuery = new ObjectQuery(query, EntityContext.Instance)); oQuery.Parameters.Add(new ObjectParameter("ID", id)); oQuery.EnablePlanCaching = true; return oQuery.FirstOrDefault(); 

La première fois que vous exécutez cette requête, le framework va générer l’arborescence d’expression et la conserver en mémoire. Donc, la prochaine fois qu’il sera exécuté, vous économiserez sur cette étape coûteuse. Dans cet exemple, EnablePlanCaching = true, ce qui est inutile car c’est l’option par défaut.

L’autre façon de comstackr une requête pour une utilisation ultérieure est la méthode ComstackdQuery.Comstack. Cela utilise un délégué:

  static readonly Func query_GetDog = ComstackdQuery.Comstack((ctx, id) => ctx.DogSet.FirstOrDefault(it => it.ID == id)); 

ou en utilisant linq

  static readonly Func query_GetDog = ComstackdQuery.Comstack((ctx, id) => (from dog in ctx.DogSet where dog.ID == id select dog).FirstOrDefault()); 

appeler la requête:

 query_GetDog.Invoke( YourContext, id ); 

L’avantage de ComstackdQuery est que la syntaxe de votre requête est vérifiée au moment de la compilation, contrairement à EntitySQL. Cependant, il y a d’autres considérations …

Comprend

Disons que vous voulez avoir les données pour que le propriétaire du chien soit renvoyé par la requête pour éviter de faire 2 appels à la firebase database. Facile à faire, non?

EntitySQL

  ssortingng query = "select value dog " + "from Entities.DogSet as dog " + "where dog.ID = @ID"; ObjectQuery oQuery = new ObjectQuery(query, EntityContext.Instance)).Include("Owner"); oQuery.Parameters.Add(new ObjectParameter("ID", id)); oQuery.EnablePlanCaching = true; return oQuery.FirstOrDefault(); 

ComstackdQuery

  static readonly Func query_GetDog = ComstackdQuery.Comstack((ctx, id) => (from dog in ctx.DogSet.Include("Owner") where dog.ID == id select dog).FirstOrDefault()); 

Maintenant, que faire si vous souhaitez que l’inclusion soit paramétrée? Ce que je veux dire, c’est que vous voulez avoir une seule fonction Get () appelée depuis différentes pages qui se soucient des différentes relations pour le chien. On se soucie du Propriétaire, un autre de son FavoriteFood, un autre de son FavotireToy et ainsi de suite. De manière simple, vous voulez indiquer à la requête quelles associations charger.

C’est facile à faire avec EntitySQL

 public Dog Get(int id, ssortingng include) { ssortingng query = "select value dog " + "from Entities.DogSet as dog " + "where dog.ID = @ID"; ObjectQuery oQuery = new ObjectQuery(query, EntityContext.Instance)) .IncludeMany(include); oQuery.Parameters.Add(new ObjectParameter("ID", id)); oQuery.EnablePlanCaching = true; return oQuery.FirstOrDefault(); } 

L’inclusion utilise simplement la chaîne transmise. Assez facile. Notez qu’il est possible d’améliorer la fonction Include (ssortingng) (qui n’accepte qu’un seul chemin) avec un IncludeMany (chaîne) qui vous permettra de passer une chaîne d’associations séparées par des virgules à charger. Regardez plus loin dans la section d’extension pour cette fonction.

Si nous essayons de le faire avec ComstackdQuery cependant, nous rencontrons de nombreux problèmes:

L’évidence

  static readonly Func query_GetDog = ComstackdQuery.Comstack((ctx, id, include) => (from dog in ctx.DogSet.Include(include) where dog.ID == id select dog).FirstOrDefault()); 

va s’étouffer lorsqu’il est appelé avec:

 query_GetDog.Invoke( YourContext, id, "Owner,FavoriteFood" ); 

Parce que, comme mentionné ci-dessus, Include () veut seulement voir un seul chemin dans la chaîne et nous lui donnons ici 2: “Owner” et “FavoriteFood” (à ne pas confondre avec “Owner.FavoriteFood”!).

Ensuite, utilisons IncludeMany (), qui est une fonction d’extension

  static readonly Func query_GetDog = ComstackdQuery.Comstack((ctx, id, include) => (from dog in ctx.DogSet.IncludeMany(include) where dog.ID == id select dog).FirstOrDefault()); 

Encore une fois, cette fois-ci c’est parce que le EF ne peut pas parsingr IncludeMany car il ne fait pas partie des fonctions reconnues: c’est une extension.

Ok, donc vous voulez passer un nombre arbitraire de chemins à votre fonction et Includes () n’en prend qu’un seul. Que faire? Vous pourriez décider que vous n’aurez jamais besoin de plus que 20, par exemple, et que vous passiez chaque chaîne séparée dans une structure à ComstackdQuery. Mais maintenant, la requête ressemble à ceci:

 from dog in ctx.DogSet.Include(include1).Include(include2).Include(include3) .Include(include4).Include(include5).Include(include6) .[...].Include(include19).Include(include20) where dog.ID == id select dog 

ce qui est terrible aussi. Ok, alors attendez une minute. Ne pouvons-nous pas retourner un object ObjectQuery avec CompiledQuery? Ensuite, définissez les inclus sur cela? Eh bien, ce que j’aurais pensé ainsi:

  static readonly Func<Entities, int, ObjectQuery> query_GetDog = ComstackdQuery.Comstack<Entities, int, string, ObjectQuery>((ctx, id) => (ObjectQuery)(from dog in ctx.DogSet where dog.ID == id select dog)); public Dog GetDog( int id, ssortingng include ) { ObjectQuery oQuery = query_GetDog(id); oQuery = oQuery.IncludeMany(include); return oQuery.FirstOrDefault; } 

Cela aurait dû fonctionner, sauf que lorsque vous appelez IncludeMany (ou Include, Where, OrderBy …), vous invalidez la requête compilée en cache car elle est entièrement nouvelle maintenant! Ainsi, l’arborescence des expressions doit être reparsée et vous obtenez à nouveau ces performances.

Alors, quelle est la solution? Vous ne pouvez simplement pas utiliser ComstackdQueries avec Includes inclus. Utilisez plutôt EntitySQL. Cela ne signifie pas qu’il n’y a pas d’utilisations pour ComstackdQueries. C’est génial pour les requêtes localisées qui seront toujours appelées dans le même contexte. Idéalement, ComstackdQuery devrait toujours être utilisé car la syntaxe est vérifiée au moment de la compilation, mais en raison de la limitation, ce n’est pas possible.

Un exemple d’utilisation serait: vous pouvez avoir une page qui demande quels deux chiens ont le même aliment favori, ce qui est un peu étroit pour une fonction BusinessLayer, donc vous le mettez dans votre page et savez exactement quel type d’inclusion sont Champs obligatoires.

Passer plus de 3 parameters à un ComstackdQuery

Func est limité à 5 parameters, dont le dernier est le type de retour et le premier est votre object Entités du modèle. Donc, cela vous laisse avec 3 parameters. Une pitance, mais on peut l’améliorer très facilement.

 public struct MyParams { public ssortingng param1; public int param2; public DateTime param3; } static readonly Func<Entities, MyParams, IEnumerable> query_GetDog = ComstackdQuery.Comstack<Entities, MyParams, IEnumerable>((ctx, myParams) => from dog in ctx.DogSet where dog.Age == myParams.param2 && dog.Name == myParams.param1 and dog.BirthDate > myParams.param3 select dog); public List GetSomeDogs( int age, ssortingng Name, DateTime birthDate ) { MyParams myParams = new MyParams(); myParams.param1 = name; myParams.param2 = age; myParams.param3 = birthDate; return query_GetDog(YourContext,myParams).ToList(); } 

Types de retour (cela ne s’applique pas aux requêtes EntitySQL car elles ne sont pas compilées en même temps pendant l’exécution en tant que méthode ComstackdQuery)

En travaillant avec Linq, vous ne forcez généralement pas l’exécution de la requête jusqu’au tout dernier moment, au cas où d’autres fonctions en aval voudraient modifier la requête d’une manière ou d’une autre:

  static readonly Func<Entities, int, string, IEnumerable> query_GetDog = ComstackdQuery.Comstack<Entities, int, string, IEnumerable>((ctx, age, name) => from dog in ctx.DogSet where dog.Age == age && dog.Name == name select dog); public IEnumerable GetSomeDogs( int age, ssortingng name ) { return query_GetDog(YourContext,age,name); } public void DataBindStuff() { IEnumerable dogs = GetSomeDogs(4,"Bud"); // but I want the dogs ordered by BirthDate gridView.DataSource = dogs.OrderBy( it => it.BirthDate ); } 

Que va-t-il se passer ici? En jouant toujours avec l’ObjectQuery d’origine (c’est-à-dire le type de retour réel de l’instruction Linq, qui implémente IEnumerable), il invalidera la requête compilée et sera forcée de ré-parsingr. Donc, la règle est de retourner une liste d’objets à la place.

  static readonly Func<Entities, int, string, IEnumerable> query_GetDog = ComstackdQuery.Comstack<Entities, int, string, IEnumerable>((ctx, age, name) => from dog in ctx.DogSet where dog.Age == age && dog.Name == name select dog); public List GetSomeDogs( int age, ssortingng name ) { return query_GetDog(YourContext,age,name).ToList(); //<== change here } public void DataBindStuff() { List dogs = GetSomeDogs(4,"Bud"); // but I want the dogs ordered by BirthDate gridView.DataSource = dogs.OrderBy( it => it.BirthDate ); } 

Lorsque vous appelez ToList (), la requête est exécutée conformément à la requête compilée, puis, ultérieurement, OrderBy est exécuté sur les objects en mémoire. C’est peut-être un peu plus lent, mais je n’en suis même pas sûr. Une chose sûre est que vous n’avez pas à vous soucier de la mauvaise manipulation de ObjectQuery et de l’invalidation du plan de requête compilé.

Encore une fois, ce n’est pas une déclaration générale. ToList () est une astuce de programmation défensive, mais si vous avez une raison valable de ne pas utiliser ToList (), continuez. Il existe de nombreux cas dans lesquels vous souhaitez affiner la requête avant de l’exécuter.

Performance

Quel est l’impact de la compilation d’une requête sur les performances? Il peut en fait être assez grand. Une règle de base est que la compilation et la mise en cache de la requête pour la réutilisation prennent au moins le double de la simple exécution sans mise en cache. Pour les requêtes complexes (lire inherirante), j’ai vu jusqu’à 10 secondes.

Ainsi, la première fois qu’une requête pré-compilée est appelée, vous obtenez un impact sur les performances. Après ce premier hit, les performances sont nettement meilleures que la même requête non pré-compilée. Pratiquement le même que Linq2Sql

Lorsque vous chargez une page avec des requêtes pré-compilées la première fois, vous obtiendrez un hit. Il se chargera peut-être en 5 à 15 secondes (évidemment, plus d’une requête pré-compilée sera appelée), alors que les chargements suivants prendront moins de 300 ms. Différence dramatique, et c’est à vous de décider si votre premier utilisateur peut prendre un coup ou si vous voulez qu’un script appelle vos pages pour forcer une compilation des requêtes.

Cette requête peut-elle être mise en cache?

 { Dog dog = from dog in YourContext.DogSet where dog.ID == id select dog; } 

Non, les requêtes ad hoc de Linq ne sont pas mises en cache et vous devrez payer le coût de génération de l’arborescence à chaque fois que vous l’appelez.

Requêtes paramétrées

La plupart des fonctionnalités de recherche impliquent des requêtes fortement paramétrées. Il existe même des bibliothèques disponibles qui vous permettront de créer une requête paramétrée à partir des expressions lamba. Le problème est que vous ne pouvez pas utiliser de requêtes pré-compilées avec celles-ci. Une solution consiste à définir tous les critères possibles dans la requête et à indiquer celui que vous souhaitez utiliser:

 public struct MyParams { public ssortingng name; public bool checkName; public int age; public bool checkAge; } static readonly Func<Entities, MyParams, IEnumerable> query_GetDog = ComstackdQuery.Comstack<Entities, MyParams, IEnumerable>((ctx, myParams) => from dog in ctx.DogSet where (myParams.checkAge == true && dog.Age == myParams.age) && (myParams.checkName == true && dog.Name == myParams.name ) select dog); protected List GetSomeDogs() { MyParams myParams = new MyParams(); myParams.name = "Bud"; myParams.checkName = true; myParams.age = 0; myParams.checkAge = false; return query_GetDog(YourContext,myParams).ToList(); } 

L’avantage ici est que vous obtenez tous les avantages d’un quert pré-compilé. Les inconvénients sont que vous allez probablement vous retrouver avec une clause where qui est assez difficile à gérer, que vous aurez une plus grande pénalité pour la pré-compilation de la requête et que chaque requête que vous exécutez n’est pas aussi efficace que possible (en particulier avec des jointures jetées).

Une autre méthode consiste à créer une requête EntitySQL pièce par pièce, comme nous l’avons fait avec SQL.

 protected List GetSomeDogs( ssortingng name, int age) { ssortingng query = "select value dog from Entities.DogSet where 1 = 1 "; if( !Ssortingng.IsNullOrEmpty(name) ) query = query + " and dog.Name == @Name "; if( age > 0 ) query = query + " and dog.Age == @Age "; ObjectQuery oQuery = new ObjectQuery( query, YourContext ); if( !Ssortingng.IsNullOrEmpty(name) ) oQuery.Parameters.Add( new ObjectParameter( "Name", name ) ); if( age > 0 ) oQuery.Parameters.Add( new ObjectParameter( "Age", age ) ); return oQuery.ToList(); } 

Ici, les problèmes sont: – il n’y a pas de vérification de syntaxe pendant la compilation – chaque combinaison de parameters génère une requête différente qui devra être pré-compilée lors de sa première exécution. Dans ce cas, il n’y a que 4 requêtes différentes possibles (pas de parameters, seulement âge, nom seul et les deux parameters), mais vous pouvez voir qu’il peut y en avoir beaucoup plus avec une recherche mondiale normale. – Personne n’aime concaténer des cordes!

Une autre option consiste à interroger un grand sous-dataset, puis à le réduire en mémoire. Ceci est particulièrement utile si vous travaillez avec un sous-ensemble défini des données, comme tous les chiens d’une ville. Vous savez qu’il y en a beaucoup mais vous savez aussi qu’il n’y en a pas beaucoup … alors votre page de recherche CityDog peut charger tous les chiens de la ville en mémoire, ce qui est une requête unique pré-compilée, puis affiner les résultats

 protected List GetSomeDogs( ssortingng name, int age, ssortingng city) { ssortingng query = "select value dog from Entities.DogSet where dog.Owner.Address.City == @City "; ObjectQuery oQuery = new ObjectQuery( query, YourContext ); oQuery.Parameters.Add( new ObjectParameter( "City", city ) ); List dogs = oQuery.ToList(); if( !Ssortingng.IsNullOrEmpty(name) ) dogs = dogs.Where( it => it.Name == name ); if( age > 0 ) dogs = dogs.Where( it => it.Age == age ); return dogs; } 

Il est particulièrement utile lorsque vous commencez à afficher toutes les données, puis autorisez le filtrage.

Problèmes: – Peut entraîner un transfert de données sérieux si vous ne faites pas attention à votre sous-ensemble. – Vous ne pouvez filtrer que sur les données que vous avez renvoyées. Cela signifie que si vous ne retournez pas l’association Dog.Owner, vous ne pourrez pas filtrer sur Dog.Owner.Name Alors quelle est la meilleure solution? Il n’y en a pas. Vous devez choisir la solution qui vous convient le mieux, à vous et à votre problème: – Utilisez la création de requêtes basée sur lambda lorsque vous ne pré-comstackz pas vos requêtes. – Utilisez une requête Linq pré-compilée entièrement définie lorsque la structure de votre object n’est pas trop complexe. – Utiliser la concaténation EntitySQL / ssortingng lorsque la structure peut être complexe et lorsque le nombre possible de requêtes résultantes différentes est petit (ce qui signifie moins de hits de pré-compilation). – Utilisez le filtrage en mémoire lorsque vous travaillez avec un petit sous-dataset ou lorsque vous devez récupérer toutes les données sur les données de toute façon (si les performances correspondent à toutes les données, le filtrage en mémoire ne sera pas possible). provoquer tout temps à passer dans la firebase database).

Accès singleton

La meilleure façon de gérer votre contexte et vos entités dans toutes vos pages consiste à utiliser le modèle singleton:

 public sealed class YourContext { private const ssortingng instanceKey = "On3GoModelKey"; YourContext(){} public static YourEntities Instance { get { HttpContext context = HttpContext.Current; if( context == null ) return Nested.instance; if (context.Items[instanceKey] == null) { On3GoEntities entity = new On3GoEntities(); context.Items[instanceKey] = entity; } return (YourEntities)context.Items[instanceKey]; } } class Nested { // Explicit static constructor to tell C# comstackr // not to mark type as beforefieldinit static Nested() { } internal static readonly YourEntities instance = new YourEntities(); } } 

NoTracking, ça vaut le coup?

Lors de l’exécution d’une requête, vous pouvez indiquer au framework de suivre les objects qu’il retournera ou non. Qu’est-ce que ça veut dire? Avec le suivi activé (option par défaut), le framework suivra ce qui se passe avec l’object (a-t-il été modifié? Créé? Supprimé?) Et liera également les objects lorsque des requêtes supplémentaires seront faites à partir de la firebase database. est intéressant ici.

Par exemple, supposons que Dog with ID == 2 a un propriétaire dont l’ID == 10.

 Dog dog = (from dog in YourContext.DogSet where dog.ID == 2 select dog).FirstOrDefault(); //dog.OwnerReference.IsLoaded == false; Person owner = (from o in YourContext.PersonSet where o.ID == 10 select dog).FirstOrDefault(); //dog.OwnerReference.IsLoaded == true; 

Si nous devions faire la même chose sans suivi, le résultat serait différent.

 ObjectQuery oDogQuery = (ObjectQuery) (from dog in YourContext.DogSet where dog.ID == 2 select dog); oDogQuery.MergeOption = MergeOption.NoTracking; Dog dog = oDogQuery.FirstOrDefault(); //dog.OwnerReference.IsLoaded == false; ObjectQuery oPersonQuery = (ObjectQuery) (from o in YourContext.PersonSet where o.ID == 10 select o); oPersonQuery.MergeOption = MergeOption.NoTracking; Owner owner = oPersonQuery.FirstOrDefault(); //dog.OwnerReference.IsLoaded == false; 

Le suivi est très utile et dans un monde parfait sans problème de performance, il serait toujours actif. Mais dans ce monde, il y a un prix à payer, en termes de performance. Alors, devriez-vous utiliser NoTracking pour accélérer les choses? Cela dépend de l’utilisation prévue des données.

Est-il possible d’utiliser les données de votre requête avec NoTracking pour mettre à jour / insérer / supprimer dans la firebase database? Si tel est le cas, n’utilisez pas NoTracking car les associations ne sont pas suivies et provoqueront des exceptions.

Dans une page où il n’y a absolument aucune mise à jour de la firebase database, vous pouvez utiliser NoTracking.

Le suivi du mixage et le NoTracking sont possibles, mais vous devez faire très attention aux mises à jour / insertions / suppressions. Le problème est que si vous mélangez alors vous risquez d’avoir le framework essayant d’attacher () un object NoTracking au contexte où une autre copie du même object existe avec le suivi. Fondamentalement, ce que je dis, c’est que

 Dog dog1 = (from dog in YourContext.DogSet where dog.ID == 2).FirstOrDefault(); ObjectQuery oDogQuery = (ObjectQuery) (from dog in YourContext.DogSet where dog.ID == 2 select dog); oDogQuery.MergeOption = MergeOption.NoTracking; Dog dog2 = oDogQuery.FirstOrDefault(); 

dog1 et dog2 sont 2 objects différents, l’un suivi et l’autre non. L’utilisation de l’object détaché dans une mise à jour / insertion forcera Attach () à dire “Attendez une minute, j’ai déjà un object avec la même clé de firebase database. Fail”. Et lorsque vous attachez un object, toute sa hiérarchie est également attachée, causant des problèmes partout. Soyez très prudent.

Combien de temps est-il plus rapide avec NoTracking

Cela dépend des requêtes. Certains sont beaucoup plus faciles à suivre que d’autres. Je n’ai pas de règle facile pour ça, mais ça aide.

Donc, je devrais utiliser NoTracking partout alors?

Pas exactement. Il y a des avantages à suivre un object. Le premier est que l’object est mis en cache, donc l’appel ultérieur pour cet object ne touchera pas la firebase database. Ce cache n’est valide que pour la durée de vie de l’object YourEntities, qui, si vous utilisez le code singleton ci-dessus, est identique à la durée de vie de la page. Demande d’une page == un object YourEntity. Ainsi, pour plusieurs appels pour le même object, il ne se chargera qu’une fois par demande de page. (Un autre mécanisme de mise en cache pourrait étendre cela).

Que se passe-t-il lorsque vous utilisez NoTracking et que vous essayez de charger plusieurs fois le même object? La firebase database sera interrogée à chaque fois, donc il y a un impact là-bas. À quelle fréquence appelez-vous / devriez-vous appeler le même object pendant une seule demande de page? Aussi peu que possible, bien sûr, mais ça arrive.

Rappelez-vous également le point ci-dessus concernant la connexion automatique des associations pour votre compte? Vous ne l’avez pas avec NoTracking, donc si vous chargez vos données en plusieurs lots, vous n’aurez pas de lien entre eux:

 ObjectQuery oDogQuery = (ObjectQuery)(from dog in YourContext.DogSet select dog); oDogQuery.MergeOption = MergeOption.NoTracking; List dogs = oDogQuery.ToList(); ObjectQuery oPersonQuery = (ObjectQuery)(from o in YourContext.PersonSet select o); oPersonQuery.MergeOption = MergeOption.NoTracking; List owners = oPersonQuery.ToList(); 

Dans ce cas, aucun chien n’aura son ensemble de propriétés .Owner.

Certaines choses à garder à l’esprit lorsque vous essayez d’optimiser les performances.

Pas de chargement paresseux, que dois-je faire?

Cela peut être considéré comme une bénédiction déguisée. Bien sûr, il est embêtant de tout charger manuellement. Cependant, cela réduit le nombre d’appels à la firebase database et vous oblige à réfléchir au moment où vous devez charger des données. Plus vous pouvez charger dans une firebase database, mieux c’est. C’était toujours vrai, mais il est maintenant appliqué avec cette “fonctionnalité” d’EF.

Bien sûr, vous pouvez appeler if (! ObjectReference.IsLoaded) ObjectReference.Load (); Si vous le souhaitez, mais une meilleure pratique est de forcer le framework à charger les objects dont vous avez besoin en une seule fois. C’est là que la discussion sur les inclusions paramétrées commence à avoir un sens.

Disons que vous avez vous object chien

 public class Dog { public Dog Get(int id) { return YourContext.DogSet.FirstOrDefault(it => it.ID == id ); } } 

C’est le type de fonction avec lequel vous travaillez tout le temps. Il est appelé de partout et une fois que vous avez cet object Dog, vous allez faire des choses très différentes dans différentes fonctions. Tout d’abord, il doit être pré-compilé, car vous appellerez cela très souvent. Deuxièmement, chaque page voudra avoir access à un sous-ensemble différent des données du chien. Certains voudront le propriétaire, certains le favori, etc.

Bien sûr, vous pouvez appeler Load () pour chaque référence dont vous avez besoin. Mais cela générera un appel à la firebase database à chaque fois. Mauvaise idée. Ainsi, chaque page demandera les données qu’elle souhaite voir lorsqu’elle demande pour la première fois l’object Dog:

  static public Dog Get(int id) { return GetDog(entity,"");} static public Dog Get(int id, ssortingng includePath) { ssortingng query = "select value o " + " from YourEntities.DogSet as o " + 

S’il vous plaît ne pas utiliser toutes les informations ci-dessus telles que “Singleton Access”. Vous devez absolument 100% ne pas stocker ce contexte pour le réutiliser car il n’est pas thread-safe.

Bien qu’informatif, je pense qu’il peut être plus utile de partager comment tout cela s’intègre dans une architecture de solution complète. Exemple – Vous avez une solution montrant à quel endroit vous utilisez l’inheritance EF et votre alternative afin de montrer leur différence de performances.