Gabriel Kunkel
Gabriel Kunkel

Reputation: 2761

Is there a way to factory inject a custom DbContext?

...Maybe using TFactory in AddDbContextFactory<TContext, TFactory> in EF Core extensions?

I've only seen AddDbContextFactory examples being used with just the TContext generic. They're always very explicit to say you have to use a using statement.

In similar situations (when I useClass in Angular or AddScoped in .NET Core), I make the variable I want to see in a constructor the first generic argument and the second generic argument what actually gets injected. You know, like:

services.AddScoped<IService, RealService>();

Obviously, this isn't the case with

services.AddDbContextFactory<ADbContextIHaveBeenInjecting, AFactoryThatWillReturnADbContextIHaveBeenInjecting>();

I was hoping this would eliminate the need to do the whole using thing.

Is there another way I can do this without having to re-write every injected DbContext to conform with their prescribed:

public void DoSomething()
{
    using (var context = _contextFactory.CreateDbContext())
    {
        // ...
    }
}

As I said, my hope was to use something like this for the factory:

public class MyDbContextFactory : IDbContextFactory<MyDbContext>
{

    public MyDbContextFactory(DbContextOptions options)
    {
    }

    public MyDbContext CreateDbContext()
    {
        var ProviderName = GetProviderName();
   
        switch (ProviderName)
        {
            case "System.Data.SqlClient":
                return new SqlServerDbContext(new DbContextOptionsBuilder<SqlServerDbContext>().UseSqlServer(ConnectionString).Options);
            case "Npgsql":
                return new PostgreSqlDbContext(new DbContextOptionsBuilder<PostgreSqlDbContext>().UseNpgsql(ConnectionString).Options);
            default:
                throw new NullReferenceException("Missing provider name for DbContext. Should be Npgsql or System.Data.SqlClient");
        }
    }

 
}

Then, set it up in Startup.cs ConfigureServices like:

services.AddDbContextFactory<MyDbContext, MyDbContextFactory>();

So I could inject in a class like this:

public class MyController : BaseApiController
{

    private readonly MyDbContext _myDbContext;


    public MyController(MyDbContext myDbContext)
    {
        _myDbContext = myDbContext;
    }


    [HttpGet("GetACount")]
    public IActionResult GetACount()
    {
        var count = _myDbContext.MyRecord.Count();

        return Ok(count);
    }
...

Is there a way to do this using the AddDbContextFactory? What is TFactory actually for? Is there another way to do this?

Upvotes: 2

Views: 10493

Answers (2)

David Browne - Microsoft
David Browne - Microsoft

Reputation: 89191

DbContextFactory is specifically intended to require you to manage the lifecycle of your DbContext, because Blazor server apps don't use a Scope-per-Http request like ASP.NET Core does, so a Scoped DbContext won't work.

If you want a Scoped DbContext just use .AddDbContext intead of .AddDbContextFactory.

If you have registered a DbContextFactory but still want to inject a scoped DbContext directly in your services, then register it like this:

    services.AddScoped<MyDbContext>(sp =>
    {
        var cf = sp.GetRequiredService<IDbContextFactory<MyDbContext>>();

        var db = cf.CreateDbContext();

        return db;

    });

Upvotes: 2

Dependency inject the concrete class. create a factory to select the subclass by type. Create a parent class with a private DbContext _dbContext. Inherit the subclass from the parent class and call the parent class constructor with :base(dbContext) of the subclass. The parent class can now access in its methods the subclass context. The subclass can share the methods of the parent class for (add, select, update, and deleting by set the data context of the subclass). the subclass will dependency inject the specific dbcontext in its constructor and set the parent class dbcontext variable in its constructor. the subclass repository class can then access the base class methods within its body.

in startup define the subclass repository pattern

in public void ConfigureServices(IServiceCollection services)

  var connectionString = Configuration.GetConnectionString("ABCContext_DbCoreConnectionString");
  services.AddDbContext<ABCContext>(options1 => options1.UseSqlServer(connectionString));

 services.AddTransient<IRepositoryMySubclass, RepositoryMySubclass>();

sub class

  public RepositorySubclass(ABCContext dbContext) : base(dbContext)
    {
        _dbContext = dbContext;
    }

Upvotes: 1

Related Questions