Кэширование моделей Entity Framework делает его бесполезным при большом количестве схем баз данных. - PullRequest
0 голосов
/ 27 февраля 2019

Я работаю с продуктом SaaS, который имеет довольно большую базу пользователей.До сих пор наш подход к изоляции данных о клиентах заключался в том, чтобы иметь конкретные клиентские базы данных.Это отлично работает с Entity Framework 6, так как все, что нам нужно сделать, это передать специфическую для клиента строку соединения в DbContext, и все работает отлично.

По причинам, не имеющим отношения к этому вопросу, мы должны отойти от этогоодна база данных для модели клиента.С точки зрения изоляции данных было бы неплохо иметь одну схему базы данных для каждого клиента вместо одной базы данных для каждого клиента.После выполнения некоторых тестов кажется, что это довольно непригодно, когда мы говорим о большом количестве различных схем.

Вот упрощенный пример того, как мы в настоящее время используем DbContext:

public class CustomDbContext : DbContext

    public CustomDbContext(IConnectionStringProvider provider)
        : base(provider.ConnectionString)
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new SomeEntityMap());
        modelBuilder.Configurations.Add(new SomeOtherEntityMap());
    }
}

И вот пример того, как мы думали, что это может работать:

public class CustomDbContext : DbContext, IDbModelCacheKeyProvider

    public CustomDbContext(IConnectionStringProvider provider)
        : base(provider.ConnectionString)
    {
        CacheKey = provider.Schema;
    }

    public string CacheKey { get; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.HasDefaultSchema(CacheKey);
        modelBuilder.Configurations.Add(new SomeEntityMap());
        modelBuilder.Configurations.Add(new SomeOtherEntityMap());
    }
}

Microsoft была достаточно любезна, чтобы разрешить обход кэширования по умолчанию для модели базы данных.Использование имени схемы в качестве ключа кэша вынуждает Entity Framework создавать новую модель для каждой схемы.В теории это работает.На практике не очень.Я создал тестовое приложение, которое отправляет запросы в службу, которая вызывает создание экземпляра DbContext.Он рандомизирует CacheKey из группы из 5000 ключей, поэтому в основном при первом запуске приложения почти каждый запрос вызывает OnModelCreating().После нескольких сотен запросов процесс IIS Worker сожрал всю доступную память (использовал около 9 ГБ), загрузка ЦП была близка к 100%, а служба практически остановилась.

Я посмотрел на источник Entity Frameworkкодирует и надеется, что использование пустой строки со строителем модели HasDefaultSchema() заставит EF использовать схему по умолчанию для пользователя базы данных.Затем мы могли бы кэшировать только одну модель и иметь схему, «определенную в строке подключения», установив схему по умолчанию для учетных данных каждого клиента в базе данных.Однако EF выдает исключение, если схема представляет собой пустую строку.

Таким образом, вопрос в том, сталкивался ли кто-нибудь с такой же проблемой, и если да, то как вы ее решили?Если решение состоит в том, чтобы просто разветвить Entity Framework, я был бы признателен за понимание того, насколько обширными являются требуемые изменения.

1 Ответ

0 голосов
/ 27 февраля 2019

Спасибо, Иван Стоев, за то, что указал мне правильное направление.Перехватчик был абсолютно простым способом преодолеть эту проблему.Протестировано с 1000 последовательных запросов и не оказывает заметного влияния на время выполнения при использовании перехватчика.Этот подход не будет работать с EF Migrations без дополнительной работы, но поскольку мы не используем его, это не проблема.

РЕДАКТИРОВАТЬ: некоторые исправления в примере

Вот пример начто, кажется, делает трюк:

public class CustomDbContext : DbContext
{
    static CustomDbContext()
    {
        Database.SetInitializer<CustomDbContext>(null);
        DbInterception.Add(new SchemaInterceptor());
    }

    public CustomDbContext(IConnectionStringProvider provider)
        : base(provider.ConnectionString)
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.HasDefaultSchema("RemoveThisDefaultSchema");
        modelBuilder.Configurations.Add(new SomeEntityMap());
        modelBuilder.Configurations.Add(new SomeOtherEntityMap());
    }
}

public class SchemaInterceptor : IDbCommandInterceptor
{
    public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
    }

    public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
        command.CommandText = command.CommandText.Replace("[RemoveThisDefaultSchema].", string.Empty);
    }

    public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
    }

    public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        command.CommandText = command.CommandText.Replace("[RemoveThisDefaultSchema].", string.Empty);
    }

    public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
    }

    public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
        command.CommandText = command.CommandText.Replace("[RemoveThisDefaultSchema].", string.Empty);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...