엔티티 프레임 워크에서 추적 된 엔티티를 지우는 방법


84

속도가 감소함에 따라 많은 엔티티에 걸쳐 실행되는 수정 코드를 실행하고 있습니다. 즉, 컨텍스트에서 추적되는 엔티티의 수가 반복 될 때마다 증가하기 때문에 시간이 오래 걸릴 수 있으므로 마지막에 변경 사항을 저장합니다. 각 반복의. 각 반복은 독립적이며 이전에로드 된 엔티티를 변경하지 않습니다.

변경 내용 추적을 끌 수 있다는 것을 알고 있지만 대량 삽입 코드가 아니기 때문에 원하지 않습니다. 엔티티를로드하고 몇 가지를 계산하고 숫자가 올바르지 않으면 새 숫자를 설정하고 업데이트 / 삭제 / 생성합니다. 일부 추가 엔티티. 각 반복에 대해 새 DbContext를 만들 수 있으며 동일한 인스턴스에서 모두 수행하는 것보다 더 빨리 실행될 수 있다는 것을 알고 있지만 더 나은 방법이있을 수 있다고 생각합니다.

그래서 질문은; 이전에 db 컨텍스트에로드 된 엔티티를 지우는 방법이 있습니까?


7
호출 만하면 context.Entry(entity).State = EntityState.Detached특정 엔터티 추적이 중지됩니다.
Ben Robinson

2
새 컨텍스트를 인스턴스화하지 않는 이유는 무엇입니까? 최적화 된 코드가 필요하지 않으면 큰 오버 헤드가 없습니다.
Adrian Nasui 2014

엔터티 프레임 워크는 변경된 엔터티에 대해서만 데이터베이스 서버에 도달하므로 성능 문제는 없습니다. 하지만 작업 속도를 높이기 위해 작업하는 테이블로만 구성된 새 컨텍스트를 만들 수 있습니다.
İsmet Alkan 2014

1
@IsThat 그래서 변경 사항을 감지하는 데 시간이 걸리므로 DbPerformance에 대해 걱정하지 않습니다.
hazimdikenli

실제로 성능 병목 현상을 디버깅하고 추적 했습니까? 아니면 이것을 가정 했습니까?
İsmet Alkan 2014

답변:


120

DbContextChangeTracker를 사용하여 추가, 수정 및 삭제 된 모든 항목을 분리하는 확장 메서드에 메서드를 추가 할 수 있습니다 .

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
'Where'다음에 'ToList'를 호출해야합니다. 그렇지 않으면 System.InvalidOperationException : 'Collection was modified; 열거 작업이 실행되지 않을 수 있습니다. '
mabead

6
내 단위 테스트에서 항목 상태는 "Unmodified"입니다. 테스트 메서드의 끝에서 롤백하는 트랜잭션을 사용하기 때문입니다. 즉, 현재 상태를 확인하지 않고 추적 된 항목 상태를 "분리됨"으로 설정해야 테스트가 한 번에 모두 올바르게 실행됩니다. 트랜잭션을 롤백 한 직후 위의 코드를 호출했지만 확인했습니다. 롤백은 확실히 Unmodified 상태를 의미합니다.
barbara.post

2
(그리고 var entity정말해야 var entry는 항목이 실제 엔티티 아니므로)
oatsoda

2
@DavidSherret 그럴 수도 있다고 생각했습니다! 내 테스트 앱에서 1000 개 항목을 순환하고 Detached로 표시하는 데 기존 코드로 약 6000ms가 걸렸기 때문에 이것을 잡았습니다. 새로운 약 15ms :)
oatsoda

3
e.State == EntityState.Unchanged도 사용해야하지 않습니까? 엔터티는 변경되지 않았지만 여전히 컨텍스트에서 추적되며 DetectChanges 중에 고려되는 엔터티 집합의 일부입니다. 예를 들어 새 엔터티를 추가하고 (Added 상태에 있음) SaveChanges를 호출하면 추가 된 엔터티가 이제 Unchanged 상태가됩니다 (UnitOfWork 패턴에 위배되지만 op 요청 : 각 반복이 끝날 때 변경 사항을 저장하고 있습니다 ).
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 개가 가장 좋습니다.
Yegor Androsov

2
@pwrigshihanomoronimo와 동의하면 컨텍스트는 UnitOfWork 디자인 패턴을 따릅니다. Martin Fowler가 정의한대로 :> 비즈니스 트랜잭션의 영향을받는 개체 목록을 유지하고> 변경 내용 작성 및 동시성> 문제 해결을 조정합니다.
Michel

이것은 나를 위해 속임수를 쓰는 것 같았습니다. 2 백만 개의 행이있는 테이블에 약 1 백만 개의 트랜잭션 (삽입 및 업데이트)이있는 데이터를 동기화하고 있습니다. 그래서 잠시 후 (또는 여러 작업) OutOfMemoryException으로 고생했습니다. 이 문제는 컨텍스트를 다시 인스턴스화하는 자연스러운 장소 인 루프 수 X 개당 새 DbContext를 만들 때 해결되었습니다. EF 및 장기 실행 작업에서 가능한 메모리 누수에 대해 생각할 필요가없는 더 나은 방식으로 GC를 트리거했을 수 있습니다. 감사합니다!
Mats Magnem

확실하지 의존성 주입이 작동하는지 확인
사브리나 Leggett 보낸

그것은 문맥에서 유래되었다 상관없이 작동합니다 @SabrinaLeggett
Ogglas

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()and를 호출 DbContext.Resurrect()하지만 향후 릴리스에서 변경 될 수 있으므로 코드에서 직접 호출하지 않는 것이 좋습니다.) https://github.com/aspnet/EntityFrameworkCore/blob/v2 .2.1 / src / EFCore / Internal / DbContextPool.cs # L157


1

EF Core 3.0에는 ChangeTracker를 재설정 할 수 있는 내부 API 가 있습니다. 프로덕션 코드에서는 사용하지 마십시오. 시나리오에 따라 테스트하는 데 도움이 될 수 있으므로 언급합니다.

using Microsoft.EntityFrameworkCore.Internal;

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

코드에 대한 주석이 말했듯이;

이는 Entity Framework Core 인프라를 지원하고 공용 API와 동일한 호환성 표준이 적용되지 않는 내부 API입니다. 모든 릴리스에서 예고없이 변경 또는 제거 될 수 있습니다. 새 Entity Framework Core 릴리스로 업데이트 할 때 응용 프로그램 오류가 발생할 수 있다는 점을 염두에두고 코드에서 직접 사용해야합니다.



-2

글쎄요, 제 경험상, EF 또는 어떤 조직이든 너무 많은 압력이나 복잡한 모델에서는 잘 작동하지 않는다고 생각합니다.

추적하고 싶지 않다면 정말 왜 orm을하는지 말할 것입니다.

속도가 주된 힘이라면 저장 프로 시저와 좋은 인덱싱을 능가하는 것은 없습니다.

그리고 쿼리가 항상 ID 단위 인 경우 키와 json 만있는 nosql 또는 SQL을 사용하는 것이 좋습니다. 이것은 클래스와 테이블 간의 임피던스 문제를 피할 수 있습니다.

귀하의 경우 시나리오의 경우 이러한 방식으로 객체를로드하는 것이 매우 느립니다. 실제로 귀하의 경우에는 저장 프로 시저가 네트워크를 통한 데이터 전송을 피하고 sql이 집계 등을 관리하는 데 훨씬 빠르고 최적화되어 있기 때문에 더 좋습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.