Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
You can store migrations in a different project from the one containing your DbContext. This is recommended when the application project is platform-specific, such as WinUI, .NET MAUI, Blazor WebAssembly, or Azure Functions, or when it targets a specific runtime identifier (RID). It can also be used to maintain more than one set of migrations.
Tip
You can view this article's sample on GitHub.
Project layout
The sample uses three projects:
| Project | Responsibility | References |
|---|---|---|
WebApplication1.Data |
Owns the DbContext and entity types |
EF Core provider |
WebApplication1.Migrations |
Owns migrations, the model snapshot, and design-time context creation | Data project, EF Core provider, and Microsoft.EntityFrameworkCore.Design |
WebApplication1 |
Runs the application | Data project and migrations project |
The application needs a reference to the migrations project when it discovers or applies migrations at run time, for example by calling Migrate. If migrations are applied only by a deployment artifact and the application never loads them, that reference isn't required.
Configure the projects
Create a class library for the migrations and add a reference to the project containing the
DbContext.Add the database provider and
Microsoft.EntityFrameworkCore.Designto the migrations project. Mark the design package as a private development dependency:<ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="..."> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="..." /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\WebApplication1.Data\WebApplication1.Data.csproj" /> </ItemGroup>Implement
IDesignTimeDbContextFactory<TContext>in the migrations project. The factory allows the tools to create the context without running the application project:public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext> { public ApplicationDbContext CreateDbContext(string[] args) { var connectionString = args.FirstOrDefault() ?? @"Server=(localdb)\mssqllocaldb;Database=WebApplication1;Trusted_Connection=True"; var options = new DbContextOptionsBuilder<ApplicationDbContext>() .UseSqlServer( connectionString, sqlServer => sqlServer.MigrationsAssembly(typeof(ApplicationDbContextFactory).Assembly.GetName().Name)) .Options; return new ApplicationDbContext(options); } }Keep design-time provider and model configuration consistent with the runtime configuration. The sample accepts an optional connection string argument and uses a local development connection when no argument is supplied.
Configure the migrations assembly when registering the context at run time:
services.AddDbContext<ApplicationDbContext>( options => options.UseSqlServer( Configuration.GetConnectionString("DefaultConnection"), x => x.MigrationsAssembly("WebApplication1.Migrations")));If the application applies migrations or otherwise discovers them at run time, add a normal reference from the application to the migrations project:
<ItemGroup> <ProjectReference Include="..\WebApplication1.Migrations\WebApplication1.Migrations.csproj" /> </ItemGroup>The data project must not reference the migrations project. That would create a circular dependency because the migrations project already references the data project.
If migrations already exist, move all migration files and the model snapshot to the migrations project and update their namespaces. When there are no existing migrations, the design-time factory allows the initial migration to be created directly in the migrations project.
Use the tools
Use the migrations project as both the target project and startup project. The target project receives generated files, while the startup project is built and executed by the tools. In this layout, using the migrations project for both prevents the tools from executing application startup code.
Run these commands from the solution directory:
dotnet ef migrations add NewMigration \
--project WebApplication1.Migrations \
--startup-project WebApplication1.Migrations
The same project options apply to other commands:
dotnet ef migrations list \
--project WebApplication1.Migrations \
--startup-project WebApplication1.Migrations
dotnet ef migrations script --output artifacts/migrations.sql \
--project WebApplication1.Migrations \
--startup-project WebApplication1.Migrations
dotnet ef migrations bundle --output artifacts/efbundle \
--project WebApplication1.Migrations \
--startup-project WebApplication1.Migrations
Starting with EF Core 11, repeated project options can be stored in .config/dotnet-ef.json.
Build the migrations project before running commands with --no-build, or before another process consumes its output. A normal dotnet ef command builds the target and startup projects automatically.
Platform-specific applications
Don't use a platform-specific application project as the startup project for EF tools. Mobile, browser, desktop, function, and RID-specific projects can require a workload or native host that dotnet ef can't execute. Starting with EF Core 11, the tools warn when a platform-specific startup project is used.
Use the layout described above for .NET MAUI, WinUI, Blazor WebAssembly, Azure Functions, and similar applications:
- Put the context and entity types in a shared data project.
- Put migrations and
IDesignTimeDbContextFactory<TContext>in a normal cross-platform .NET project. - Run the tools with the migrations project as the target and startup project.
- Reference the migrations project from the application only if the application loads or applies migrations at run time.
Direct tooling support for Xamarin and MAUI platform projects isn't planned; see dotnet/efcore#7152. Xamarin applications should first be upgraded to .NET MAUI.
Process architecture
The process running the tools must be able to load every design-time assembly. A 64-bit Visual Studio or .NET process can't load an x86-only startup assembly, and the same constraint applies to Arm64 and other architectures. Prefer an AnyCPU migrations project. If design-time dependencies require a specific architecture, invoke a matching .NET SDK explicitly.
The design-time process architecture is separate from the deployment target. When creating a bundle, use --target-runtime or -TargetRuntime to generate an artifact for the deployment RID, such as linux-arm64 or osx-arm64.