Scaffolding: Scaffolding doesn't work when DbContext is in a separate project

Consistently getting this error when attempting to scaffold a controller while having DbContext in a separate project:

There was an error running selected code generator:

'Unable to resolve service for type 
'Microsoft.EntityFrameworkCore.DbContextOptions`1[MyProject.Data.MyProjectDbContext]' 
while attempting to activate 'MyProject.Data.MyProjectDbContext'.'

MyProjectDbContext:

public class MyProjectDbContext : DbContext
{
    public MyProjectDbContext(DbContextOptions<MyProjectDbContext> options) : base (options)
    { }

    public DbSet<Note> Notes => Set<Note>();
}

DbContext is registered in Program.cs as follows:

builder.Services.AddDbContext<MyProjectDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("MyProjectDbContext")));

Scaffolding works if DbContext moved to the web project.

IDE: Visual Studio 2022 Community SDK: .NET 6.0 Packages: “Microsoft.VisualStudio.Web.CodeGeneration.Design” Version=“6.0.1” “Microsoft.EntityFrameworkCore.Tools” Version=“6.0.1” “Microsoft.EntityFrameworkCore.SqlServer” Version=“6.0.1”

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 12
  • Comments: 29

Most upvoted comments

I found a solution. You can create a dbcontext factory class in same folder with your ApplicationDbContext class. This factory class creates ApplicationDbContext at design time and scaffolding runs correctly.

public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
        public ApplicationDbContext CreateDbContext(string[] args)
        {
            var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
            optionsBuilder.UseSqlServer("Server=(localdb)\\MSSQLLocalDB;Database=EcommerceDb;Trusted_Connection=True;MultipleActiveResultSets=true");

            return new ApplicationDbContext(optionsBuilder.Options);
        }
}

IDE: VS 2022 Pro Framework: .NET6 Packages Version: 6.0.2

You can use ConfigurationBuilder class for build the configuration.

public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
    public ApplicationDbContext CreateDbContext(string[] args)
    {
        var configuration = new ConfigurationBuilder()
            .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
            .AddJsonFile("appsettings.json")
            .Build();
        
        var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
        optionsBuilder.UseSqlServer(configuration.GetConnectionString("Default"));

        return new ApplicationDbContext(optionsBuilder.Options);
    }
}

Did you find a solution? I’m having the same issue.

The migrations are working fine, but Scaffolding wont work. Only slight difference is I am using IdentityDbContext.

I found a solution. You can create a dbcontext factory class in same folder with your ApplicationDbContext class. This factory class creates ApplicationDbContext at design time and scaffolding runs correctly.

public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
        public ApplicationDbContext CreateDbContext(string[] args)
        {
            var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
            optionsBuilder.UseSqlServer("Server=(localdb)\\MSSQLLocalDB;Database=EcommerceDb;Trusted_Connection=True;MultipleActiveResultSets=true");

            return new ApplicationDbContext(optionsBuilder.Options);
        }
}

IDE: VS 2022 Pro Framework: .NET6 Packages Version:

Thanks Cem, nice workaround though, its working! Teşekkürler.

Eu encontrei uma solução. Você pode criar uma classe de fábrica dbcontext na mesma pasta com sua classe ApplicationDbContext. Essa classe de fábrica cria ApplicationDbContext em tempo de design e o scaffolding é executado corretamente.

public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
        public ApplicationDbContext CreateDbContext(string[] args)
        {
            var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
            optionsBuilder.UseSqlServer("Server=(localdb)\\MSSQLLocalDB;Database=EcommerceDb;Trusted_Connection=True;MultipleActiveResultSets=true");

            return new ApplicationDbContext(optionsBuilder.Options);
        }
}

IDE: VS 2022 Pro Framework: Pacotes .NET6 Versão: 6.0.2

Embora isso tenha resolvido o problema para mim, introduziu alguns efeitos colaterais que só percebi semanas depois. Ele altera algumas das partes internas das classes de identidade, consulte a migração a seguir:

            migrationBuilder.AlterColumn<string>(
                name: "Name",
                table: "AspNetUserTokens",
                type: "nvarchar(450)",
                nullable: false,
                oldClrType: typeof(string),
                oldType: "nvarchar(128)",
                oldMaxLength: 128);

            migrationBuilder.AlterColumn<string>(
                name: "LoginProvider",
                table: "AspNetUserTokens",
                type: "nvarchar(450)",
                nullable: false,
                oldClrType: typeof(string),
                oldType: "nvarchar(128)",
                oldMaxLength: 128);

            migrationBuilder.AlterColumn<string>(
                name: "ProviderKey",
                table: "AspNetUserLogins",
                type: "nvarchar(450)",
                nullable: false,
                oldClrType: typeof(string),
                oldType: "nvarchar(128)",
                oldMaxLength: 128);

            migrationBuilder.AlterColumn<string>(
                name: "LoginProvider",
                table: "AspNetUserLogins",
                type: "nvarchar(450)",
                nullable: false,
                oldClrType: typeof(string),
                oldType: "nvarchar(128)",
                oldMaxLength: 128);

Mesmo que nada mais tenha sido alterado no projeto (relacionado ao esquema do banco de dados), quando sua classe proposta está presente, essas colunas são alteradas ao tentar criar uma nova migração. Remover a classe e reestruturar a migração resulta em uma migração vazia.

Definir a MaxLengthForKeyspropriedade manualmente no relevante Program.cs(como mencionado aqui ) não altera nada.

Não tenho certeza de onde isso vem, mas talvez seja uma informação útil para alguém.

I believe that your problem has come from elsewhere, microsoft itself recommends building a db context factory.

configuration.GetConnectionString("Default")

It was useful for me but the SetBasePath method throwed me an error. I fix it adding two nugget packages:

Microsoft.Extensions.Configuration.FileExtensions Microsoft.Extensions.Configuration.Json

The answer was commented on: https://stackoverflow.com/questions/36001695/setting-base-path-using-configurationbuilder

I found a solution. You can create a dbcontext factory class in same folder with your ApplicationDbContext class. This factory class creates ApplicationDbContext at design time and scaffolding runs correctly.

public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
        public ApplicationDbContext CreateDbContext(string[] args)
        {
            var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
            optionsBuilder.UseSqlServer("Server=(localdb)\\MSSQLLocalDB;Database=EcommerceDb;Trusted_Connection=True;MultipleActiveResultSets=true");

            return new ApplicationDbContext(optionsBuilder.Options);
        }
}

IDE: VS 2022 Pro Framework: .NET6 Packages Version: 6.0.2

Although this resolved the issue for me, it introduced some side effects which I only realized weeks later. It changes some of the internals of identity classes, see the following migration:

            migrationBuilder.AlterColumn<string>(
                name: "Name",
                table: "AspNetUserTokens",
                type: "nvarchar(450)",
                nullable: false,
                oldClrType: typeof(string),
                oldType: "nvarchar(128)",
                oldMaxLength: 128);

            migrationBuilder.AlterColumn<string>(
                name: "LoginProvider",
                table: "AspNetUserTokens",
                type: "nvarchar(450)",
                nullable: false,
                oldClrType: typeof(string),
                oldType: "nvarchar(128)",
                oldMaxLength: 128);

            migrationBuilder.AlterColumn<string>(
                name: "ProviderKey",
                table: "AspNetUserLogins",
                type: "nvarchar(450)",
                nullable: false,
                oldClrType: typeof(string),
                oldType: "nvarchar(128)",
                oldMaxLength: 128);

            migrationBuilder.AlterColumn<string>(
                name: "LoginProvider",
                table: "AspNetUserLogins",
                type: "nvarchar(450)",
                nullable: false,
                oldClrType: typeof(string),
                oldType: "nvarchar(128)",
                oldMaxLength: 128);

Even though nothing else was changed in the project (related to the database schema), when your proposed class is present, these columns are changed when trying to create a new migration. Removing the class and rescaffolding the migration results in an empty migration.

Setting the MaxLengthForKeys property manually in the relevant Program.cs (as mentioned here) changes nothing.

Not sure where this comes from but maybe this is useful information to someone.

Thanks @cemg. Found this after hours of testing. Works great

@cemg Thanks! This also works for me.