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

TableBuilder.ExcludeFromMigration is not Excluding transitive dependencies #35725

Closed
Denrage opened this issue Mar 4, 2025 · 2 comments
Closed
Labels
area-migrations closed-no-further-action The issue is closed and no further action is planned. customer-reported

Comments

@Denrage
Copy link

Denrage commented Mar 4, 2025

Bug description

I have a project where i use multiple DbContexts, which can have dependencies to one another. The migrations should be managed by one DbContext only, to not have multiple migrations adding/changing the same entities.

For that use case, i want to exclude specific entities and all of it dependent entities from migrations.
Using modelBuilder.Entity<ENTITYTYPE>().ToTable(t => t.ExcludeFromMigration()) is excluding the entity but not its dependencies, which defeats the purpose of using that method in my use case.

It is possible to add ExcludeFromMigration for every Entity, but that would need to be maintained continually and the bigger problem are Many-To-Many-Relationships on convention which will create Join-Tables, that don't have a EntityType represented in code.

Your code

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace ExcludeMigrationReproduction;

internal class Program
{
    private static void Main(string[] args) => Console.WriteLine("Hello, World!");
}

public class BookDbContext : DbContext
{
    public DbSet<Book> Books { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Author>().ToTable(t => t.ExcludeFromMigrations());
        base.OnModelCreating(modelBuilder);
    }

    protected override void OnConfiguring(DbContextOptionsBuilder options)
        => options.UseSqlite($"Data Source=E:\\test.db");
}

public class AuthorDbContext : DbContext
{
    public DbSet<Author> Authors { get; set; }

    public DbSet<City> Cities { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder options)
        => options.UseSqlite($"Data Source=E:\\test.db");
}

public class Author
{
    public Guid Id { get; set; }

    public required City City { get; set; }
}

public class City
{
    public Guid Id { get; set; }

    public required string Name { get; set; }
}

[EntityTypeConfiguration(typeof(EntityConfiguration))]
public class Book
{
    public Guid Id { get; set; }

    public required ICollection<Author> Authors { get; set; }

    private class EntityConfiguration : IEntityTypeConfiguration<Book>
    {
        public void Configure(EntityTypeBuilder<Book> builder) => builder.HasMany(x => x.Authors).WithMany();
    }
}

Stack traces


Verbose output

╰─ dotnet ef migrations add Initial --context ExcludeMigrationReproduction.BookDbContext --verbose
Using project 'C:\Users\USER\source\repos\ExcludeMigrationReproduction\ExcludeMigrationReproduction.csproj'.
Using startup project 'C:\Users\USER\source\repos\ExcludeMigrationReproduction\ExcludeMigrationReproduction.csproj'.
dotnet msbuild /target:GetEFProjectMetadata /property:EFProjectMetadataFile=C:\Users\USER\AppData\Local\Temp\tmppvjrys.tmp /verbosity:quiet /nologo C:\Users\USER\source\repos\ExcludeMigrationReproduction\ExcludeMigrationReproduction.csproj
dotnet msbuild /target:GetEFProjectMetadata /property:EFProjectMetadataFile=C:\Users\USER\AppData\Local\Temp\tmps2dgdu.tmp /verbosity:quiet /nologo C:\Users\USER\source\repos\ExcludeMigrationReproduction\ExcludeMigrationReproduction.csproj
Build started...
dotnet build C:\Users\USER\source\repos\ExcludeMigrationReproduction\ExcludeMigrationReproduction.csproj /verbosity:quiet /nologo /p:PublishAot=false
Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:01.45

Workload updates are available. Run `dotnet workload list` for more information.

Build succeeded.
dotnet exec --depsfile C:\Users\USER\source\repos\ExcludeMigrationReproduction\bin\Debug\net9.0\ExcludeMigrationReproduction.deps.json --additionalprobingpath C:\Users\USER\.nuget\packages --additionalprobingpath "C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages" --additionalprobingpath "C:\Program Files (x86)\Microsoft\Xamarin\NuGet" --additionalprobingpath "C:\Program Files\dotnet\sdk\NuGetFallbackFolder" --runtimeconfig C:\Users\USER\source\repos\ExcludeMigrationReproduction\bin\Debug\net9.0\ExcludeMigrationReproduction.runtimeconfig.json C:\Users\USER\.dotnet\tools\.store\dotnet-ef\9.0.2\dotnet-ef\9.0.2\tools\net8.0\any\tools\netcoreapp2.0\any\ef.dll migrations add Initial --context ExcludeMigrationReproduction.BookDbContext --assembly C:\Users\USER\source\repos\ExcludeMigrationReproduction\bin\Debug\net9.0\ExcludeMigrationReproduction.dll --project C:\Users\USER\source\repos\ExcludeMigrationReproduction\ExcludeMigrationReproduction.csproj --startup-assembly C:\Users\USER\source\repos\ExcludeMigrationReproduction\bin\Debug\net9.0\ExcludeMigrationReproduction.dll --startup-project C:\Users\USER\source\repos\ExcludeMigrationReproduction\ExcludeMigrationReproduction.csproj --project-dir C:\Users\USER\source\repos\ExcludeMigrationReproduction\ --root-namespace ExcludeMigrationReproduction --language C# --framework net9.0 --nullable --working-dir C:\Users\USER\source\repos\ExcludeMigrationReproduction --verbose
Using assembly 'ExcludeMigrationReproduction'.
Using startup assembly 'ExcludeMigrationReproduction'.
Using application base 'C:\Users\USER\source\repos\ExcludeMigrationReproduction\bin\Debug\net9.0'.
Using working directory 'C:\Users\USER\source\repos\ExcludeMigrationReproduction'.
Using root namespace 'ExcludeMigrationReproduction'.
Using project directory 'C:\Users\USER\source\repos\ExcludeMigrationReproduction\'.
Remaining arguments: .
Finding DbContext classes...
Using environment 'Development'.
Finding IDesignTimeDbContextFactory implementations...
Finding DbContext classes in the project...
Found DbContext 'BookDbContext'.
Found DbContext 'AuthorDbContext'.
Finding application service provider in assembly 'ExcludeMigrationReproduction'...
Finding Microsoft.Extensions.Hosting service provider...
No static method 'CreateHostBuilder(string[])' was found on class 'Program'.
No application service provider was found.
Using context 'BookDbContext'.
Finding design-time services referenced by assembly 'ExcludeMigrationReproduction'...
Finding design-time services referenced by assembly 'ExcludeMigrationReproduction'...
No referenced design-time services were found.
Finding design-time services for provider 'Microsoft.EntityFrameworkCore.Sqlite'...
Using design-time services from provider 'Microsoft.EntityFrameworkCore.Sqlite'.
Finding IDesignTimeServices implementations in assembly 'ExcludeMigrationReproduction'...
No design-time services were found.
Writing migration to 'C:\Users\USER\source\repos\ExcludeMigrationReproduction\Migrations\20250304125059_Initial.cs'.
Writing model snapshot to 'C:\Users\USER\source\repos\ExcludeMigrationReproduction\Migrations\BookDbContextModelSnapshot.cs'.
'BookDbContext' disposed.
Done. To undo this action, use 'ef migrations remove'

EF Core version

9.0.2

Database provider

Microsoft.EntityFrameworkCore.Sqlite

Target framework

.NET 9.0

Operating system

Windows 11

IDE

Visual Studio 2022 17.13.0

@cincuranet
Copy link
Contributor

This would be a perfect fit for custom convention (standup). You could do it in one place, hence it is not error prone, one line to enable/disable it and with IModelFinalizingConvention you can handle join entities for M:N as well.

@Denrage
Copy link
Author

Denrage commented Mar 6, 2025

Using a convention works flawlessly, thanks! I didn't think about it doing it that way and had a tunnel vision that i would need to adjust the MigrationBuilder instead.
Still i think that the option is working counter-intuitive right now. Why would i want to exclude an Entity and don't want to exclude the dependent Entity when i use Entities from another DbContext? The documentation also states "if you want to use an Entity from another DbContext you can exclude it for migrations with ...", so it appears to be the main use case why it was added.
Therefor i still think this should be possible without a custom convention.

(The convention if other ppl should stumble upon this)

public class ExcludeMigrationConvention : IModelFinalizingConvention
{
    public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
    {
        var entities = modelBuilder.Metadata.GetEntityTypes();
        foreach (var item in entities)
        {
            if (item.IsTableExcludedFromMigrations())
            {
                this.ExcludeFromMigration(item);
            }
        }
    }

    public void ExcludeFromMigration(IConventionEntityType entity)
    {
        foreach (var navigation in entity.GetDeclaredNavigations())
        {
            if (!navigation.TargetEntityType.IsTableExcludedFromMigrations())
            {
                navigation.TargetEntityType.SetIsTableExcludedFromMigrations(true);
                this.ExcludeFromMigration(navigation.TargetEntityType);
            }
        }

        foreach (var skipNavigation in entity.GetSkipNavigations())
        {
            if (!skipNavigation.TargetEntityType.IsTableExcludedFromMigrations())
            {
                skipNavigation.TargetEntityType.SetIsTableExcludedFromMigrations(true);
                skipNavigation.JoinEntityType.SetIsTableExcludedFromMigrations(true);
                this.ExcludeFromMigration(skipNavigation.TargetEntityType);
            }
        }
    }
}

@cincuranet cincuranet closed this as not planned Won't fix, can't repro, duplicate, stale Mar 6, 2025
@cincuranet cincuranet added the closed-no-further-action The issue is closed and no further action is planned. label Mar 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-migrations closed-no-further-action The issue is closed and no further action is planned. customer-reported
Projects
None yet
Development

No branches or pull requests

2 participants