Использование памяти EntityFramework6 с большим объемом таблицы из-за списка InitializedDatabases - PullRequest
4 голосов
/ 13 января 2020

В нашем приложении имеется большое количество таблиц (около 50 тыс.) - все эти таблицы фактически используются, и это приводит к высокому потреблению памяти в объектной среде.

После некоторого профилирования памяти я заметил, что Классы DbCompiledModel хранились в памяти, поэтому после некоторого поиска они отслеживались до класса LazyInternalContext, который хранит список «InitializedDatabase».

https://github.com/dotnet/ef6/blob/master/src/EntityFramework/Internal/LazyInternalContext.cs#L670

Есть ли способ, чтобы не допустить этого в платформе сущностей? Это не первая настройка кода, настройка базы данных и миграция в этом не выполняются приложение, если это то, что подразумевает "InitializeDatabaseAction".

Установка «return null» или установка «InitializerDisabled» в true заставляет все работать, но не будет запускать сборку пользовательского объекта, а также не знает, как это повлияет на «изменение» источника.

Большинство таблиц имеют одинаковое определение, поэтому также пытались найти решение, которое я нашел здесь: Изменить имя таблицы во время выполнения

При попытке получить сообщение об ошибке "Открытые данные для этой команды существует «читатель», использующий postgres и MARS там не поддерживается (понятия не имею, зачем мне это нужно, это просто меняет sql, который выполняется)

1 Ответ

0 голосов
/ 14 января 2020

Решение было дано в комментарии Бу Ивана Стоева и работает.

Невозможно отключить это без использования отражения, если для свойства InternalContext.InitializerDisabled установить значение true, пропустить словарь .

Итак:

  • Использовать конструктор DbContext, который предоставляет DbCachedModel
  • Использовать Database.SetInitializer (null);
  • Установить InternalContext.InitializerDisabled = true с использованием отражения

Код из примера, который я использовал для проверки этого, в качестве тестовой установки у меня была 1 основная таблица с разделами 30k, сами разделы запрашиваются, потому что postgres (особенно 9.x ) плохо масштабируется при большом количестве разделов:

    public class PartContext : DbContext {
        private static readonly string _ConnectionString = new NpgsqlConnectionStringBuilder {
            Host = "localhost",
            Port = 5432,
            Database = "postgres",
            Username = "postgres",
            Password = "password"
        }.ConnectionString;

        public readonly string Table;
        public readonly string Partition;

        public PartContext(string pMainTable, string pPartition) : base(
            new NpgsqlConnection() { ConnectionString = _ConnectionString },
            PartDbModelBuilder.Get(_ConnectionString, pPartition),
            true
        ) {
            Table = pMainTable;
            Partition = pPartition;

            Database.SetInitializer<PartContext>(null);


            /**
             * Disable database initialization so that the DbCachedModels are not kept internally in Entity
             * This causes high memory usage when having a lot of tables 
             * In EF 6.4.2 there was no way to 'manage' that Dictionary externally
             */
            try {
                var InternalContext = typeof(PartContext).BaseType.GetProperty("InternalContext", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this, null);
                InternalContext.GetType().GetProperty("InitializerDisabled").SetValue(InternalContext, true);
            } catch(Exception) { }
        }

        public DbSet<MyPart> Parts { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder) {
            modelBuilder.HasDefaultSchema("public");
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }
    }

Это обеспечивает DbCachedModels:

Я рекомендую добавить некоторый пользовательский код кэширования и т. д. c, это только из примера

    class PartDbModelBuilder {
        public static DbCompiledModel Get(string pConnectionString, string pTable) {
            DbModelBuilder builder = new DbModelBuilder();
            builder.Entity<MyPart>().ToTable(pTable, "public");
            using (var connection = new NpgsqlConnection() { ConnectionString = pConnectionString }) {
                var obj = builder.Build(connection).Compile();
                return obj;
            }
        }
    }

Это объект, который я использовал в качестве теста:

    public class MyPart {
        public int id { get; set; }
        public string name { get; set; }
        public string value { get; set; }
    }

Класс I, использованный для запуска теста:

    class EFTest {
        public void Run(int tableCount) {
            int done = 0;
            Parallel.For(0, tableCount, new ParallelOptions { MaxDegreeOfParallelism = 5 }, (i) => {
                string id = i.ToString().PadLeft(5, '0');
                using (var context = new PartContext("mypart", "mypart_" + id)) {
                    var objResult = context.Parts.First();
                    Console.WriteLine(objResult.name);
                }
                done++;
                Console.WriteLine(done + " DONE");
            });
        }
    }

Определение таблицы:

    CREATE TABLE IF NOT EXISTS mypart (
        id SERIAL,
        name text,
        value text
    ) partition by list (name);

    CREATE TABLE IF NOT EXISTS part partition of mypart_00000 for values in ('mypart00000');
    CREATE TABLE IF NOT EXISTS part partition of mypart_00001 for values in ('mypart00001');
    CREATE TABLE IF NOT EXISTS part partition of mypart_00002 for values in ('mypart00002');
    ...
* 1 031 *Postgres 9:
    CREATE TABLE IF NOT EXISTS mypart (
        id SERIAL,
        name text,
        value text
    );

    CREATE TABLE IF NOT EXISTS ".$name."( CHECK ( name =  'mypart00000')) INHERITS (mypart);
    CREATE TABLE IF NOT EXISTS ".$name."( CHECK ( name =  'mypart00001')) INHERITS (mypart);
    CREATE TABLE IF NOT EXISTS ".$name."( CHECK ( name =  'mypart00002')) INHERITS (mypart);
    ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...