C
C#3y ago
camel

Inject AppDbContext with parameterized constructor

I'm trying to encapsulate the AppDbContext, so everything needed is passed in the constructor.
public class AppDbContext : DbContext, IAppDbContext
{
private readonly string _connectionString;
public DbSet<Schedule> Schedules { get; set; } // only expose DbSet for aggregate roots

public AppDbContext(string connectionString)
{
_connectionString = connectionString;
}

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connectionString).UseLazyLoadingProxies();
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
}
}
public class AppDbContext : DbContext, IAppDbContext
{
private readonly string _connectionString;
public DbSet<Schedule> Schedules { get; set; } // only expose DbSet for aggregate roots

public AppDbContext(string connectionString)
{
_connectionString = connectionString;
}

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connectionString).UseLazyLoadingProxies();
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
}
}
If I omit the "<IAppDbContext, AppDbContext>", it works for projects that have a reference to the Infrastructure, where AppDbContext resides. But if I want to use the IAppDbContext to inject it anywhere, it doesn't want to migrate.
builder.Services.AddScoped<IAppDbContext, AppDbContext>(_ => new AppDbContext(builder.Configuration.GetConnectionString("MsSqlConnection")));
builder.Services.AddScoped<IAppDbContext, AppDbContext>(_ => new AppDbContext(builder.Configuration.GetConnectionString("MsSqlConnection")));
The error: "Unable to create an object of type 'AppDbContext'. "
20 Replies
Becquerel
Becquerel3y ago
when you say 'migrate', do you mean entity framework migrations or the construction of the DI service provider? where do you get the error specifically? are your service classes actually asking for an IAppDbContext instead of a AppDbContext in their constructors? also, what happens if you just use .AddScoped<IAppDbContext>(_ => new ...?
camel
camelOP3y ago
dotnet ef migrations add initial_migration -s .\API\ -p .\Infrastructure\ same error Yes, in my application layer I'm asking for IAppDbContext
Becquerel
Becquerel3y ago
ok, i'm not familiar with migrations so i don't think i'll be able to help - apologies
phaseshift
phaseshift3y ago
"Unable to create an object of type 'AppDbContext'. "
sounds like somewhere you are asking for 'not the interface'
camel
camelOP3y ago
private readonly IAppDbContext _context;

public SchedulingService(IAppDbContext context)
{
_context = context;
}
private readonly IAppDbContext _context;

public SchedulingService(IAppDbContext context)
{
_context = context;
}
I checked it again, this is the only time I ask for it
Pobiega
Pobiega3y ago
For the migration, you can specify your dbcontext. I think it might need the real one thou, not the interface
camel
camelOP3y ago
I tried it, but same error unfortunately.
dotnet ef migrations add initial_migration --context AppDbContext -s .\API\ -p .\Infrastructure\
dotnet ef migrations add initial_migration --context AppDbContext -s .\API\ -p .\Infrastructure\
Pobiega
Pobiega3y ago
Hm, if you set up a unit test to verify your DI setup? Does that pass?
phaseshift
phaseshift3y ago
Or use dbcontextoptions or whatever the normal way is to pass connection strings
Pobiega
Pobiega3y ago
Oh, that's probably it
camel
camelOP3y ago
I am able to use the db context the "normal" way by injecting with AddDbContext but all of the configurations then take place outside of the AppDbContext class I'm doing this for encapsulation as taught by Vladimir Khorikov on Pluralsight But he seems the be the only soul doing it that way
phaseshift
phaseshift3y ago
I certainly wouldnt be doing that string param injectin like that
Pobiega
Pobiega3y ago
I'm not really sure what you mean with encapsulation here. The custom context already encapsulates your db access Encapsulating the context itself seems.. weird?
camel
camelOP3y ago
You think so? It kinda looks appealing to me to configure everything inside the AppDbContext class instead of in the Program.cs (or it's static extensions).
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connectionString).UseLazyLoadingProxies();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connectionString).UseLazyLoadingProxies();
}
I removed the parameter and hardcoded the connection string in the AppDbContext constructor. Works now with
builder.Services.AddScoped<IAppDbContext>(provider => new AppDbContext());
builder.Services.AddScoped<IAppDbContext>(provider => new AppDbContext());
Question is now, how do I set the connection string properly?
phaseshift
phaseshift3y ago
IOptions pattern if there's not some property for it in the context options/builder. Also, AddDbContext is more conventional than your registration above (which should just be .AddScoped<IAppDbContext, AppDbContext>() anyway)
camel
camelOP3y ago
Oops, you are right. I don't need that provider => new stuff here anymore. I'll try it with the IOptions then
Pobiega
Pobiega3y ago
That seems weird. You want the setup to be in program.cs with all the other configuration
camel
camelOP3y ago
The argument is that, if you have two options that need to be set in pairs, then they are encapsulated here like if you have logging and then some logging option set, you would not want to set a logging option if logging is not set
Pobiega
Pobiega3y ago
This sounds like IDbContextOptions to me :p
camel
camelOP3y ago
Actually, yes. WTF am I doing?! I'll use AddDbContext from now on. Thank you.
Want results from more Discord servers?
Add your server