10

The story: in our multi-tenant app (one PostgreSql db, multiple schemas) we need to use one DbContext against multiple schemas.

What I tried: holding a cache (Dictionary, where key is schema name, value is the context for that schema). When instatiating new context for another schema I can see that dbContext schema is still set to previous schema provided. I assume the model in context is cached internally by context type, so that is the reason I see this behavior?

So above doesn't seem to work and I found that implementing IModelCacheKeyFactory should do the trick. Does anyone know what should go into Create method though? There are no samples nor documentation anywhere.

What I found: Dynamically changing schema in Entity Framework Core but it answers for EF6, so not much help.

Community
  • 1
  • 1
Tomas Voracek
  • 5,886
  • 1
  • 25
  • 41

2 Answers2

21

Here is an example.

Derived DbContext that replaces it's ModelCacheKey (and factory) with a Custom one.

class MyDbContext : DbContext
{
    public MyDbContext(string schema)
    {
        Schema = schema;
    }

    public string Schema { get; }

    protected override void OnConfiguring(DbContextOptionsBuilder options)
        => options
            .UseSqlServer("...")
            .ReplaceService<IModelCacheKeyFactory, MyModelCacheKeyFactory>();

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

        // ...
    }
}

The factory that creates the Context with a specific key.

class MyModelCacheKeyFactory : IModelCacheKeyFactory
{
    public object Create(DbContext context)
        => new MyModelCacheKey(context);
}

The custom ModelCacheKey per context.

class MyModelCacheKey : ModelCacheKey
{
    string _schema;

    public MyModelCacheKey(DbContext context)
        : base(context)
    {
        _schema = (context as MyDbContext)?.Schema;
    }

    protected override bool Equals(ModelCacheKey other)
        => base.Equals(other)
            && (other as MyModelCacheKey)?._schema == _schema;

    public override int GetHashCode()
    {
        var hashCode = base.GetHashCode() * 397;
        if (_schema != null)
        {
            hashCode ^= _schema.GetHashCode();
        }

        return hashCode;
    }
}
Erik Philips
  • 53,428
  • 11
  • 128
  • 150
bricelam
  • 28,825
  • 9
  • 92
  • 117
  • 1
    The entire class `MyModelCacheKey` for making identify based on the value of `MyDbContext.Schema` can be replaced with a much simpler `ValueTuple` as described in https://learn.microsoft.com/en-us/ef/core/modeling/dynamic-model – n0099 Jun 06 '23 at 10:53
2

There actually have demo project in docs https://github.com/aspnet/EntityFramework.Docs/tree/master/samples/core/DynamicModel adding post for convinience !

Diaskhan
  • 128
  • 1
  • 7