Як очистити відстежувані сутності в рамках сутності


84

Я запускаю деякий код виправлення, який запускається через велику купу сутностей, оскільки його прогрес зменшується, це означає, що кількість відстежуваних сутностей у контексті збільшується з кожною ітерацією. Це може зайняти багато часу, тому я зберігаю зміни в кінці кожної ітерації. Кожна ітерація є незалежною і не змінює попередньо завантажені сутності.

Я знаю, що можу відключити відстеження змін, але не хочу, оскільки це не код масової вставки, а завантаження сутностей та обчислення кількох речей, а якщо цифри неправильні, встановіть нові номери та оновіть / видаліть / створіть деякі додаткові сутності. Я знаю, що можу створити новий DbContext для кожної ітерації, і, ймовірно, це буде працювати швидше, ніж робити все в тому ж екземплярі, але я думаю, що може бути кращий спосіб.

Тож питання в тому; Чи є спосіб очищення сутностей, раніше завантажених у контексті db?


7
Ви можете просто зателефонувати, context.Entry(entity).State = EntityState.Detachedі це припинить відстеження цієї конкретної сутності.
— Бен Робінсон,

2
Чому б вам просто не створити інстанцію нового контексту? Насправді великих накладних витрат немає, якщо вам не потрібен дуже оптимізований код.
— Адріан Насуї

entity framework потрапляє на сервер бази даних лише для змінених сутностей, у вас немає проблем щодо продуктивності. але ви можете створити новий контекст, що складається лише з таблиць, з якими ви працюєте, щоб зробити це швидшим.
— Ісмет Алкан

1
@IsThatТак що виявлення змін потребує часу, мене не турбує DbPerformance.
— hazimdikenli

Ви насправді налагоджували та відстежували вузьке місце в роботі, або просто припускаєте це?
— Ісмет Алкан

Відповіді:


120

Ви можете додати метод до свого DbContextабо методу розширення, який використовує ChangeTracker для від'єднання всіх доданих, модифікованих та видалених сутностей:

public void DetachAllEntities()
{
    var changedEntriesCopy = this.ChangeTracker.Entries()
        .Where(e => e.State == EntityState.Added ||
                    e.State == EntityState.Modified ||
                    e.State == EntityState.Deleted)
        .ToList();

    foreach (var entry in changedEntriesCopy)
        entry.State = EntityState.Detached;
}

5
Не забудьте зателефонувати "ToList" після "Where". В іншому випадку викине System.InvalidOperationException: 'Колекція була змінена; операція перерахування може не виконуватися. '
— mabead

6
У моєму модульному тесті стан записів - "Немодифікований", maybse, оскільки я використовую транзакцію, яку відкочую в кінці методу тестування. Це означало, що мені довелося встановити для стану відстежених записів значення "Від'єднано", не перевіряючи поточний стан, щоб мої тести працювали правильно відразу. Я називаю вищезазначений код відразу після відкочування транзакції, але я його зрозумів, відкат, безумовно, означає незмінений стан.
— barbara.post

2
(а також var entityнасправді має бути, var entryоскільки це вхід не фактична Сутність)
— oatsoda

2
@DavidSherret Думав, що це може бути так! Я зрозумів це, тому що в моєму тестовому додатку, переїзд через 1000 елементів та позначення як відокремлений зайняв приблизно 6000 мс з існуючим кодом. Близько 15 мс з новим :)
— oatsoda

3
Чи не слід вам також використовувати e.State == EntityState.Unchanged? Хоча сутність є незмінною, вона все ще відстежується в контексті та є частиною набору сутностей, які розглядаються під час DetectChanges. Наприклад, ви додаєте нову сутність (вона в стані Додано), викликаєте SaveChanges, і додана сутність тепер має стан Незмінений (це суперечить шаблону UnitOfWork, але оператор запитав: я зберігаю зміни в кінці кожної ітерації ).
— jahav

28

1. Можливість: від'єднати вхід

dbContext.Entry(entity).State = EntityState.Detached;

Коли ви від'єднаєте запис, відстежувач змін припинить його відстежувати (і це має призвести до кращої продуктивності)

Див .: http://msdn.microsoft.com/de-de/library/system.data.entitystate(v=vs.110).aspx

2. Можливість: робота з власним Statusполем + відключені контексти

Можливо, ви хочете самостійно контролювати статус своєї сутності, щоб ви могли використовувати відключені графіки. Додайте властивість для статусу сутності та перетворіть цей статус у під dbContext.Entry(entity).Stateчас виконання операцій (для цього використовуйте сховище)

public class Foo
{
    public EntityStatus EntityStatus { get; set; }
}

public enum EntityStatus
{
    Unmodified,
    Modified,
    Added
}

Див. Наступне посилання для прикладу: https://www.safaribooksonline.com/library/view/programming-entity-framework/9781449331825/ch04s06.html


Я думаю, що додавання методу розширення та запуск усіх сутностей у ChangeTracker та їх від’єднання повинні працювати.
— hazimdikenli

15

Я запускаю службу Windows, яка щохвилини оновлює значення, і у мене була та сама проблема. Я спробував запустити рішення @DavidSherrets, але через кілька годин це теж повільно. Моє рішення полягало в тому, щоб просто створити такий контекст, як цей, для кожного нового запуску. Просто, але це працює.

_dbContext = new DbContext();


5
Це не "просто, але працює" рішення для вашої мети. Це єдино правильний. Контекст повинен жити якомога менше, найкращий варіант - 1 контекст на 1 транзакцію.
— Єгор Андросов

2
Погоджуючись з @pwrigshihanomoronimo, контекст відповідає шаблону дизайну UnitOfWork. Як визначено Мартіном Фаулером:> Веде перелік об’єктів, на які впливає ділова транзакція, і> координує виписування змін та вирішення проблем паралельності>.
— Мішель

Здавалося, це зробило для мене фокус. Я синхронізую дані, маючи приблизно половину мільйона транзакцій (вставка та оновлення) у таблицях з парою мільйонів рядків. Тож я через деякий час (або ряд операцій) боровся з OutOfMemoryException. Це вирішилося, коли я створив новий DbContext на кожну кількість циклів X, що було природним місцем для повторного встановлення контексту. Це, мабуть, спровокувало GC кращим чином, не потрібно думати про можливий витік пам’яті в EF та тривалі операції. Дякую!
— Mats Magnem

не впевнений, чи це працює при введенні залежності
— Сабріна Леггетт

@SabrinaLeggett Це повинно працювати незалежно від того, звідки походить контекст
— Огглас,

4

Я просто зіткнувся з цією проблемою і врешті-решт натрапив на краще рішення для тих, хто використовує типову ін’єкцію залежностей .NET Core. Для кожної операції ви можете використовувати DbContext з масштабом. Це буде скинуто, DbContext.ChangeTrackerщоб SaveChangesAsync()не заглиблюватися в перевірку сутностей із минулих ітерацій. Ось приклад методу контролера ASP.NET Core:

    /// <summary>
    /// An endpoint that processes a batch of records.
    /// </summary>
    /// <param name="provider">The service provider to create scoped DbContexts.
    /// This is injected by DI per the FromServices attribute.</param>
    /// <param name="records">The batch of records.</param>
    public async Task<IActionResult> PostRecords(
        [FromServices] IServiceProvider provider,
        Record[] records)
    {
        // The service scope factory is used to create a scope per iteration
        var serviceScopeFactory =
            provider.GetRequiredService<IServiceScopeFactory>();

        foreach (var record in records)
        {
            // At the end of the using block, scope.Dispose() will be called,
            // release the DbContext so it can be disposed/reset
            using (var scope = serviceScopeFactory.CreateScope())
            {
                var context = scope.ServiceProvider.GetService<MainDbContext>();

                // Query and modify database records as needed

                await context.SaveChangesAsync();
            }
        }

        return Ok();
    }

Враховуючи, що в проектах ASP.NET Core зазвичай використовується DbContextPool, це навіть не створює / руйнує об'єкти DbContext. (Якщо вам було цікаво, DbContextPool насправді викликає DbContext.ResetState()та DbContext.Resurrect(), але я не рекомендував би телефонувати безпосередньо з вашого коду, оскільки вони, ймовірно, будуть змінюватися в наступних випусках.) Https://github.com/aspnet/EntityFrameworkCore/blob/v2 .2.1 / src / EFCore / Internal / DbContextPool.cs # L157


1

У EF Core 3.0 є внутрішній API, який може скинути ChangeTracker. Не використовуйте це у виробничому коді, я згадую це, оскільки це може допомогти комусь у тестуванні залежно від сценарію.

using Microsoft.EntityFrameworkCore.Internal;

_context.GetDependencies().StateManager.ResetState();

Як сказано в коментарі до кодексу;

Це внутрішній API, який підтримує базову інфраструктуру Entity Framework і не підпадає під ті ж стандарти сумісності, що і загальнодоступні API. Він може бути змінений або видалений без попередження у будь-якому випуску. Ви повинні використовувати його безпосередньо у своєму коді лише з особливою обережністю, знаючи, що це може призвести до збоїв програми під час оновлення до нового випуску Entity Framework Core.



-2

Ну, моя думка така, що, на моєму досвіді, EF або будь-який орган не працює добре під великим тиском або складною моделлю.

Якщо ви не хочете відстежувати, справді я б сказав, навіщо взагалі робити орм?

Якщо швидкість є головною силою, ніщо не може перевершити збережені процедури та хорошу індексацію.

І далі, якщо ваші запити завжди відповідають ідентифікатору, розгляньте можливість використання nosql або, можливо, sql з лише key і json. Це дозволить уникнути проблеми імпедансу між класами та таблицями.

Для вашого випадку завантаження речей у об’єкти таким чином мені здається дуже повільним. Насправді у вашому випадку збережені процедури є кращими, тому що ви уникаєте транспортування даних через мережу, а sql набагато швидший та оптимізованіший для управління агрегацією та подібними речами.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.