Reputation: 6725
I have a .Net Core WebApplication Project in which the Context Class is in a Class Library. If I hard code the connection string in the OnConfiguring(DbContextOptionsBuilder optionsBuilder) method I can generate migrations. Since it is better to let dependency injection manage the context I would like to add this to the Startup Class. However when I do I get the following 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.
DbContext Class:
public class CustomerManagerContext : IdentityDbContext<User, Role, long, UserClaim, UserRole, UserLogin, RoleClaim, UserToken>
{
public CustomerManagerContext() { }
public CustomerManagerContext(DbContextOptions<CustomerManagerContext> options) : base(options)
{
}
//protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
//{
// base.OnConfiguring(optionsBuilder);
// optionsBuilder.UseSqlServer("SecretConnectionString");
//}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<User>().ToTable("Users");
builder.Entity<Role>().ToTable("Roles");
builder.Entity<UserClaim>().ToTable("UserClaims");
builder.Entity<UserRole>().ToTable("UserRoles");
builder.Entity<UserLogin>().ToTable("UserLogins");
builder.Entity<RoleClaim>().ToTable("RoleClaims");
builder.Entity<UserToken>().ToTable("UserTokens");
}
}
Startup Class - ConfigureServices Method
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<CustomerManagerContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
);
services.AddEntityFrameworkSqlServer()
.AddDbContext<CustomerManagerContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<User, Role>()
.AddEntityFrameworkStores<CustomerManagerContext>()
.AddDefaultTokenProviders();
}
Upvotes: 4
Views: 4065
Reputation: 3908
This bit me hard, getting errors like:
No database provider has been configured for this DbContext.
No design-time services were found.
The server was not found or was not accessible.
But I ended up with a fairly simple solution/work-around:
Set the default startup-project in your solution (or in your command line)
in your Startup.cs
add the migration-project:
public void ConfigureServices(IServiceCollection services)
{
var myDbContextAssemblyName = typeof(MyDbContext).Assembly.GetName().Name;
var connectionString = Configuration.GetConnectionString(MyDbContext.ConnectionStringName);
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(
connectionString,
x => x.MigrationsAssembly(myDbContextAssemblyName)));
// do more ...
}
in your connection-string use the IP-address and port-number (inspired by this corefx issue), instead of the server-name/dns (FWIW: query to get IP).
So now I have this in my appsettings.Development.json
:
"ConnectionStrings": { "MyConnectionStringName": "Data Source=10.1.2.3,1433; Initial Catalog=MyCatalog; Integrated Security=SSPI" }
I found a lot of other suggestions, and I will mention a few that seemed interesting. Maybe it will help someone else:
Mention startup-project and migration-project in command-line:
Update-Database -Verbose -Project x.Data -StartupProject x.Web
Addition (from the comments): This also works from the dotnet CLI:
dotnet ef migrations add <MIGRATION_NAME> --project <DATABASE_PROJECT_DIR> --startup-project <STARTUP_PROJECT_DIR> dotnet ef database update --project <DATABASE_PROJECT_DIR> --startup-project <STARTUP_PROJECT_DIR>
One can also call migrations at StartUp , "for apps with a local database". (I guess otherwise running on multiple nodes, may start multiple runtime migrations at the same time with concurrency issues?)
myDbContext.Database.Migrate();
This EntityFrameworkCore issue states:
The problem is that when EF calls either
CreateWebHostBuilder
orBuildWebHost
it does so without runningMain
. (This is intentional because EF needs to build the model and use theDbContext
without starting the application.) This means that when EF invokes on of these methods the staticIConfiguration
property is still null--since it is only set inMain
. So, you'll need to either make sure thatIConfiguration
is set/handled when EF calls one of these methods, or useIDesignTimeDbContextFactory
.
This is not necessary for me, I guess because .Net Core 2 loads the configuration behind the scenes.
This EntityFrameworkCore issue states:
The typical way to do this is to read a file, an environment variable, or similar inside
IDesignTimeDbContextFactory
.
This seems too much a hack to me.
Microsoft documentation mentions:
Some of the EF Core Tools commands (for example, the Migrations commands) require a derived DbContext instance to be created at design time in order to gather details about the application's entity types and how they map to a database schema.
They mention these ways to provide this design time DbContext:
The tools try to obtain the
DbContext
object from the application's service provider. [...] The tools first try to obtain the service provider by invokingProgram.BuildWebHost()
[JP: orCreateWebHostBuilder
] and accessing theIWebHost.Services
property. TheDbContext
itself and any dependencies in its constructor need to be registered as services in the application's service provider. This can be easily achieved by having a constructor on theDbContext
that takes an instance ofDbContextOptions<TContext>
as an argument and using theAddDbContext<TContext>
method.
If the
DbContext
can't be obtained from the application service provider, the tools look for the derivedDbContext
type inside the project. Then they try to create an instance using a constructor with no parameters. This can be the default constructor if theDbContext
is configured using theOnConfiguring
method.
You can also tell the tools how to create your
DbContext
by implementing theIDesignTimeDbContextFactory<TContext>
interface: If a class implementing this interface is found in either the same project as the derived DbContext or in the application's startup project, the tools bypass the other ways of creating theDbContext
and use the design-time factory.
Upvotes: 8
Reputation: 1691
I was having the same issue when I when providing options along with dbcontext while adding dbcontext to services like:-
services.AddDbContext<TodoContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
then I added dbcontext without options like below and it fixed the problem for me
services.AddDbContext<TodoContext>();
also I had to add OnConfiguring method to dbcontext class so the context can access the connection string:-
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=IdenDB;Trusted_Connection=True;MultipleActiveResultSets=true");
}
I am not sure if this is the right way of doing things since I just started with core but here is an answer which explains this issue in a bit more detail
Upvotes: 1