Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageVersion Include="System.IO.Hashing" Version="10.0.3" />
<PackageVersion Include="Vogen" Version="7.0.0" />
<PackageVersion Include="Weasel.EntityFrameworkCore" Version="8.9.0" />
<PackageVersion Include="Weasel.Postgresql" Version="8.9.0" />
<PackageVersion Include="Weasel.EntityFrameworkCore" Version="8.9.1" />
<PackageVersion Include="Weasel.Postgresql" Version="8.9.1" />
<PackageVersion Include="WolverineFx.Marten" Version="4.2.0" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
Expand Down
129 changes: 129 additions & 0 deletions src/Marten.EntityFrameworkCore.Tests/EfCoreSchemaTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
using System;
using System.Linq;
using Marten.Testing.Harness;
using Microsoft.EntityFrameworkCore;
using Shouldly;
using Weasel.Postgresql.Tables;
using Xunit;

namespace Marten.EntityFrameworkCore.Tests;

/// <summary>
/// Entities and DbContext for testing schema handling with EF Core integration.
/// Reproduces https://github.com/JasperFx/marten/issues/4175
/// </summary>
public class EntityType
{
public int Id { get; set; }
public string Key { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
}

public class Entity
{
public Guid Id { get; set; }
public int EntityTypeId { get; set; }
public bool Featured { get; set; }
public string InternalName { get; set; } = string.Empty;

public EntityType EntityType { get; set; } = null!;
}

/// <summary>
/// DbContext that places entities in an explicit "test_ef_schema" schema,
/// separate from the Marten document store schema.
/// </summary>
public class SeparateSchemaDbContext : DbContext
{
public const string EfSchema = "test_ef_schema";

public SeparateSchemaDbContext(DbContextOptions<SeparateSchemaDbContext> options) : base(options)
{
}

public DbSet<Entity> Entities => Set<Entity>();
public DbSet<EntityType> EntityTypes => Set<EntityType>();

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema(EfSchema);

modelBuilder.Entity<EntityType>(entity =>
{
entity.ToTable("entity_type");
entity.HasKey(e => e.Id);
});

modelBuilder.Entity<Entity>(entity =>
{
entity.ToTable("entity");
entity.HasKey(e => e.Id);
entity.HasOne(e => e.EntityType)
.WithMany()
.HasForeignKey(e => e.EntityTypeId)
.OnDelete(DeleteBehavior.Cascade);
});
}
}

public class EfCoreSchemaTests
{
[Fact]
public void should_respect_ef_core_explicit_schema_and_not_move_tables_to_marten_schema()
{
// Issue #4175: When EF Core entities have an explicit schema configured,
// AddEntityTablesFromDbContext should NOT move those tables into Marten's schema.
const string martenSchema = "test_marten_schema";

using var store = DocumentStore.For(opts =>
{
opts.Connection(ConnectionSource.ConnectionString);
opts.DatabaseSchemaName = martenSchema;

opts.AddEntityTablesFromDbContext<SeparateSchemaDbContext>();
});

// Find the EF Core tables that were registered
var extendedObjects = store.Options.Storage.ExtendedSchemaObjects;

var entityTable = extendedObjects.OfType<Table>()
.FirstOrDefault(t => t.Identifier.Name == "entity");
var entityTypeTable = extendedObjects.OfType<Table>()
.FirstOrDefault(t => t.Identifier.Name == "entity_type");

entityTable.ShouldNotBeNull();
entityTypeTable.ShouldNotBeNull();

// These tables should remain in the EF Core schema, NOT Marten's schema
entityTable.Identifier.Schema.ShouldBe(SeparateSchemaDbContext.EfSchema,
"Entity table should stay in the EF Core schema, not be moved to Marten's schema");
entityTypeTable.Identifier.Schema.ShouldBe(SeparateSchemaDbContext.EfSchema,
"EntityType table should stay in the EF Core schema, not be moved to Marten's schema");
}

[Fact]
public void should_move_tables_without_explicit_schema_to_marten_schema()
{
// When EF Core entities do NOT have an explicit schema,
// they should still be moved to Marten's schema (existing behavior).
const string martenSchema = "test_marten_schema";

using var store = DocumentStore.For(opts =>
{
opts.Connection(ConnectionSource.ConnectionString);
opts.DatabaseSchemaName = martenSchema;

// TestDbContext does not set an explicit schema
opts.AddEntityTablesFromDbContext<TestDbContext>();
});

var extendedObjects = store.Options.Storage.ExtendedSchemaObjects;

var orderSummariesTable = extendedObjects.OfType<Table>()
.FirstOrDefault(t => t.Identifier.Name == "ef_order_summaries");

orderSummariesTable.ShouldNotBeNull();
orderSummariesTable.Identifier.Schema.ShouldBe(martenSchema,
"Tables without explicit schema should be moved to Marten's schema");
}
}
9 changes: 6 additions & 3 deletions src/Marten.EntityFrameworkCore/EfCoreProjectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,12 @@ public static void AddEntityTablesFromDbContext<TDbContext>(this StoreOptions op
{
var table = migrator.MapToTable(entityType);

// Move EF Core tables into the same schema as the Marten store
// so they participate in schema migration and cleanup together
if (!string.IsNullOrEmpty(schemaName) && table is Table pgTable)
// Only move tables to the Marten schema if the entity does NOT have an
// explicit schema configured in EF Core. When a user has deliberately placed
// entities in a separate schema (e.g., via HasDefaultSchema or ToTable("name", "schema")),
// that schema should be respected. See https://github.com/JasperFx/marten/issues/4175
var efSchema = entityType.GetSchema();
if (efSchema == null && !string.IsNullOrEmpty(schemaName) && table is Table pgTable)
{
pgTable.MoveToSchema(schemaName);
}
Expand Down
Loading