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


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.