I agree with the other commenter that this article is not very clear. I'd go so far as to say it is pretty badly written.
The code snippet refers to "MyApp.Migrations". What is MyApp? Is that the name of the original web project (with dbcontext), or is it the new class library?
And what does "startup assembly" refer to? The web app or the class library?
The fix for removing the circular reference does not work. If each project is supposed to reference the other, then we should expect a circular reference, which is what we get. The suggested fix does not appear to work (not sure if I am doing it wrong), and since no part of the article explains the reasoning behind any of the steps, one cannot even troubleshoot if these steps didn't work.
It would also help to include a few screenshots of how to add a new class library, how to add a reference, and where one can find the XML file where the circular reference fix is supposed to go. After all, we are not all Visual Studio experts.
I have been unable to successfully put migrations in a separate assembly, so I would say this article has not helped me at all.
⚠Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.
Seconded.
Number 3 says _Move the migrations and model snapshot files to the class library._
Do I just move the files, or do I also need to change the namespace?
Number 4 says _Configure the migrations assembly_.
But the code listed after it looks like code I would expect to see in the original DbContext assembly. Where does this go in the Migration assembly? How about a filename?
With the help of https://github.com/bricelam/Sample-SplitMigrations I could create a setup which allows to use the same migrations for both my App and my unit tests. It is what I wanted to accomplish when reading the documentation, hopefully this usecase helps someone else; and possibly when enhancing the documentation.
Setup:
Data (contains MyDbContext & entities)Migrations (contains Migrations; references Data)App (contains startable .Net Core app, references Data + Migrations)Test (contains unit tests, references App + Data + Migrations)Usecase:
I want to use same migrations for both App & Test.
How to prepare:
dotnet ef migrations add InitialMigration --startup-project ../App/ inside the Migrations folderApp, you need to initialize MyDbContext as follows: services.AddDbContext<MyDbContext>(options => options.UseSqlite(connection, x => x.MigrationsAssembly("Migrations")));
Now you can
dotnet ef database update inside the App folder which upgrades the App databaseMyDbContext in your unit tests, applying migrations manuallyUnit Test example code:
[TestClass]
public class MyUnitTest
{
[ClassInitialize]
public static void SetUp(TestContext testContext)
{
//setup DI
var services = new ServiceCollection();
services.AddDbContext<MyDbContext>(options => options.UseSqlite("DataSource=test.sqlite", x => x.MigrationsAssembly("Migrations")));
var serviceCollection = services.BuildServiceProvider();
//migrate db
var context = serviceCollection.GetService<MyDbContext>();
context.Database.Migrate();
}
}
... but how to migrate with continuous delivery (like in Azure DevOps)? Deployment tasks typically access build artifacts only. Is there a way to run migrations after build? Or do I need to include source in the artifacts and add dotnet SDK to task environment? Is dotnet ef database update equivalent to myDbContext.Database.Migrate();?
You can run any commands in the post deployment script of your release task. I did not need any special configuration (I am using a release pipeline which Visual Studio setup automatically). I do not know if its completely equivalent, but I've used both and did not run into any issues.
Azure DevOps is a bit different. The "release" pipelines begin with artifacts (from builds) only. Another complication is I'm doing a self-contained deployment (no dotnet core runtime on target machines), I essentially only have access to the compiled binaries (.exe and dll) at deploy time. I ended up adding a stage in my pipeline to call the .exe with a command line argument which triggers context.Database.Migrate(). I'm hoping this has same behavior as dotnet ef database update. What I'm seeing so far is it is "silent" (no output). Hmm...
The documentation is too brief, but I've managed to get it working as follows (whilst also adhering to Onion Architecture):
public class Country
{
public int Id { get; set; }
public string Name { get; set; }
}
public class AppDbContext : DbContext
{
public DbSet<Country> Countries { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=localhost;Database=App;Trusted_Connection=True;",
x => x.MigrationsAssembly("App.Migrations"));
}
}
'App.Migrations' will be our separate class library just for migrations
'App.Infrastructure' needs to reference 'Microsoft.EntityFrameworkCore.SqlServer' and 'Microsoft.EntityFrameworkCore.Tools'
dotnet ef migrations add InitialCreateDo this from the command line in the 'App.Infrastructure' directory
This will create a 'Migrations' folder in your 'App.Infrastructure' class library with a migration called 'InitialCreate'
Move the 'Migrations' folder from 'App.Infrastructure' to 'App.Migrations' - you will need to update the namespaces after the move
Add a project reference in 'App.Migrations' to 'App.Infrastructure'
Edit the .csproj file for 'App.Migrations' and add an output path:
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<OutputPath>App.Infrastructure\bin\$(Configuration)\</OutputPath>
</PropertyGroup>
The above path will be correct if 'App.Infrastructure' and 'App.Migrations' are in the same directory, if not, the output path will need to be adjusted
On build this results in 'App.Migrations' being output to the 'App.Infrastructure' bin directory - we need to do this as we can't reference 'App.Migrations' in 'App.Infrastructure' as this results in a circular reference, so this is a workaround
Build the solution
Run dotnet ef database update from the command line in the 'App.Infrastructure' directory and this should create the database and create the 'Countries' table
Run dotnet ef migrations add AddCity --project App.Migrations for your next migration
'AddCity' is just another migration to create a 'Cities' table - this requires adding a 'City' class and updating the DbContext
Run the command from the 'App.Infrastructure' directory and the migration will be added to the 'App.Migrations' class library
Note: several of the duplicate issues have scenarios/gotchas that we should make sure are covered.
Lots of good stuff in the comments - hopefully enough for me to get it to work...
Here's another specific problem with the docs. I got as far as item 2:
Add a reference to your DbContext assembly.
Add WHAT KIND OF reference (I assume it's an assembly reference) FROM WHERE (I assume its the existing project assembly that contains the DbContext and migrations) to the DbContext assembly
The examples in these comments put the migrations into a Migrations library, and the entities and DbContext into a Data library. If the point of this repackaging exercise is to allow more than one "startup" project to reference the entities and migrations, why use two class libraries? Is there a fundamental reason why the migrations, entities, and DbContext can't all be moved to a class library, and that library be referenced from the startup project(s)? Is this 2-library scheme necessary, or simply advantageous for some (unstated) reason?
We should add some samples for this page: (similar to this one)
@bricelam Your example was perfect. Thank you very much for posting that here.
I'd also like to see additional documentation explaining how/when to use IDesignTimeDbContextFactory and also the -s option. In my case, my DbContext derived classes are in .net standard 2.0 projects (i have a few legacy .net projects that use the same DB model/code). To get the right DB options, I had to add a factory to my EFCoreMigrationTest.CLI assembly and then also use the -s option like:
PS C:\mydbcontextlib1 dotnet ef migrations add InitialCreate -s ..\EFCoreMigrationTest.CLI\
I eventually figured this out by googling, after I didn't find much useful for this case in the documentation.
Just tried separate migrations project today and was unsuccessful. bricelam's sample looks promising but it is a three-project solution. I'd like to just have a two-project solution. Anyways, back to googling! :)
I just got a two-project solution working with the following setup:
Projects:
MyApp (An ASP.NET Core 3 web app): contains the DbContext implementation (e.g. MyDbContext) and modelsMyApp.Migrations (A .NET Core 3 class library): contains nothing at firstMyApp.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.7">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.7" />
</ItemGroup>
</Project>
Startup.cs ConfigureServices
services.AddDbContext<MyDbContext>(o => o
.UseSqlServer(
"server=(localdb)\\mssqllocaldb;database=mydb;",
x => x.MigrationsAssembly("MyApp.Migrations")));
MyApp.Migrations.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.7" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MyApp\MyApp.csproj" />
</ItemGroup>
<PropertyGroup>
<OutputPath>..\MyApp\bin\$(Configuration)\</OutputPath>
</PropertyGroup>
</Project>
The folder structure is:
srcMyAppMyApp.MigrationsBuild both the projects and open powershell in src and run the following command:
dotnet ef migrations add create --project MyApp.Migrations --startup-project MyApp
dotnet ef command. This is to update the MyApp.Migrations.dll referenced by MyApp.Update the database by running
dotnet ef database update --project MyApp.Migrations --startup-project MyApp
Most helpful comment
Seconded.
Number 3 says _Move the migrations and model snapshot files to the class library._
Do I just move the files, or do I also need to change the namespace?
Number 4 says _Configure the migrations assembly_.
But the code listed after it looks like code I would expect to see in the original DbContext assembly. Where does this go in the Migration assembly? How about a filename?