We have a multi-tenant solution and have decided to model it in EF by passing the TenantId to the DbContext and using that value for both grabbing the tenant-specific connection string (inside of the OnConfiguring method), as well as for declaring a global filter on each entity.
At the same time, we are using dedicated IEntityTypeConfiguration implementations for each of our entities, to keep code more manageable (vs putting everything in the DbContext's OnModelCreating method), and leveraging the ApplyConfigurationsFromAssembly method to automatically find and apply all such instances.
In this scenario, we are unable to specify the global filter inside of each IEntityTypeConfiguration implementation, because there is no way to transfer the tenantId value from the DbContext instance to the configuration classes when using that scanning/auto-construction approach.
We currently have a dependency injection-based mechanism to provide tenant information to classes in our codebase, by injecting a ITenantContext abstraction in the constructor.
We can provide the tenantId to the DbContext this way, since it supports DI, but the same cannot be done for the underlying IEntityTypeConfiguration classes since they are not resolved by DI. Instead, IEntityTypeConfiguration instances are created using the raw Activator.CreateInstance method, which in turn require the classes to have parameterless constructors.
This feels like an arbitrary and confusing limitation: if DI is used when resolving dependencies in the context, it should be used by the configuration class instances as well.
The proposal is that ApplyConfigurationsFromAssembly is changed to rely on ActivatorUtilities.CreateInstance (or equivalent) instead of on the non-DI Activator.CreateInstance method for constructing the configuration classes, so that we can inject dependencies into the IEntityTypeConfiguration implementations.
In our particular use case, we want to be able to inject tenant-specific abstractions for declaring a tenant-limiting global filter on the entity as per above.
Workaround
We have 2 less-than-ideal ways to work around this limitation today.
Manually provide each implementation of IEntityTypeConfiguration and propagate the ITenantContext via constructor argument this way
This of course breaks down today, since the implementation is an inner private class on the entity class. This not only would force us to add each class manually but also change our organization and start exposing the configuration implementation to the DbContext.
Create our own scanning extension that uses ActivatorUtilities in conjunction with IServiceProvider to construct the configuration instances
This is probably what we'll eventually end up doing but we'd rather the built-in method just did it for us so we didn't have to maintain such custom extension. And since we don't control the DbContext base dependencies, we are forced to manually pass IServiceProvider this way:
Problem
We have a multi-tenant solution and have decided to model it in EF by passing the
TenantId
to theDbContext
and using that value for both grabbing the tenant-specific connection string (inside of theOnConfiguring
method), as well as for declaring a global filter on each entity.At the same time, we are using dedicated
IEntityTypeConfiguration
implementations for each of our entities, to keep code more manageable (vs putting everything in theDbContext
'sOnModelCreating
method), and leveraging theApplyConfigurationsFromAssembly
method to automatically find and apply all such instances.In this scenario, we are unable to specify the global filter inside of each
IEntityTypeConfiguration
implementation, because there is no way to transfer thetenantId
value from theDbContext
instance to the configuration classes when using that scanning/auto-construction approach.We currently have a dependency injection-based mechanism to provide tenant information to classes in our codebase, by injecting a
ITenantContext
abstraction in the constructor.We can provide the
tenantId
to theDbContext
this way, since it supports DI, but the same cannot be done for the underlyingIEntityTypeConfiguration
classes since they are not resolved by DI. Instead,IEntityTypeConfiguration
instances are created using the rawActivator.CreateInstance
method, which in turn require the classes to have parameterless constructors.https://github.com/dotnet/efcore/blob/d1e1dfa531dbf2d90df4a42ba71890ed5025dffd/src/EFCore/ModelBuilder.cs#L538
This feels like an arbitrary and confusing limitation: if DI is used when resolving dependencies in the context, it should be used by the configuration class instances as well.
In essence, we'd like for this change to work:
Proposal
The proposal is that
ApplyConfigurationsFromAssembly
is changed to rely onActivatorUtilities.CreateInstance
(or equivalent) instead of on the non-DIActivator.CreateInstance
method for constructing the configuration classes, so that we can inject dependencies into theIEntityTypeConfiguration
implementations.In our particular use case, we want to be able to inject tenant-specific abstractions for declaring a tenant-limiting global filter on the entity as per above.
Workaround
We have 2 less-than-ideal ways to work around this limitation today.
Manually provide each implementation of
IEntityTypeConfiguration
and propagate theITenantContext
via constructor argument this wayThis of course breaks down today, since the implementation is an inner
private
class on the entity class. This not only would force us to add each class manually but also change our organization and start exposing the configuration implementation to theDbContext
.Create our own scanning extension that uses
ActivatorUtilities
in conjunction withIServiceProvider
to construct the configuration instances This is probably what we'll eventually end up doing but we'd rather the built-in method just did it for us so we didn't have to maintain such custom extension. And since we don't control theDbContext
base dependencies, we are forced to manually passIServiceProvider
this way: