Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation on migrations with multiple projects is unclear #933

Open
garyapps opened this issue Aug 22, 2018 — with docs.microsoft.com · 22 comments
Open

Documentation on migrations with multiple projects is unclear #933

garyapps opened this issue Aug 22, 2018 — with docs.microsoft.com · 22 comments

Comments

Copy link

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.

  1. 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?

  2. And what does "startup assembly" refer to? The web app or the class library?

  3. 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.

  4. 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.


Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

@mloffer
Copy link

mloffer commented Aug 30, 2018

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?

@famoser
Copy link

famoser commented Oct 8, 2018

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:

  • Create projects & link appropriately
  • execute dotnet ef migrations add InitialMigration --startup-project ../App/ inside the Migrations folder
  • in the App, you need to initialize MyDbContext as follows:
 services.AddDbContext<MyDbContext>(options => options.UseSqlite(connection, x => x.MigrationsAssembly("Migrations")));

Now you can

  • execute dotnet ef database update inside the App folder which upgrades the App database
  • use the MyDbContext in your unit tests, applying migrations manually

Unit 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();
	}
}

@divega divega added the help wanted This issue involves technologies where we are not experts. Expert help would be appreciated. label Nov 5, 2018
@fizxmike
Copy link

fizxmike commented Nov 7, 2018

... 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();?

@famoser
Copy link

famoser commented Nov 7, 2018

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.

@fizxmike
Copy link

fizxmike commented Nov 7, 2018

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...

@asad-c
Copy link

asad-c commented Nov 10, 2018

The documentation is too brief, but I've managed to get it working as follows (whilst also adhering to Onion Architecture):

  1. Create a class library 'App.Core'
  • Add a 'Country' domain model to this library:
public class Country
{
	public int Id { get; set; }

	public string Name { get; set; }
}
  • This is just a simple class to get things working quickly
  1. Create a class library 'App.Infrastructure'
  • Add a DbContext to this library:
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'

  1. Run dotnet ef migrations add InitialCreate
  • Do 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'

  1. Create a class library 'App.Migrations'
  • 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

  1. Build the solution

  2. 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

  3. 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

  1. Remember to rebuild the solution every time a migration is added

@divega divega added this to the 2.2.0 milestone Nov 10, 2018
@divega divega removed the help wanted This issue involves technologies where we are not experts. Expert help would be appreciated. label Nov 10, 2018
@divega divega changed the title Documentation unclear, suggestions for improvement Documentation on migrations with multiple projects unclear Nov 10, 2018
@divega divega changed the title Documentation on migrations with multiple projects unclear Documentation on migrations with multiple projects is unclear Nov 10, 2018
@divega divega added help wanted This issue involves technologies where we are not experts. Expert help would be appreciated. punted-for-2.2 labels Feb 21, 2019
@divega divega modified the milestones: 2.2.0, 3.0.0 Feb 21, 2019
@ajcvickers ajcvickers added area-migrations type-enhancement and removed help wanted This issue involves technologies where we are not experts. Expert help would be appreciated. labels Aug 29, 2019
@ajcvickers ajcvickers modified the milestones: 3.0.0, Backlog Aug 29, 2019
@ajcvickers
Copy link
Member

Note: several of the duplicate issues have scenarios/gotchas that we should make sure are covered.

@ajcvickers
Copy link
Member

ajcvickers commented Mar 16, 2021

See also discussions in #3147 and #3152

@VerdonTrigance
Copy link

Guys, I still can't figure out how to add migration to such projects like this one in example https://github.com/dotnet/EntityFramework.Docs/tree/main/samples/core/Schemas/ThreeProjectMigrations ?
When trying to execute this:
dotnet ef migrations add OnDeleteRestrict --project Project.WebAPI -- --Provider Postgre
I've got an error:

Your target project 'Project.WebAPI' doesn't match your migrations assembly 'Project.Migration.Postgre'. Either change your target project or change your migrations assembly.

I already configured startup.cs with:

var provider = Configuration.GetValue("Provider", "Postgre");
            services.AddDbContext<MyDbCtx>(
                options => _ = provider switch
                {
                    "MsSql" => options.UseSqlServer(
                        Configuration.GetConnectionString("MyMsSql"),
                        x => x.MigrationsAssembly("Project.Migration.MsSql")),

                    "Postgre" => options.UseNpgsql(
                        Configuration.GetConnectionString("MyPostgre"),
                        x => x.MigrationsAssembly("Project.Migration.Postgre")),

                    _ => throw new Exception($"Unsupported provider: {provider}. Use 'MsSql' or 'Postgre'.")
                });

Just FYI.
I have successfully executed such thing like:
dotnet ef migrations script --project Project.WebAPI -i -o $(Build.ArtifactStagingDirectory)/Sql/db-postgre.sql -- --Provider Postgre

@bricelam
Copy link
Contributor

@VerdonTrigance These two things contradict each other:

  • --project Project.WebAPI
  • .MigrationsAssembly("Project.Migration.Postgre")

@VerdonTrigance
Copy link

VerdonTrigance commented Mar 24, 2021

@bricelam but that is only way I make
dotnet ef migrations script
working properly. So I tried to do same with migrations add. Is there any other way? I tried neither with or without each of these args. It’s not working.
PS:
I changed current folder from solution root folder to Project.Migration.Postgre and then executed:
dotnet ef migrations add OnDeleteRestrict --startup-project ../Project.WebAPI -- --Provider Postgre
I thought '--project' doing same as 'cd Project.Migration.Postgre', but it doesn't. I'm confused. What is the purpose for project, startup-project and .MigrationsAssembly() ?
Can you add some clarifying readme to the project https://github.com/dotnet/EntityFramework.Docs/tree/main/samples/core/Schemas/ThreeProjectMigrations ?

@shorstok
Copy link

It also seems that TwoProjectMigrations sample is not working — at least, not with context.Database.Migrate(), probably because Migrate() attempts to load migration assembly by calling Assembly.Load with configured MigrationsAssembly argument passed as assembly name, and .net core by default does not look for copied assembly .dll in the application folder.

This could be worked around with setting "Microsoft.NETCore.DotNetHostPolicy.SetAppPaths": true in the main (wokrer) app's runtimeconfig.template.json, but this is extremely non-intuitive, for the sample project at least.

@ajcvickers ajcvickers self-assigned this Nov 3, 2021
@ajcvickers ajcvickers modified the milestones: 6.0.0, 7.0.0 Nov 3, 2021
@dgxhubbard
Copy link

I think this issue #3152 should re-opened, trying to follow the docs which seem to assume most people are running migrations from CLI command line, does not work for the case of context.Database.Migrate. I have done the separate solutions for our different providers and generated the migrations, only to find context.Migrate.Database cannot find the migrations assembly specified in UseMyProvider ( connStr, x => x.MigrationsAssembly ( "MyProviderMigrations" ).

@defense89
Copy link

defense89 commented Feb 10, 2022

The documentation is too brief, but I've managed to get it working as follows (whilst also adhering to Onion Architecture):

  1. Create a class library 'App.Core'
  • Add a 'Country' domain model to this library:
public class Country
{
	public int Id { get; set; }

	public string Name { get; set; }
}
  • This is just a simple class to get things working quickly
  1. Create a class library 'App.Infrastructure'
  • Add a DbContext to this library:
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'
  1. Run dotnet ef migrations add InitialCreate
  • Do 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'
  1. Create a class library 'App.Migrations'
  • 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
  1. Build the solution
  2. 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
  3. 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
  1. Remember to rebuild the solution every time a migration is added

talking about a tutorial!!!!! you got it man a step by step for beginners your awesome.... i get an error however while running dotnet ef migrations add initial create with code , could not find librairy app.migration , i tried to add it but still not working.

probmig

@thewrath
Copy link

thewrath commented Nov 3, 2022

Hello,
For those who are looking for a solution to put Migrations in different assemblies here is a little helper I wrote: https://gist.github.com/thewrath/a6b735efccff34230ceb3ca11a2d9cb4

I simply documented the steps that took me to a working solution. Maybe it can help until the documentation is suitable for a beginner like me 😉.

@bricelam bricelam removed their assignment Jul 8, 2023
@ajcvickers ajcvickers removed their assignment Aug 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests