为何两段.NET Core Program.cs代码执行EF迁移时表现不同?
Great question! The difference in behavior boils down to how EF Core's design-time tools (like dotnet ef migrations add) look for ways to construct your application's host and access your AppDbContext. Let's break this down step by step:
1. EF Core Design-Time Tool Logic
When you run migration commands, EF Core needs to create an instance of your AppDbContext to scaffold migrations. To do this without launching your full application, it looks for specific entry points in your Program class:
- In EF Core 2.0, the tools explicitly search for a static method named
BuildWebHostthat returns anIWebHost. This method gives the tools a direct path to build the application host and pull theDbContextfrom the service container. - Starting in EF Core 2.1, the tools were updated to also recognize a
CreateWebHostBuildermethod that returns anIWebHostBuilder(the pattern you used in Code 2). However, if you're using an older EF Core version (2.0), this method won't be detected.
2. Breakdown of Your Code Snippets
Code 1 (Works Everywhere)
public static void Main(string[] args) { BuildWebHost(args).Run(); } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .Build();
This matches the exact method signature EF Core 2.0+ tools expect. The BuildWebHost method directly returns a fully built IWebHost, so the tools can call it, access the service provider, and resolve your AppDbContext without extra steps.
Code 2 (Throws Error)
public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>();
Here, you're using the newer CreateWebHostBuilder pattern (recommended in .NET Core 2.1+), but:
- If you're on EF Core 2.0, the tools don't recognize this method. They can't automatically call
Build()on theIWebHostBuilderto get the host, so they fail to create theAppDbContext. - Even on newer EF Core versions, edge cases (like missing design-time configuration) might cause the tools to struggle to resolve the context via this method, though this is rare if your
Startupis properly configured.
3. How to Fix Code 2
If you want to stick with the CreateWebHostBuilder pattern, you have two solid options:
- Upgrade EF Core to 2.1 or later: This will make the tools recognize the
CreateWebHostBuildermethod automatically. - Add an
IDesignTimeDbContextFactoryimplementation: This explicitly tells EF Core how to create yourDbContextat design time, bypassing the need to rely on theProgramclass methods. Example:using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; using Microsoft.Extensions.Configuration; using System.IO; public class AppDbContextFactory : IDesignTimeDbContextFactory<AppDbContext> { public AppDbContext CreateDbContext(string[] args) { IConfigurationRoot configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json") .Build(); var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>(); optionsBuilder.UseSqlServer(configuration.GetConnectionString("DefaultConnection")); return new AppDbContext(optionsBuilder.Options); } }
Key Takeaway
The core issue is backward compatibility in EF Core's design-time tooling. Code 1 uses the older, tool-friendly method signature, while Code 2 uses a newer pattern that requires a more recent EF Core version or an explicit factory implementation.
内容的提问来源于stack exchange,提问作者Andy Parinas

