web-dev-qa-db-fra.com

Quand dois-je créer un nouveau DbContext ()

J'utilise actuellement une DbContext semblable à ceci:

namespace Models
{
    public class ContextDB: DbContext
    {

        public DbSet<User> Users { get; set; }
        public DbSet<UserRole> UserRoles { get; set; }

        public ContextDB()
        {

        }
    }
}

J'utilise alors la ligne suivante en haut deTOUSmes contrôleurs ayant besoin d'accéder à la base de données. Je l’utilise aussi dans ma classe UserRepository, qui contient toutes les méthodes relatives à l’utilisateur (telles que l’obtention de l’utilisateur actif, la vérification de ses rôles, etc.):

ContextDB _db = new ContextDB();

En pensant à cela .. il y a des occasions où un visiteur peut avoir plusieurs DbContexts actifs .. ie. Si je visite un contrôleur qui utilise UserRepository, ce n’est peut-être pas la meilleure des idées, et j’ai quelques questions à ce sujet.

  1. Quand devrais-je créer un nouveau DbContext/devrais-je avoir un contexte global que je transmettrai?
  2. Puis-je avoir un contexte global que je réutilise dans tous les lieux?
  3. Est-ce que cela cause un coup dur à la performance?
  4. Comment tout le monde fait ça?
49
JensB

J'utilise un contrôleur de base qui expose une propriété DataBase à laquelle les contrôleurs dérivés peuvent accéder.

public abstract class BaseController : Controller
{
    public BaseController()
    {
        Database = new DatabaseContext();
    }

    protected DatabaseContext Database { get; set; }

    protected override void Dispose(bool disposing)
    {
        Database.Dispose();
        base.Dispose(disposing);
    }
}

Tous les contrôleurs de mon application dérivent de BaseController et sont utilisés comme suit:

public class UserController : BaseController
{
    [HttpGet]
    public ActionResult Index()
    {
        return View(Database.Users.OrderBy(p => p.Name).ToList());
    }
}

Maintenant, pour répondre à vos questions:

Quand devrais-je créer un nouveau DbContext/devrais-je avoir un contexte global ?__. que je passe autour?

Le contexte doit être créé par demande. Créez le contexte, faites ce que vous devez en faire, puis éliminez-le. Avec la solution de classe de base que j'utilise, vous n'avez plus qu'à vous soucier de l'utilisation du contexte.

N'essayez pas d'avoir un contexte global (ce n'est pas ainsi que fonctionnent les applications Web).

Puis-je avoir un contexte global que je réutilise dans tous les lieux?

Non, si vous gardez un contexte autour, toutes les mises à jour, les ajouts, les suppressions, etc. seront conservés, ce qui ralentira votre application et risque même de provoquer l'apparition de bogues assez subtils dans votre application.

Vous devriez probablement choisir d’exposer votre référentiel ou votre contexte à votre contrôleur mais pas les deux. Si deux contextes sont accessibles à partir de la même méthode, des bogues risquent de se produire s'ils ont des idées différentes sur l'état actuel de l'application.

Personnellement, je préfère exposer DbContext directement comme la plupart des exemples de référentiels que j'ai vus se retrouvent simplement comme de minces wrappers autour de DbContext de toute façon.

Est-ce que cela cause un coup dur à la performance?

La première création d'une DbContext est assez chère, mais une fois que cela est fait, une grande partie de l'information est mise en cache, de sorte que les instanciations ultérieures sont beaucoup plus rapides. vous êtes plus susceptible de constater des problèmes de performances liés à la conservation d'un contexte plutôt qu'à leur instanciation à chaque fois que vous avez besoin d'accéder à votre base de données. 

Comment tout le monde fait ça?

Ça dépend.

Certaines personnes préfèrent utiliser un framework d'injection de dépendance pour transmettre une instance concrète de leur contexte à leur contrôleur lors de sa création. Les deux options sont bien. Le mien est plus approprié pour une application à petite échelle où vous savez que la base de données spécifique utilisée ne va pas changer.

certains diront peut-être que vous ne pouvez pas le savoir et c’est pourquoi la méthode d’injection de dépendance est meilleure car elle rend votre application plus résiliente au changement. Mon opinion à ce sujet est que cela ne changera probablement pas (le serveur SQL et Entity Framework ne sont guère obscurs) et qu'il vaut mieux passer mon temps à écrire le code spécifique à mon application.

60
Benjamin Gale

J'essaie de répondre à partir de ma propre expérience.

1. Quand devrais-je créer un nouveau DbContext/devrais-je avoir un contexte global que je transmettrai?

Le contexte doit être injecté par l'injection de dépendance et ne doit pas être instancié par vous-même. La meilleure pratique consiste à le créer en tant que service limité par l'injection de dépendance. (Voir ma réponse à la question 4)

Pensez également à utiliser une structure d'application en couches appropriée, telle que Contrôleur> BusinessLogic> Référentiel. Dans ce cas, votre contrôleur ne recevra pas le contexte de base de données, mais le référentiel. L'injection/l'instanciation d'un contexte de base de données dans un contrôleur m'indique que l'architecture de votre application mélange de nombreuses responsabilités au même endroit, ce que je ne peux en aucun cas recommander.

2. Puis-je avoir un seul contexte global que je peux réutiliser dans tous les lieux?

Oui, vous pouvez avez mais la question devrait être " Should I have ..." -> NO. Le contexte est destiné à être utilisé à la demande pour modifier votre référentiel, puis à nouveau le supprimer.

3. Est-ce que cela cause un coup dur aux performances?

Oui, car DBContext n’est tout simplement pas fait pour être global. Il stocke toutes les données saisies ou interrogées jusqu'à ce qu'elles soient détruites. Cela signifie qu'un contexte mondial va devenir de plus en plus grand, les opérations sur celui-ci vont devenir de plus en plus lentes jusqu'à ce que vous obteniez une exception de mémoire insuffisante ou que vous mourriez de vieillesse parce que tout cela était ralenti à une vitesse critique.

Vous obtiendrez également des exceptions et de nombreuses erreurs lorsque plusieurs threads accèdent simultanément au même contexte.

4. Comment tout le monde fait-il cela?

DBContext injecté par injection de dépendance par une usine; portée:

services.AddDbContext<UserDbContext>(o => o.UseSqlServer(this.settings.DatabaseOptions.UserDBConnectionString));

J'espère que mes réponses ont été utiles.

3
Ravior

Ceci est évidemment une question plus ancienne, mais si vous utilisez DI, vous pouvez faire quelque chose comme cela et définir tous vos objets pour la durée de vie de la demande.

 public class UnitOfWorkAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            var context = IoC.CurrentNestedContainer.GetInstance<DatabaseContext>();
            context.BeginTransaction();
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionContext)
        {
            var context = IoC.CurrentNestedContainer.GetInstance<DatabaseContext>();
            context.CloseTransaction(actionContext.Exception);
        }
    }
0
CSharper

En ce moment, j'essaie cette approche, qui évite d'instancier le contexte lorsque vous appelez des actions qui ne l'utilisent pas.

public abstract class BaseController : Controller
{
    public BaseController() { }

    private DatabaseContext _database;
    protected DatabaseContext Database
    {
        get
        {
            if (_database == null)
                _database = new DatabaseContext();
            return _database;
        }
    }

    protected override void Dispose(bool disposing)
    {
        if (_database != null)
            _database.Dispose();
        base.Dispose(disposing);
    }
}
0
Andrew