Deleting a model with a composite key bypassing multitenancy
See original GitHub issueWhen deleting a multitenant entity that’s configured to use a composite key in our database migrations scripts and with the EF Core ModelBuilder
isn’t respecting multitenancy.
The database constraint in our migrations script looks like: ALTER TABLE ONLY public."Examples" ADD CONSTRAINT "PK_Examples" PRIMARY KEY ("Number", "TenantId", "Username");
Our EF Core classes look like:
[MultiTenant]
public class ExampleModel
{
public string Username { get; set; }
public string Number { get; set; }
}
public class TestDbContext : MultiTenantDbContext
{
public DbSet<ExampleModel>? Examples { get; set; }
public TestDbContext(TenantInfo tenantInfo,
DbContextOptions options) :
base(tenantInfo, options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ExampleModel>().HasKey(b => new { b.Number, b.Username });
}
}
When we delete an ExampleModel
from the context, all other ExampleModel
s with the same Username
and Number
are deleted as well, even with different TenantId
s.
Should Finbuckle
be adding the TenantId
to the composite key? Is there a way we can add it as part of the model builder such that the call to modelBuilder
mirrors our database constraints? Is there some other solution?
Issue Analytics
- State:
- Created 2 years ago
- Comments:8 (4 by maintainers)
Alright I removed the generator and filed a bug with the efcore team. They said only on “adding” a tracked entity will it pick up a shadow key property. I voted on an issue to expand that to any method which attaches an entity.
The best option here is to do exactly what you did – or to use the
TenantNotSetMode
which as of the latest release should work as intended with the generator removed.Thanks!
Ok, having dug in I can explain some parts but not all. Thank you for isolating the behavior so well.
HandleNoTenantIdInCompositeKeyAndShadowTenantIdInModel
andHandleNoTenantIdInCompositeKeyAndExplicitTenantIdInModel
I would expect to fail because EFCore doesn’t even knowTenantId
is intended to be part of the key. Finbuckle leans heavily on global query filters for tenant data isolation and unfortunately they don’t apply to delete statements. EFCore seems to use the key columns it knows about to generate delete statements hence wiping the records for all tenants.HandleTenantIdInCompositeKeyAndExplicitTenantIdInModel
works as expected and I recommend you explicitly add TenantId to models if within your control – shadow properties just cause so many headaches. ForHandleAdjustedCompositeKeyAndShadowTenantIdInModel
this should work but I think we might be hitting this bug so I posed a question there.TenantNotSetMode
is the default of throw so it should actually prevent the save attempt. This makes me realize the value generator for TenantId is masking this intended behavior. I’ll have to take a closer look at this.I hope this help and thanks for all the details.