web-dev-qa-db-fra.com

Utiliser DbContext dans ASP .Net Singleton Injected Class

Je dois accéder à ma base de données dans une classe Singleton instanciée dans ma classe Startup. Il semble que son injection aboutisse directement à la suppression d'un DbContext. 

Je reçois l'erreur suivante:

Impossible d'accéder à un objet supprimé. Nom de l'objet: 'MyDbContext'.

Ma question est double: pourquoi cela ne fonctionne-t-il pas et comment puis-je accéder à ma base de données dans une instance de classe singleton?

Voici ma méthode ConfigureServices dans ma classe de démarrage:

public void ConfigureServices(IServiceCollection services)
{
    // code removed for brevity

    services.AddEntityFramework().AddSqlServer().AddDbContext<MyDbContext>(
        options =>
        {
            var config = Configuration["Data:DefaultConnection:ConnectionString"];
            options.UseSqlServer(config);
        });

    // code removed for brevity

    services.AddSingleton<FunClass>();
}

Voici ma classe de contrôleur:

public class TestController : Controller
{
    private FunClass _fun;

    public TestController(FunClass fun)
    {
        _fun = fun;
    }

    public List<string> Index()
    {
        return _fun.GetUsers();
    }
}

Voici mon FunClass:

public class FunClass
{
    private MyDbContext db;

    public FunClass(MyDbContext ctx) {
        db = ctx;
    }

    public List<string> GetUsers()
    {
         var lst = db.Users.Select(c=>c.UserName).ToList();
        return lst;
    }
}
18
Tjaart

Cela ne fonctionne pas parce que l'extension .AddDbContext l'ajoute comme étendue par demande. Portée par demande correspond généralement à ce que vous souhaitez et généralement, les modifications de sauvegarde sont appelées une fois par demande, puis la variable dbcontext est supprimée à la fin de la demande.

Si vous avez vraiment besoin d'utiliser un dbContext dans un singleton, votre classe FunClass devrait probablement prendre une dépendance sur IServiceProvider et DbContextOptions au lieu de prendre directement une dépendance sur DbContext, afin de pouvoir la créer vous-même.

public class FunClass
{
    private GMBaseContext db;

    public FunClass(IServiceProvider services, DbContextOptions dbOptions) 
    {
        db = new GMBaseContext(services, dbOptions);
    }

    public List<string> GetUsers()
    {
         var lst = db.Users.Select(c=>c.UserName).ToList();
        return lst;
    }
}

Cela dit, je vous conseillerais de déterminer avec soin si vous avez vraiment besoin que votre FunClass soit un singleton, ce que je voudrais éviter sauf si vous avez une très bonne raison pour en faire un singleton.

14
Joe Audette

La solution consistait à appeler AddSingleton avec ma classe étant instanciée dans le paramètre method de ma classe Startup:

services.AddSingleton(s => new FunClass(new MyContext(null, Configuration["Data:DefaultConnection:ConnectionString"])));

La solution a été de changer ma classe DbContext:

public class MyContext : IdentityDbContext<ApplicationUser>
{
    private string connectionString;

    public MyContext()
    {

    }

    public MyContext(DbContextOptions options, string connectionString)
    {
        this.connectionString = connectionString;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        // Used when instantiating db context outside IoC 
        if (connectionString != null)
        {
            var config = connectionString;
            optionsBuilder.UseSqlServer(config);
        }

        base.OnConfiguring(optionsBuilder);
    }

}

Comme plusieurs personnes l'ont toutefois prévenu, utiliser un DbContext dans une classe singleton pourrait être une très mauvaise idée. Mon utilisation est très limitée dans le code réel (pas l'exemple FunClass), mais je pense que si vous faites cela, il serait préférable de trouver d'autres moyens.

3
Tjaart

Pas besoin de surcharger le ctor de MyDbContext. 

services.AddSingleton(s=>new FunClass(new MyDbContext(new DbContextOptionsBuilder<MyDbContext>().UseSqlServer(configuration.GetConnectionString("DefaultConnection")).Options)));
0
Lapenkov Vladimir