I have ASP.Net Core 2.1 with EF Core 2.1. This is how my DbContext class looks like
app.DAL.EF -> Layer
using app.domain;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.Extensions.Configuration;
using System;
using System.IO;
namespace app.EF
{
public class MyAppContext : DbContext
{
public MyAppContext(DbContextOptions<MyAppContext> options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new CustomerConfiguration());
modelBuilder.HasDefaultSchema("app");
base.OnModelCreating(modelBuilder);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
}
public DbSet<Customer> Customers { get; set; }
}
public class MyAppContextConfiguration : IDesignTimeDbContextFactory<MyAppContext>
{
public MyAppContext CreateDbContext(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT ") ?? "Production" }.json", optional: true)
.Build();
var optionsBuilder = new DbContextOptionsBuilder<MyAppContext>();
//optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
var dbConString = configuration.GetConnectionString("ITMDbConnection");
optionsBuilder.UseSqlServer(dbConString);
return new MyAppContext(optionsBuilder.Options);
}
}
public class CustomerConfiguration : IEntityTypeConfiguration<Customer>
{
public void Configure(EntityTypeBuilder<Customer> builder)
{
builder.HasKey(x => x.Id);
}
}}
app.DI -> Layer
public static class Factory
{
public static void Initialize(ref IServiceCollection services)
{
//services.AddTransient<MyAppContext>();
services.AddDbContext<MyAppContext>(options =>
{
});
//services.AddTransient<MyAppContextConfiguration>();
services.AddTransient<ICustomerRepository, CustomerRepository>();
}
}
app.API -> Layer
namespace app.api
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
Factory.Initialize(ref services);
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}
}}
When running Add-Migration DbInit
from Package Manager Console, throwing the below error
No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.
Thanks!
The error is clear - the provider and connection are never configured. All this code could be replaced with this context :
public class MyAppContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
public MyAppContext(){}
public MyAppContext(DbContextOptions<MyAppContext> options)
:base(options)
{}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//The base method is empty
modelBuilder.HasDefaultSchema("app");
}
}
And a call to AddDbContext
inside ConfigureServices
:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyAppContext>(options =>
{
var dbConString = Configuration.GetConnectionString("ITMDbConnection");
options.UseSqlServer(dbConString);
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
....
}
Most applications have more than one DbContext though. One option would be to add another method to Startup.cs
to register them. For more complex applications, eg applications composed of different domains/modules/subsystems/projects, a better idea would be to create extension methods, eg :
public static CustomerDomainExtensions
{
public static IServicesCollection AddCustomerDomain(this IServicesCollection services,IConfiguration configuration)
{
return services.AddCustomerContexts(configuration)
.AddRepositories(...)
...;
}
public static AddCustomerContexts(this IServicesCollection services,IConfiguration configuration)
{
var dbConString = Configuration.GetConnectionString("ITMDbConnection");
services.AddDbContext<MyAppContext>(options =>
{
options.UseSqlServer(dbConString);
});
//Add more contexts ...
}
}
In Startup.cs
, this would be called inside ConfigureServices
. This is how all Microsoft.Extensions.*
classes work, by providing Add
and Use
extension methods for use in Startup.cs
:
public void ConfigureServices(IServiceCollection services)
{
services.AddCustomerDomain(Configuration);
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
EDIT
Added a default constructor to the DbContext