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

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

Это выглядит примерно так:

public class Demo {

    public IProductRepository productRepository { get; }

    static void Main(string[] args) {
        productRepository = new DependencyResolver(ConfigureServices).ServiceProvider.GetService<IProductRepository>();

        //list of products just for demo purposes
        List<Product> productList = getMagicalProductList();

        //do the work here
        foreach (Product p in productList) {
            productRepository.AddProduct(p);
        }
    }

    private void ConfigureServices(IServiceCollection services)
    {
        services.AddTransient<IProductRepository, ProductRepository>();
    }

}

А ProductRepository выглядит так:

public class ProductRepository : IProductRepository {

    public MyDbContext Context { get; }

    public ProductRepository(MyDbContext context) {
        Context = context;
    }

    public void AddProduct(Product product) {
        Context.products.Add(product);
        Context.SaveChanges();
    }

}

Теперь, если вы посмотрите на раздел //do the work here в моем методе Demo.Main(), вы увидите, что он использует один и тот же контекст для вставки всех объектов Product в список. Это плохо? Если я создам новый контекст для каждой итерации цикла, не будет ли это пустой тратой ресурсов?

Кроме того, допустим, я хочу каждый раз создавать новый контекст, как это будет выглядеть в коде? Шаблон репозитория отвечает за обработку контекста, поэтому похоже, что мне придется что-то там изменить, что позволит ему автоматически создавать и удалять контексты при каждом вызове.

Обновление: в итоге я изменил свой код для обработки данных в пакетах, поэтому теперь он создает новый контекст для каждого пакета и вызывает SaveChanges() только один раз в конце каждого пакета.

Что ж, создавать каждый раз новый контекст означало бы генерировать тысячи контекстов — пустая трата ресурсов. Что еще более важно, я бы группировал вызов SaveChanges (т.е. вызывал бы его один раз каждые 100-1000 записей). Однако в целом шаблон репозитория с EF означает дублирование усилий без уважительной причины (EF уже реализует репозиторий и UoW).

Camilo Terevinto 10.12.2020 18:39
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
64
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Не вызывайте SaveChanges после добавления каждого продукта. Это неэффективно. Звоните SaveChanges один раз.

Кроме того, если вы заботитесь о производительности, я бы посоветовал не использовать средство отслеживания изменений EF Core для заполнения базы данных таким объемом данных. Есть расширения, которые могут вставить эти данные за несколько секунд, например linq2db.EntityFrameworkCore

context.BulkCopy(new BulkCopyOptions(), productList);

Если я вызову SaveChanges() после вставки, скажем, 100 000 элементов, не будет ли это использовать много памяти? Кроме того, что, если я хочу быть уверенным, что изменение было зафиксировано в базе данных, потому что это запустит отдельный процесс, чтобы выполнить дополнительную работу с этим недавно вставленным фрагментом данных?

user928112 10.12.2020 18:48

Конечно, после каждого вызова SaveChanges. EF генерирует новый SQL-запрос, совершает двусторонний обмен данными с базой данных, регистрирует объект в средстве отслеживания изменений. Сохраняет копию старых значений. Может быть, что-то еще.

Svyatoslav Danyliv 10.12.2020 18:51

Чем больше элементов вы вставляете, тем больше времени ChangeTracker использует для обнаружения изменений.

Svyatoslav Danyliv 10.12.2020 18:54

Что мне делать, если я хочу, чтобы EF забыл о недавно добавленных элементах? Эти объекты нигде не упоминаются после того, как они добавлены в базу данных, поэтому я думал, что сборщик мусора все равно их подберет.

user928112 10.12.2020 18:56

Если вы вставляете несколько элементов, вызовите SaveChanges один раз. После этого вы должны удалить DataContext, обычно это делается контейнером IoC с областью действия. Затем следует собрать память.

Svyatoslav Danyliv 10.12.2020 19:01

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

user928112 10.12.2020 19:02

В чем проблема? Вы переопределили SaveChanges и запускаете какую-то сложную обработку?

Svyatoslav Danyliv 10.12.2020 19:06

Не было никаких проблем. Я изменил свой код, чтобы вызывать SaveChanges() только один раз каждые 100 записей. В моем коде я пропустил часть, где он отправляет сообщение в очередь, сообщая отдельной задаче начать дальнейшую обработку данных, которые она получает из базы данных, но все должно быть в порядке, даже если это происходит каждые 100 записей.

user928112 10.12.2020 19:34

Красиво, так лучше. Еще лучше, вы можете пересоздавать контекст для каждых 100-1000 записей, это упростит обнаружение изменений. Потому что EF запоминает ваши ранее вставленные данные и пытается обнаружить изменения в уже сохраненных данных.

Svyatoslav Danyliv 10.12.2020 19:42

Другие вопросы по теме