0

Coming from a Java environment I'm having a few issues wrapping my head about the inheritable DBContext class. with Java EE I used to:

  1. Set up one or multiple DB Contexts (if separate clusters of entities where affected);
  2. Added the Context to respective DataAccessor classes that handled the query execution via DI;

Now with EF Core practically all samples I see create an instance of a MyDBContext by calling the default constructor:

 using (var db = new myContext()){...}

This raises a few questions for me:

  • With this Method each DataAccessor class that calls the constructor has its own instance of the Context. Wouldn't it be nicer to only ave one and use DI to inject it when needed?
  • How do I call the constructor if i didn't overload OnConfiguring(...) to pass the options, but instead used AddDbContext as a Service in Startup.cs? Now the overloaded constructor with the options expects them to be passed on each time the constructor is called.
  • Is having multiple DBContexts per application/Db even a good practise with EF Core?
Beltway
  • 508
  • 4
  • 17
  • A DbContext is a Unit-of-Work. It collects all changes and saves them only if `SaveChanges` is called. That's why **all** examples and tutorials use `using`, and you should too. – Panagiotis Kanavos Oct 21 '20 at 06:37
  • As for configuring, the examples show how to configure it from the *outside*, how to pass option, or if you use .NET Core DI, how to do that using `AddDbContext` with a builder action – Panagiotis Kanavos Oct 21 '20 at 06:39

1 Answers1

2

Normally you would want single instance per request scope

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        // use options as you would use them in the .OnConfiguring
        options.UseSqlServer("your connectionString");
    );
}

If you use constructor injection in services, ASP.NET service provider will resolve db context as constructor parameter. All services that have db context this way will share same instance.

public class ServiceDependentOnContext
{
    private readonly ApplicationDbContext dbContext;

    public ServiceDependentOnContext(ApplicationDbContext dbContext)
    {
        this.dbContext = dbContext;
    }
}

Make sure you configure your service for dependency injection as well

// This method gets called by the runtime. Use this method to add services to the container.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer("your connectionString")
    );

    services.AddScoped<ServiceDependentOnContext>();
}
Yehor Androsov
  • 4,885
  • 2
  • 23
  • 40
  • Thanks. With this and @Darem 's comment I was able to figure it out now; I Injected the DBContext into a repository used by the controller. But this raises the remaining question again: is it necessary/good practise to let the the DBContext implement multiple interfaces dependent on which responsibilities the different repos using it have? And, refering to Darem's comment: will I have threading issues with AddScoped or should I use transient? – Beltway Oct 21 '20 at 07:54
  • 1
    transient is the same as if you were doing `new DbContext` every time. You won't have any threading issues with scoped lifetime, since you are not going to use more than 1 thread per request (unless intended by code). I don't understand the first question, why would you need multiple interfaces for different repos. If you need dynamic options for db context, you could configure it at runtime using `IServiceProvider`. see my answer at https://stackoverflow.com/a/64444899/7313094 – Yehor Androsov Oct 21 '20 at 08:11
  • scoped lifetime used to ensure all entities that were created/listed belong to the same `DbContext.EntityTracker`. so if you are doing stuff in different services, you can commit all changes at once with single `DbContext.SaveChanges()` invokation. which looks very like transaction behaviour - commit all or revert. – Yehor Androsov Oct 21 '20 at 08:15
  • Presume I have three Repositories with different responsibilities. I would then let the single DBContext implement three distinct interfaces containing the required subset of methods to access the DB through the repo. The alternative would be to let the DBContext contain only one interface containing all methods used by all three repos. I see this might be a subjective question but I was wondering if there was a usual practice with ASP.NET. – Beltway Oct 21 '20 at 08:33
  • this is up to you. I don't see point of adding interfaces to DbContext, all the methods you need are `Set` and `SaveChanges`, so usually I end up inserting db context directly inside repositories/services/handlers – Yehor Androsov Oct 21 '20 at 08:46
  • the only case when I used wrapper/interface for db context was a library that was accessed both by .NET Framework and .NET Core, so I had to use interfaces instead two implementations - ApplicationDbContext and ApplicationCoreDbContext – Yehor Androsov Oct 21 '20 at 08:49
  • Seems reasonable. How do you propagate the messages inherited from DBContext to the interface though? If was using `ApplicationCoreDbContext` and wanted to apply `saveChanges()` the Interface had to offer a Method not which is merely inherited to the MyDBContext. – Beltway Oct 21 '20 at 10:43
  • maybe my explanation was confusing, I created gist to show how it works. I don't remember patterns by name, but it is something like adapter or decorator, not sure https://gist.github.com/yegorandrosov/9fb746b185f93ac52e9079b2c44a754a – Yehor Androsov Oct 21 '20 at 11:16
  • Thanks a lot. That is not exactly what I meant but this pattern might even be cleaner solution than what I had in mind. – Beltway Oct 23 '20 at 12:19