1

I've got a Solution with many projects. One of them (Domain) is a .NET Standard 2.0 project where I made my EF Core DbContext implementation for which I want to enable database migrations.

I saw various blogs and Q/A forums where the problem was explained but none of the proposed solutions seem to work for me because of the .NET Core newer version or (probably) for my particular solution configuration.

Solution projects

  • Engine (.NET Core 2.1 Console App)
  • Web API (.NET Core 2.1 Library)
  • Application (.NET Core 2.1 Library)
  • Domain (.NET Standard 2.0 Library)
  • WindowsService (.NET Core 2.1 Console App)

The WindowsService is the Startup project, where an instance of the Engine is created and encapsulated to run as Windows Service or Console application (for debug).

The Engine is the real core application, where an instance of Kestrel self-host Web server is configured as Web API and instantieted. Also other components are instantiated and stay alive (UDP listener, machine watchdog, etc...).

WebAPI has Startup class but no Program class, since all the configuration and the Web server start is done inside Engine.Program.cs class.

Dependencies

  • WindowsService => Engine
  • Engine => WebAPI
  • WebAPI => Application, Domain
  • Application => Domain

The first attempt was to simply launch add-migration Initial from PMC with Domain project as target, but it turns to:

Unable to create an object of type 'MyDbContext'. Add an implementation of 'IDesignTimeDbContextFactory' to the project, or see https://go.microsoft.com/fwlink/?linkid=851728 for additional patterns supported at design time.

Then I followed the proposed solution on this question, but:

  1. add-migration Initial from PMC, after setting My WebAPI (with no Program class) as startup project, turns to:

    It was not possible to find any compatible framework version The specified framework 'Microsoft.NETCore.App', version '2.1.0' was not found.

    • Check application dependencies and target a framework version installed at: C:\Program Files\dotnet\
    • Installing .NET Core prerequisites might help resolve this problem: http://go.microsoft.com/fwlink/?LinkID=798306&clcid=0x409
    • The .NET Core framework and SDK can be installed from: https://aka.ms/dotnet-download
    • The following versions are installed: 2.0.5 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] 2.0.6 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] 2.0.7 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] 2.0.9 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] 2.1.2 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  2. add-migration Initial from PMC, after adding an additional target framework (netcoreapp2.1) to the Domain library project file, leads me to the same error as the 1st attempt.

  3. add-migration Initial from PMC, after adding a reference to Microsoft.EntityFrameworkCore.SqlServer.Design v1.1.6 always leads to same error as the 1st attempt.

What should I do?

UPDATE

This is the new Domain.csproj file after removing the unecessary libraries references.

<ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.1.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.1" />
    <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
</ItemGroup>

All libraries in all projects are up to date.

Cheshire Cat
  • 1,941
  • 6
  • 36
  • 69
  • Your problem is using `Microsoft.EntityFrameworkCore.SqlServer.Design v1.1.6`. All of the Microsoft packages should be the same version. `2.1.0` or `2.1.1`. – Todd Skelton Aug 10 '18 at 16:01
  • Updated my answer to address your `Unable to create an object of type 'MyDbContext'.` error. – Todd Skelton Aug 10 '18 at 16:16
  • 1
    The `Design` package has been deprecated since 2.0, which is why there's no version available greater than 1.1.6. Remove it from your project. – Chris Pratt Aug 10 '18 at 18:46
  • @ChrisPratt The package didn't caused any problem or warning during the installation... and my first attempts was made without the package installed. Anyway, since it didn't solved anything, I removed it. – Cheshire Cat Aug 13 '18 at 07:15

2 Answers2

2
  1. Download and install appropriate Runtime and SDK from here - I guess you need .NET Core 2.1.302 at the moment

  2. Microsoft.EntityFrameworkCore.SqlServer.Design is not needed anymore as it's included to SDK. CLI reference in csproj fiels for EntityFrameworkCore is not needed as well.

  3. Make sure you Manage NuGet packages window shows all updated.

  4. Add anywhere in you web project implementation of IDesignTimeDbContextFactory interface - it will be found automatically and used for EF Add-Migration (or dotnet ef... analogues) command in Package Manager Console

    public class DesignTimeActivitiesDbContextFactory : IDesignTimeDbContextFactory<ActivitiesDbContext>
    {
        public ActivitiesDbContext CreateDbContext(string[] args)
        {
            DbContextOptionsBuilder<ActivitiesDbContext> builder = new DbContextOptionsBuilder<ActivitiesDbContext>();
    
            var context = new ActivitiesDbContext(
                builder
                .UseSqlServer("Data Source=(local)\LocalDB;Initial Catalog=DB_name;Integrated Security=True;")
                .Options);
    
            return context;
        }
    }
    

To read the connection string from your appsettings config file you could do the following:

public class DesignTimeActivitiesDbContextFactory : IDesignTimeDbContextFactory<ActivitiesDbContext>
{
    public ActivitiesDbContext CreateDbContext(string[] args)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(Path.Combine(Directory.GetCurrentDirectory()))
            .AddJsonFile("appsettings.Development.json", optional: false);

        var config = builder.Build();

        var optionsBuilder = new DbContextOptionsBuilder<ActivitiesDbContext>()
            .UseSqlServer(config.GetConnectionString("DefaultConnection"));

        return new ActivitiesDbContext(optionsBuilder.Options);
    }
}

NOTE: in the above code factory will use connection string value defined in appsettings.Development.json file. And connection name is DefaultConnection. In other words, this design time factory is used for the Code First commands like Add-Migration, Update-Database, etc...) and will apply them to the database connection configured for Development environment.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Dmitry Pavlov
  • 30,789
  • 8
  • 97
  • 121
  • Thank you, it works! I've just one remark. Since my actual project configuration is not done inside the WebAPI project `Startup` class, I added the `DesignTimeActivitiesDbContextFactory` class into that project but I need to manually specify the `ConnectionString`, while my actual configuration take it from the `appsettings.json` file using `IConfiguration` object. Is it someway possible to read it from there too? – Cheshire Cat Aug 13 '18 at 08:47
  • @CheshireCat - I have updated the answer - added how you could point factory to use connection string defined in config file. So you can avoid duplicated connection string defined in two places. Hope that helps. – Dmitry Pavlov Aug 13 '18 at 13:11
  • Since I already have a `ConfigurationBuilder` block with all Json and Env. variables settings, is it a problem in runtime if I instantiate another `ConfigurationBuilder` on another class? – Cheshire Cat Aug 14 '18 at 06:37
  • 1
    No, it is not a problem at all. `ConfigurationBuilder` is just a class to access configuration sources. You can create it when you need. – Dmitry Pavlov Aug 14 '18 at 10:09
  • If you'd like to get connection strings from custom section of your `appsettings.json` (instead of `ConnectionStrings` section) check this stackoverflow.com/a/53924899/804385 – Dmitry Pavlov Dec 25 '18 at 19:02
0

Your csproj file for the class library should contain at least this package. Make sure your versions match for all your Microsoft packages. Either 2.1.0 or 2.1.1 (latest at this time)

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.1.1" />
  </ItemGroup>

</Project>

In the Package Manager Console, set your default project set to the class library where you want the migrations. It's a drop down in the top right of the console window. Also, make sure your start-up project is the project that has the database connection info.

If the start-up project doesn't have an instance of the DbContext you are wanting to scaffold, you will get the error because it doesn't know how to materialize a DbContext. You should have the DbContext registered in your Startup.cs like this:

services.AddDbContext<MyDbContext>(options => options.UseSqlServer(connectionString));

Then, run the command Add-Migration InitialCreate. This will scaffold your migration, it should add a folder to your class library.

After, run Update-Database and it should apply the migration.

If you are still having issues. Make sure you are running the latest SDK. https://www.microsoft.com/net/download

More info can be found in the Docs. https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/

Todd Skelton
  • 6,839
  • 3
  • 36
  • 48
  • "If the start-up project doesn't have an instance of the DbContext you are wanting to scaffold, you will get the error because it doesn't know how to materialize a DbContext" Yes, I was pretty sure that the problem was related to the materialization of DbContext, since my actual configuration (`IConfiguration`, `IServiceProvider`, etc.) is done inside another project that has no standard scaffolding, so I was trying to find a workaroung for that. The solution proposed by @DmitryPavlov works. – Cheshire Cat Aug 13 '18 at 08:37