c#, entity framework: utilisation correcte de la classe DBContext à l'intérieur de votre référentiel de la classe
J'ai utilisé pour mettre en œuvre mes classes de dépôt comme vous pouvez le voir ci-dessous
public Class MyRepository
{
private MyDbContext _context;
public MyRepository(MyDbContext context)
{
_context = context;
}
public Entity GetEntity(Guid id)
{
return _context.Entities.Find(id);
}
}
Cependant j'ai lu récemment cet article qui dit que c'est une mauvaise pratique d'avoir des données de contexte comme un membre privé dans votre référentiel: http://devproconnections.com/development/solving-net-scalability-problem
Maintenant, théoriquement, l'article est en droit: depuis DbContext implémente IDisposable de la correcte mise en œuvre devrait être le suivant.
public Class MyRepository
{
public Entity GetEntity(Guid id)
{
using (MyDbContext context = new MyDBContext())
{
return context.Entities.Find(id);
}
}
}
Toutefois, selon cet autre article de l'élimination DbContext serait pas indispensable: http://blog.jongallant.com/2012/10/do-i-have-to-call-dispose-on-dbcontext.html
Lequel des deux articles est à droite? Je suis assez confus.
Ayant DbContext en tant que membre privé dans le référentiel de la classe peut vraiment causer des "problèmes d'évolutivité" le premier article le suggère?
- J'ai toujours compris DBContext ne doit être ouvert pour une unité de travail
- Alors, préférez-vous le deuxième code?
- C'est mieux pour moi oui
- Dix réponses. "Première approche", "deuxième approche", "une autre approche". C'est un exemple de manuel de principalement une opinion basée sur la question. Je vote pour la fermer si il n'y avait pas la bounty.
- Arnold comment est-ce qu'un avis basé sur la question? Se demander quelle est la façon correcte d'utiliser une langue ou d'un cadre n'est pas une opinion.
Vous devez vous connecter pour publier un commentaire.
Je pense que vous ne devrait pas suivre le premier article, et je vais vous dire pourquoi.
À la suite de la première approche, vous êtes de perdre à peu près chaque fonctionnalité que Entity Framework fournit via le
DbContext
, y compris son 1er-niveau de cache, sa carte d'identité, son unité de travail, et son changement de suivi et de chargement différé des capacités. C'est parce que dans le scénario ci-dessus, une nouvelleDbContext
instance est créée pour chaque requête de base de données et éliminés immédiatement après, empêchant ainsi leDbContext
instance d'être en mesure de suivre l'état de vos objets de données à travers l'ensemble de l'opération commerciale.Avoir un
DbContext
comme une propriété privée dans le référentiel de la classe a aussi ses problèmes. Je crois que la meilleure approche est d'avoir un CustomDbContextScope. Cette approche est très bien expliqué par ce mec: Mehdi El GueddariCet article http://mehdi.me/ambient-dbcontext-in-ef6/ l'un des meilleurs articles sur EntityFramework que j'ai vu. Vous devriez le lire entièrement, et je crois qu'il va répondre à toutes vos questions.
Supposons, vous avez plus d'un dépôt et que vous devez mettre à jour 2 enregistrements à partir des référentiels différents. Et vous devez le faire transactionnelle (si l'on omet - les deux mises à jour restaurations):
Donc, si vous avez propre DbContext pour chaque référentiel (cas 2), vous devez utiliser TransactionScope pour atteindre cet objectif.
De mieux - ont partagé DbContext pour une opération (pour un simple appel, pour un unité de travail). Donc, DbContext pouvez gérer la transaction. EF est assez pour que.
Vous pouvez créer un seul DbContext, faire toutes les modifications dans de nombreux référentiels, appelez SaveChanges une fois, mettez-la au rebut après toutes les opérations et le travail est fait.
Ici est un exemple de UnitOfWork modèle de mise en œuvre.
Votre deuxième voie peut être bon pour les opérations en lecture seule.
La règle de la racine est : votre DbContext durée de vie devrait être limitée à la transaction que vous êtes en cours d'exécution.
Ici, "transaction" peut se référer à une requête en lecture seule ou en écriture de la requête.
Et comme vous le savez peut-être déjà, une transaction doit être aussi court que possible.
Cela dit, je dirais que vous devriez faveur de la "aide" dans la plupart des cas et ne pas utiliser un membre privé.
Le seul cas que je peux voir à l'usage d'un membre privé est un Modèle CQRS (CQRS : UNE Croix de l'Examen De la Façon dont Il Fonctionne).
Par ailleurs, le Diego de la Vega de réponse dans le Jon Gallant post donne également quelques conseils avisés :
HTH
De l'approche à utiliser dépend de la responsabilité du référentiel.
Est de la responsabilité du référentiel pour effectuer des transactions? c'est à dire d'apporter des modifications, puis enregistrer les modifications apportées à la base de données en appelant `SaveChanges? Ou est-ce seulement une partie d'une plus grande transaction et, par conséquent, il ne apporter des modifications sans les enregistrer?
Cas n ° 1) Le dépôt d'exécuter des transactions (il faudra effectuer les modifications et les enregistrer):
Dans ce cas, la seconde approche est le meilleur (l'approche de votre deuxième exemple de code).
Je ne modifier cette approche légèrement par l'introduction d'une usine comme ceci:
Je suis en train de faire cette petite modification pour permettre L'Injection De Dépendance. Cela nous permet de modifier ultérieurement la façon dont nous créons le contexte.
Je ne veux pas le référentiel d'avoir la responsabilité de créer le contexte lui-même. L'usine, qui met en œuvre
IFactory<MyContext>
aura la responsabilité de créer le contexte.Remarquez comment le référentiel est la gestion de la durée de vie du contexte, il crée le contexte, ne fait quelques modifications, enregistrez les modifications, puis cède le contexte. Le référentiel a une durée de vie plus longue que le contexte dans ce cas.
Cas #2) Le référentiel est partie d'une grande opération (il va faire quelques changements, d'autres référentiels de faire d'autres changements, et puis quelqu'un d'autre va valider la transaction en invoquant
SaveChanges
):Dans ce cas, la première approche (que vous décrivez d'abord dans votre question), c'est mieux.
Imaginer ce que cela va à comprendre comment l'espace de stockage peut être une partie d'une plus grande transaction:
Veuillez noter qu'une nouvelle instance du référentiel est utilisé pour chaque transaction. Cela signifie que la durée de vie du référentiel est très court.
Veuillez noter que je suis en nouvelle-ing le référentiel dans mon code (qui est une violation de l'injection de dépendance). Je suis juste montrer un exemple. Dans le code réel, nous pouvons utiliser les usines de résoudre ce problème.
Maintenant, l'une des améliorations que nous pouvons faire de cette approche est de cacher le contexte derrière une interface afin que le référentiel n'a plus accès à
SaveChanges
(jetez un oeil à la L'Interface De La Ségrégation Principe).Vous pouvez avoir quelque chose comme ceci:
Vous pouvez ajouter d'autres méthodes que vous avez besoin de l'interface si vous le souhaitez.
Veuillez noter également que cette interface n'hérite pas de
IDisposable
. Ce qui signifie que leRepository
classe n'est pas responsable de la gestion de la durée de vie du contexte. Le contexte, dans ce cas, une plus grande durée de vie que le référentiel. Quelqu'un d'autre s'occupera de la gestion de la durée de vie du contexte.Notes sur le premier article:
Le premier article suggère que vous ne devriez pas utiliser la première méthode que vous décrivez dans votre question (injecter le contexte dans le référentiel).
L'article n'est pas clair sur la façon dont le référentiel est utilisé. Il est utilisé comme une partie d'une seule opération? Ou faut-il s'étendre sur plusieurs transactions?
Je suppose (je ne suis pas sûr), que dans l'approche que l'article est de décrire (négativement), le référentiel est utilisé comme une mesure d'exécuter le service qui va s'étaler sur un grand nombre de transactions. Dans ce cas je suis d'accord avec l'article.
Mais ce que je suggère ici, c'est différent, je suggère que cette approche est utilisée uniquement dans le cas où la création d'une nouvelle instance du référentiel est créé chaque fois qu'une opération est nécessaire.
Notes sur le deuxième article:
Je pense que ce que le deuxième article est d'en parler est pas pertinent pour quelle approche vous devriez utiliser.
Le second article, il est question de savoir s'il est nécessaire de disposer du contexte, dans tous les cas (pas pertinent pour la conception du référentiel).
Veuillez noter que dans les deux approches de conception, nous sommes à l'aliénation du contexte. La seule différence, c'est qui est responsable de cette disposition.
L'article est de dire que le
DbContext
semblent propres ressources, sans la nécessité de disposer du contexte explicitement.IDatabaseContext
à l'intérieur de dépôt, si elle ne contient pas toutDbContext
méthodes par exemple quecontext.Database.ExecuteSqlCommand
?Le premier article que vous avez lié oublié de préciser une chose importante: quelle est la durée de vie de la soi-disant
NonScalableUserRepostory
instances (il a aussi oublié de faireNonScalableUserRepostory
implémenteIDisposable
trop, afin de disposer correctement lesDbContext
exemple).Imaginez le cas suivant:
Bien... il y en aurait encore privé
DbContext
champ à l'intérieur de laNonScalableUserRepostory
classe, mais le contexte ne seront utilisées une fois. Donc, c'est exactement la même chose que ce que l'article décrit comme la meilleure pratique.Donc, la question n'est pas "dois-je utiliser un membre privé vs à l'aide d'instruction ?", c'est plus "quelle devrait être la durée de vie de mon contexte ?".
La réponse serait donc: essayer de raccourcir autant que possible. Il y a la notion de Unité De Travail, ce qui représente une opération commerciale. Fondamentalement, vous devez avoir un nouveau
DbContext
pour chaque unité de travail.La façon dont une unité de travail est défini et comment il est mis en œuvre dépendra de la nature de votre demande ; par exemple, pour un ASP.Net MVC de l'application, la durée de vie de la
DbContext
est généralement la durée de vie de laHttpRequest
, c'est à dire un nouveau contexte est créé à chaque fois que l'utilisateur génère une nouvelle demande web.EDIT :
Pour répondre à votre commentaire:
Une solution consisterait à injecter via le constructeur, une méthode de fabrique. Voici quelques exemple de base:
using
déclaration,IRepository
doit être à usage unique. Merci pour l'editLe 1er code n'a pas rapport à la scability problème, la raison pour laquelle elle est mauvaise, c'est parce qu'il créer un nouveau contexte pour chaque référentiel qui est mauvais, qui l'un des commentateurs de commentaires, mais il n'a pas encore de réponse. Dans le web, il a été 1 demande 1 dbContext, si vous prévoyez d'utiliser un modèle de référentiel, puis il se traduire en 1 demande > beaucoup de référentiel > 1 dbContext. ce facile à réaliser avec du Cio, mais pas nécessaire. c'est la façon dont vous le faites sans Cio:
Que pour l'élimination ou pas, j'ai l'habitude de jeter, mais si la conduire lui-même a dit cela, alors probablement aucune raison de le faire. Je voudrais juste jeter si il a IDisposable.
Fondamentalement DbContext classe n'est rien mais une surcouche qui gère l'ensemble de la base de données liée animaux comme:
1. Créer une connexion
2. Exécution De La Requête.
Maintenant, si nous faisons le ci-dessus mentionné choses en utilisant la normale ado.net ensuite, nous avons besoin de fermer explicitement la connexion correctement par écrit le code à l'aide de déclaration ou en appelant la méthode close() sur la classe de connexion de l'objet.
Maintenant, comme le contexte de la classe implémente l'interface IDisposable en interne, il est de bonne pratique pour écrire le dbcontext à l'aide de déclaration, de sorte que nous n'avons pas besoin de soins à propos de la fermeture de la connexion.
Grâce.
using
modèle trop).DbContext
occupe également de la mise en cache, le suivi des modifications et beaucoup plus. Je ne suis pas absolument sûr, mais je pense que la connexion est ouverte et fermée sur chaque db appel.J'utilise la première méthode (l'injection de la dbContext) bien sûr, il devrait être un IMyDbContext et votre dépendance à l'injection du moteur, c'est de gérer le cycle de vie du contexte, de sorte que c'est seulement de vivre, alors qu'il est nécessaire.
Cela vous permet de vous moquer du contexte pour les tests, le deuxième moyen le fait qu'il est impossible d'examiner les entités sans une base de données pour le contexte d'utilisation.
Deuxième approche (à l'aide) est meilleur, car il détient la connexion que pour le minimum de temps nécessaire et il est plus facile de faire des thread-safe.
Je pense que cette première approche est la meilleure, vous n'aurez jamais à créer un dbcontext pour chaque référentiel, même si vous disposer.
Cependant, dans le premier cas, vous pouvez utiliser un databaseFactory pour instancier un seul dbcontext:
Et d'utiliser cette instance dans le Référentiel: