Efcore: No database provider has been configured

Created on 4 Jul 2019  路  7Comments  路  Source: dotnet/efcore

Unless I have missed something according to this ...

https://go.microsoft.com/fwlink/?linkid=851728

... Contexts defined like this ...

public abstract class EFDataContext : DbContext, IDataContext
{
       public EFDataContext(DbContextOptions options) : base(options) { }
}

public class MyDataContext : EFDataContext
{
       public MyDataContext (DbContextOptions<MyDataContext > options) : base(options) { }
}

... should work when this is added to the Startup ...

public void ConfigureServices(IServiceCollection services)
{
       var config = config.Bind(new MyConfig());
       services.AddDbContext<MyDataContext>(options => options.UseSqlServer(config.Connections["Mydb"]));
}

... for some reason I can't seem to figure out though i'm getting this when I try to issue a "Add-Migration Initial" command to PMC ...

Unable to create an object of type 'MyDataContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728

...

What other steps must I take to ensure that the context is constructed correctly?

I would rather DI the configuration than have it coded in to the context itself.

closed-question customer-reported

Most helpful comment

Why do DbContexts have to have this "special" way of being created, why can't they just be constructed like any other object with normal DI rules?

In this case the migration tool has to know how to new up your DbContext.

There are several ways to create migrations. Here is how I do it (this is also documented at the link you cited). You can modify to suite yourself.

Create a folder in your project call Migrations
Add a new project of type Console Application called Migrations.
Add a reference to your project. Ad EF Core, EF Core Design nuget packages.
Create a class called ContextFactory (where Db is your context):

public class ContextFactory : IDesignTimeDbContextFactory<Db>
{
    public Db CreateDbContext(string[] args)
    {
        var optionsBuilder = new DbContextOptionsBuilder<Db>();
        optionsBuilder.UseSqlServer(@"Data Source =.\SQLSERVER;Initial Catalog = YourDBName; Integrated Security = True;");
        return new Db(optionsBuilder.Options);
    }
}

Open a command line in the same folder where YourProject.csproj lives.

Run these commands:

dotnet ef migrations add CreateDatabase --context Db  --startup-project ../Migrations --output-dir Migrations

dotnet ef database update --startup-project ../Migrations --context Db

All 7 comments

As a side question ...
Why do DbContexts have to have this "special" way of being created, why can't they just be constructed like any other object with normal DI rules?

When I try to manually construct a context and pass any other args that my custom contexts need in new ctors I still can't make them work.

In EF6 and with aspnet not core as long as I allowed for a default ctor it would auto figure out the provider and connection string from the config, why not just have a requirement in the DbContext class that there is at least one ctor that accepts the DbContextOptions then inheritance can be setup i nthe normal fashion and the options be built as a separate DI rule ...

For example ...

say I have a DbContext like this ...

public class MyDataContext : EFDataContext
{
public MyDataContext (DbContextOptions options, Foo foo, Bar bar) : base(options) { }
}

Then I configure DI with this ...

services.AddSingleton(
      new DbContextOptionsBuilder<MyDataContext>
               .UseSql(Config.Connections["MyConnection"])
               .Build()
);

Now whenever I need a DbContextOptions for the MyDataContext type it has a rule that's explicit and type specific but this leaves me open to extending EF however I want.

Then PMC should run the startup code to build the ServiceProvider and then simply ask that for a context just we would in code.

Is there some reason EF has to be special about this?

Why do DbContexts have to have this "special" way of being created, why can't they just be constructed like any other object with normal DI rules?

In this case the migration tool has to know how to new up your DbContext.

There are several ways to create migrations. Here is how I do it (this is also documented at the link you cited). You can modify to suite yourself.

Create a folder in your project call Migrations
Add a new project of type Console Application called Migrations.
Add a reference to your project. Ad EF Core, EF Core Design nuget packages.
Create a class called ContextFactory (where Db is your context):

public class ContextFactory : IDesignTimeDbContextFactory<Db>
{
    public Db CreateDbContext(string[] args)
    {
        var optionsBuilder = new DbContextOptionsBuilder<Db>();
        optionsBuilder.UseSqlServer(@"Data Source =.\SQLSERVER;Initial Catalog = YourDBName; Integrated Security = True;");
        return new Db(optionsBuilder.Options);
    }
}

Open a command line in the same folder where YourProject.csproj lives.

Run these commands:

dotnet ef migrations add CreateDatabase --context Db  --startup-project ../Migrations --output-dir Migrations

dotnet ef database update --startup-project ../Migrations --context Db

@leaderanalytics Thanks for the feedback ... I took some time out and played some games for the evening then revisited it and came up with this implementation (which seems to work and agree with your thoughts) ...

public abstract class ContextFactory<T> : IDesignTimeDbContextFactory<T> 
    where T : DbContext
{
    public T CreateDbContext(string[] args)
    {
        var config = new Config();
        new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .Build()
            .Bind(config);

        return Build(
            new DbContextOptionsBuilder<T>()
                .UseSqlServer(config.Connections[typeof(T).Name.Replace("DataContext", "")])
                .Options
        );
    }

    protected abstract T Build(DbContextOptions<T> options);
}

... all my DbContexts implement an interface in my core code called "IDataContext" so they end up being named "FooDataContext" or something with a connection string named "Foo". within the config like this ...

{
     "Connnections": {
           "Foo": "some connection string",
           "Bar": "some other connection string"
      },
     Settings: { ... }
}

The above abstract class when implemented like this ...

public class FooContextFactory : ContextFactory<FooDataContext>
{
    protected override FooDataContext Build(DbContextOptions<FooDataContext> options)
    {
        return new FooDataContext(options);
    }
}

Then allows for a clean implementation as I add new context types that follow my convention.

It would be really nice if the "special" helper ...

services.AddDbContext<FooDataContext>(options => ... );

... actually provided enough information for the PMC / command line tooling to be able to build the context ... oh wait it does ... just not at design time ?!?!?!?

... I guess my question was really ... "What exactly does AddToDbContext() do here?" and if that information isn't enough to construct a context of the given type then why would it work during normal execution?

Given the default template for a .Net Core app has this in program.cs ...

public static IWebHost BuildWebHost(string[] args) => ...

You'd think the tooling could call that and then grab the ServiceProvider from it and simply ask that for a context instance ... or am i missing something here?

Looks good! I hope you got it working. I'm pretty sure AddDbContext registers your DbContext with the Microsoft DI container. I don't use it so I'm not 100% sure. If so it's used at run time to inject your context where needed.

Migrations are a different story. The EF designer has to know explicitly how to new up your context without using the DI container. So AddDbContext does nothing for you when creating a migration.

@sam-wheat thats the issue ... EF6 had a requirement that a default ctor be provided so that it could just new one up for migrations reasons.

There's nothing wrong with the EF Core implementation as such, it just feels clunky / like a I have to implement a cludge for the tooling.

@TehWardy There is nothing really special in how the context is registered in D.I by AddDbContext. For example, this:
C# services.AddDbContext<MyDataContext>(options => ptions.UseSqlServer(config.Connections["Mydb"]));
registers MyDataContext and DbContextOptions<MyDataContext> in D.I.

This is why the constructor for MyDataContext should depend on DbContextOptions<MyDataContext>--so it can be injected in the normal way. The constructor can of course take other things as well, assuming they are registered in D.I.

EF Tooling understands and uses this pattern; there is no need for IDesignTimeDbContextFactory. Usually when this doesn't work it's because the config being loaded is not available/found at design time. If this isn't the case, then please please post a small, runnable project/solution that demonstrates the behavior so we can investigate.

I know this in an old issue, but it comes up when searching for this problem in the nets, so hopefully this will help someone.

A very important part form https://go.microsoft.com/fwlink/?linkid=851728 is the comment:

public class Program
{
    public static void Main(string[] args)
        => CreateHostBuilder(args).Build().Run();

    // EF Core uses this method at design time to access the DbContext
    public static IHostBuilder CreateHostBuilder(string[] args)
        => Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(
                webBuilder => webBuilder.UseStartup<Startup>());
}

I had this method removed, since it seemed like the only place where it is used is in the Main method.
Don't remove CreateHostBuilder.

Also, if you add configuration in CreateHostBuilder like so:

public static IHostBuilder CreateHostBuilder(string[] args)
    => Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostingContext, config)
            => config.AddJsonFile(
                $"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json",
                optional: true, reloadOnChange: true
            ))
        .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());

Then your Startup has the configuration loaded and ready to use at design-time:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();

        services.AddDbContext<Context>(builder 
            => builder.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
    }
}
Was this page helpful?
0 / 5 - 0 ratings