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

Write guidance for using Entity Framework migrations with Aspire solutions #64

Closed
gameraliaz opened this issue Nov 18, 2023 · 14 comments · Fixed by #838
Closed

Write guidance for using Entity Framework migrations with Aspire solutions #64

gameraliaz opened this issue Nov 18, 2023 · 14 comments · Fixed by #838
Assignees
Labels
database Content related to database. doc-enhancement Improve the current content [org][type][category] documentation Improvements or additions to documentation help wanted Good for community contributors to help [up-for-grabs] Pri1 High priority, do before Pri2 and Pri3 📌 seQUESTered Identifies that an issue has been imported into Quest.

Comments

@gameraliaz
Copy link

gameraliaz commented Nov 18, 2023

Type of issue

Missing information

Description

my AppHost program.cs:

var builder = DistributedApplication.CreateBuilder(args);

var sql = builder.AddSqlServerContainer("sql",password:"P4s$wordBuT$tr0ng",port:14420).AddDatabase("sqldata");

builder.AddProject<Projects.adminendpoint>("admin").WithReference(sql);

builder.Build().Run();

Then i want to run it but first make database from my dbcontext that used in admin endpoint.
admin endpoint program.cs is:

builder.AddSqlServerDbContext<DataBaseContext>("sqldata");
builder.Services.AddScoped<IDataBaseContext, DataBaseContext>();

and my dbcontext is :

public class DataBaseContext : DbContext, IDataBaseContext
{
    public DataBaseContext(DbContextOptions dbContextOptions):base(dbContextOptions) { }
    public DbSet<User> Users { get; set; }
    public DbSet<Role> Roles { get; set; }
    public DbSet<UserInRole> UserInRoles { get; set; }
}

So how should i use Add-Migration and Update-Database?

Page URL

https://learn.microsoft.com/en-us/dotnet/aspire/database/sql-server-entity-framework-component?tabs=dotnet-cli

Content source URL

https://github.com/dotnet/docs-aspire/blob/main/docs/database/sql-server-entity-framework-component.md

Document Version Independent Id

2cd52f56-0ee5-8f04-c2b5-fe52c7f8109f

Article author

IEvangelist


Associated WorkItem - 186558

@davidfowl
Copy link
Member

Excellent question! This is one of the big problems we hit while developing eshop as well. Adding migrations requires putting a connection string in the appSettings.json or EF will blow up.

For update database, we for the first preview, we settled on a separate application that runs as part of the app:

https://github.com/dotnet/aspire/tree/main/samples/eShopLite/CatalogDb

See dotnet/aspire#398 for more info.

@peteraritchie
Copy link
Contributor

Excellent question! This is one of the big problems we hit while developing eshop as well. Adding migrations requires putting a connection string in the appSettings.json or EF will blow up.

So, the connection string in code will only be known to the other project at run-time. Even if the connection string was in the appsettings for AppHost, the other project would only know about the value at run-time, not at design-time (i.e. when using dotnet ef). Bummer.

@davidfowl
Copy link
Member

Yes this is a problem.

@IEvangelist
Copy link
Member

@davidfowl - I assume we need to call this out in docs? If so, what exactly should we say?

@IEvangelist IEvangelist self-assigned this Nov 27, 2023
@IEvangelist IEvangelist added documentation Improvements or additions to documentation question Further information is requested 🗺️ reQUEST Triggers an issue to be imported into Quest. Pri1 High priority, do before Pri2 and Pri3 labels Nov 27, 2023
@github-actions github-actions bot added 📌 seQUESTered Identifies that an issue has been imported into Quest. and removed 🗺️ reQUEST Triggers an issue to be imported into Quest. labels Nov 28, 2023
@adegeo adegeo added enhancement doc-enhancement Improve the current content [org][type][category] and removed enhancement question Further information is requested labels Dec 8, 2023
@IEvangelist IEvangelist removed their assignment Jan 11, 2024
@IEvangelist IEvangelist added the help wanted Good for community contributors to help [up-for-grabs] label Jan 18, 2024
@IEvangelist IEvangelist mentioned this issue Feb 21, 2024
@JamesNK JamesNK changed the title Add-Migration and Update-Database with SqlServerContainer Guidance for using Entity Framework migrations with Aspire solutions Mar 4, 2024
@JamesNK JamesNK changed the title Guidance for using Entity Framework migrations with Aspire solutions Write guidance for using Entity Framework migrations with Aspire solutions Mar 4, 2024
@CamSoper CamSoper added the database Content related to database. label Mar 19, 2024
@jhancock-taxa
Copy link

jhancock-taxa commented May 29, 2024

FWIW, this is still poorly documented. It just says run the migration, it doesn't say any of the above, and of course the migration system doesn't spin a postgress database nor is there any tooling to do so so you have to have a second instance of postgress around for migrations at all times.

Ideally we could use a DesignTimeFactory in the AppHost to generate the new migration after it spun up the database engine and applied the migrations that were already there.

Or we would be able to keep the containers running always and not have them shutdown and then we could reference them by hand I guess.

Here's my approach that works pretty +$%! well if I do say so myself:

public sealed class DataContextDesignTimeFactory : IDesignTimeDbContextFactory<DataContext>
{
    public DataContext CreateDbContext(string[] args)
    {
        var builder = DistributedApplication.CreateBuilder(args);

        var postgres = builder.AddPostgres(Resources.POSTGRES_SERVER)
            .WithDataVolume() //Persistence between executions
            .AddDatabase(Resources.POSTGRES_DATABASE, databaseName: "Taxa");

        //Apply the migrations to the database.
        builder.AddProject<Projects.Database_Migrations>(Jobs.DATABASE_MIGRATIONS)
            .WithReference(postgres);


        var optionsBuilder = new DbContextOptionsBuilder<DataContext>();
        optionsBuilder.UseNpgsql(Resources.POSTGRES_DATABASE);
        return new DataContext(optionsBuilder.Options);
    }
}

What this does is spin postgres, run the migrations job per the docs, then creates the data context and returns it.

You can run it like this:

dotnet ef migrations --project ./Data/Data.csproj --startup-project ./AppHost/AppHost.csproj add Initial

(replace your paths as you wish of course)

This means that you can easily script this as a vs code task as an example and you're golden.

Of course the Tools had to be added as a package to the AppHost, but otherwise this just works.

Note

This content was automatically updated to filter out profanity. If you feel as though this was done in error, please file an issue with the details.

@CamSoper
Copy link
Contributor

@JHTaxa The article that was written depended heavily on the sample written by James-Newton King. It spins up a migration service at runtime to run the migrations using the same context as the app and then shut down.

Your method looks fine to my eyes, but I'd feel better if David Fowler or James NK had a chance to chime in. Can you please open another issue in this repo specifically regarding your solution?

@onionhammer
Copy link

Does this sample tackle how to apply updates via console.. for example to rollback a migration?

@jhancock-taxa
Copy link

@JHTaxa The article that was written depended heavily on the sample written by James-Newton King. It spins up a migration service at runtime to run the migrations using the same context as the app and then shut down.

Your method looks fine to my eyes, but I'd feel better if David Fowler or James NK had a chance to chime in. Can you please open another issue in this repo specifically regarding your solution?

I'd argue that my solution should be part of the article and sample. As it is right now the article is incomplete and can't work without major hacks (i.e. a second database docker instance running). My version solves the problem and should be added to both the sample and the article.

If that needs to be a new issue, then ok, but really the article and sample are insufficent to actually make ef core migrations work.

PS: This is going to be the exact same issues with OpenAPI documentation in .net 9 because it has to execute the endpoints to be able to generate the documentation, it doesn't work with reflection.

@CamSoper
Copy link
Contributor

I seriously don't mean to be flippant, and I'm trying hard to understand, but are we talking about the same article?

I just followed the steps in the article to the letter except I replaced the SQL Server provider with the Npgsql provider, and it seems to run the migrations on startup just fine.

To be completely transparent, I'm kinda a latecomer to the Aspire docs party, so I'm playing catch-up a bit. I'm not sure what you mean about requiring a second docker container in this context.

Please forgive my thrashing as I try to understand the problem. I would still like this to be opened under a new issue so we can get the right eyes on it (but also so I can track my work internally).

@jhancock-taxa
Copy link

You missed the part that doesn't work just fine: Creating the migrations in the first place.

It requires that you either have a fake docker container that you have a known connection string, or you run your host, get the connection string, then prey that Vs.net didn't lock your data project and it will build and run again with that new connection string you put in just for the purpose of creating the migration.

My solution solves all of that and allows migration creation to just work.

If you add @davidfowl 's WaitForCompletion() function, you can block until the migrations are complete so that your services don't throw errors on startup in the host like the suggested example does right now.

Honestly, the EF team or someone needs to take what I just did, make it generic, add in the sample service as a job that can be script built as a pre-deploy job and have it all just work out of the box.

@onionhammer
Copy link

You don't need a database connection to create migrations

@jhancock-taxa
Copy link

The first migration definitely did, a faux connection string didn't work and failed. After that I don't know because I fixed it with a proper IDesignTimeDbContextFactory that uses the AppHost which solves the problem correctly.

@onionhammer
Copy link

onionhammer commented May 30, 2024

You can delete all your migrations, then run dotnet ef migrations add something and it will create files happily without any running database.

@jhancock-taxa
Copy link

jhancock-taxa commented May 30, 2024

It has to construct the project wit DI and the actual data context to do so. If you have the most simplistic scenario, that probably works. But if you're doing any advanced stuff that won't work. (i.e. it just built it up based on your appsettings.config file and did it all for you and did things you weren't aware it was doing. As soon as you start using the hostbuilder for ef migrations, you'll see that it isn't that simple.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
database Content related to database. doc-enhancement Improve the current content [org][type][category] documentation Improvements or additions to documentation help wanted Good for community contributors to help [up-for-grabs] Pri1 High priority, do before Pri2 and Pri3 📌 seQUESTered Identifies that an issue has been imported into Quest.
Projects
No open projects
Status: Slipped
Status: ✅ Done
Status: Slipped
Development

Successfully merging a pull request may close this issue.

8 participants