0
votes

I am trying to learn how to generate migrations using dotnet core Entity Framework.

I have an ASP.NET Core Web API project that references two projects, each containing a DBContext.

I have successfully managed to generate the migrations for one dbcontext using:

dotnet ef migrations add Initial -s <startup_project> -c <fully qualified class name from referenced project> -o <output dir>

However, for the second DbContext it fails to find the DbContext with the message: No DbContext named 'dcs3spp.courseManagementContainers.BuildingBlocks.IntegrationEventLogEF.IntegrationEventLogContext' was found.

The project file for the referenced project has the path correctly referenced with contents:

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

  <ItemGroup>
    <ProjectReference Include="..\EventBus\EventBus.csproj" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.1">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.1" />
    <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.1.2" />
  </ItemGroup>

  <PropertyGroup>
    <TargetFramework>netstandard2.1</TargetFramework>
  </PropertyGroup>

</Project>

The project file for the ASP.NET Core Web API startup project is:

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

  <ItemGroup>
    <ProjectReference Include="..\Courses.Domain\Courses.Domain.csproj" />
    <ProjectReference Include="..\Courses.Infrastructure\Courses.Infrastructure.csproj" />
    <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" />
    <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" />
    <ProjectReference Include="..\..\..\BuildingBlocks\WebHost.Customization\WebHost.Customization.csproj" />
    <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="3.0.0" />
    <PackageReference Include="AspNetCore.HealthChecks.Npgsql" Version="3.0.0" />
    <PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="3.0.5" />
    <PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="3.0.2" />
    <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="6.0.0" />
    <PackageReference Include="Dapper" Version="2.0.30" />
    <PackageReference Include="MediatR" Version="8.0.0" />
    <PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="8.0.0" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.1">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Npgsql" Version="4.1.3" />
    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.1.2" />
    <PackageReference Include="Polly" Version="7.2.0" />
    <PackageReference Include="Serilog.AspNetCore" Version="3.2.0" />
    <PackageReference Include="Serilog.Enrichers.Environment" Version="2.1.3" />
    <PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
    <PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
    <PackageReference Include="Serilog.Sinks.Http" Version="5.2.0" />
    <PackageReference Include="Serilog.Sinks.Seq" Version="4.0.0" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0" />
    <PackageReference Include="System.Reflection" Version="4.3.0" />
  </ItemGroup>

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>


</Project>

The source code for the context class that cannot be found is:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace dcs3spp.courseManagementContainers.BuildingBlocks.IntegrationEventLogEF
{
    public class IntegrationEventLogContext : DbContext
    {       
        public IntegrationEventLogContext(DbContextOptions<IntegrationEventLogContext> options) : base(options)
        {
        }

        public DbSet<IntegrationEventLogEntry> IntegrationEventLogs { get; set; }

        protected override void OnModelCreating(ModelBuilder builder)
        {          
            builder.Entity<IntegrationEventLogEntry>(ConfigureIntegrationEventLogEntry);
        }

        void ConfigureIntegrationEventLogEntry(EntityTypeBuilder<IntegrationEventLogEntry> builder)
        {
            builder.ToTable("IntegrationEventLog");

            builder.HasKey(e => e.EventId);

            builder.Property(e => e.EventId)
                .IsRequired();

            builder.Property(e => e.Content)
                .IsRequired();

            builder.Property(e => e.CreationTime)
                .IsRequired();

            builder.Property(e => e.State)
                .IsRequired();

            builder.Property(e => e.TimesSent)
                .IsRequired();

            builder.Property(e => e.EventTypeName)
                .IsRequired();
        }
    }
}

The Startup.cs of the ASP.NET Core Web API project adds the DB Contexts is as follows:

services.AddEntityFrameworkNpgsql()
                   .AddDbContext<CourseContext>(options =>
                   {
                       options.UseNpgsql(configuration["ConnectionString"],
                           npgsqlOptionsAction: sqlOptions =>
                           {
                               sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
                               sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorCodesToAdd: null);
                           });
                   },
                       ServiceLifetime.Scoped  //Showing explicitly that the DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request)
                   );

            services.AddDbContext<IntegrationEventLogContext>(options =>
            {
                options.UseNpgsql(configuration["ConnectionString"],
                                     npgsqlOptionsAction: sqlOptions =>
                                     {
                                         sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
                                         //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency 
                                         sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorCodesToAdd: null);
                                     });
            });

Is it possible to generate migrations for many DbContext subclasses from within a startup project?

If so, what are the potential causes of not been able to find a DBContext class?

2

2 Answers

2
votes

Solved it by implementing an instance of IDesignTimeDbContextFactory. Found this solution after reading this

Now when I run...

dotnet ef migrations add Initial -s <startup_project> -c <fully.qualified.context.class.name> -o <output dir>

EntityFrameworkCore locates my second context class.

0
votes

The problem is that you are trying to create a code-first database from two different DbContext which are pointed to one database - only a single context can do that.

I would suggest you make a one common DbContext which is only responsible for creating the database. Then you can create your real application contexts that contain subsets from the big one.

Is it possible to generate migrations for many DbContext subclasses from within a startup project?

It is possible to generate migrations for many DbContext subclasses in case they are pointed to different databases.

What are the potential causes of not been able to find a DBContext class?

From the error that you have showed I see that you are looking for the namespace, not for the current DbContext class.

'dcs3spp.courseManagementContainers.BuildingBlocks.IntegrationEventLogEF' should be 'dcs3spp.courseManagementContainers.BuildingBlocks.IntegrationEventLogEF.IntegrationEventLogContext'