[linq] 엔티티 프레임 워크 :이 명령과 연관된 열린 DataReader가 이미 있습니다.

Entity Framework를 사용하고 있으며 때때로이 오류가 발생합니다.

EntityCommandExecutionException
{"There is already an open DataReader associated with this Command which must be closed first."}
   at System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands...

수동 연결 관리를 수행하지 않더라도.

이 오류는 간헐적으로 발생합니다.

오류를 유발하는 코드 (쉽게 읽기 위해 단축) :

        if (critera.FromDate > x) {
            t= _tEntitites.T.Where(predicate).ToList();
        }
        else {
            t= new List<T>(_tEntitites.TA.Where(historicPredicate).ToList());
        }

매번 새로운 연결을 열기 위해 Dispose 패턴을 사용합니다.

using (_tEntitites = new TEntities(GetEntityConnection())) {

    if (critera.FromDate > x) {
        t= _tEntitites.T.Where(predicate).ToList();
    }
    else {
        t= new List<T>(_tEntitites.TA.Where(historicPredicate).ToList());
    }

}

여전히 문제가있다

연결이 이미 열려있는 경우 EF가 연결을 재사용하지 않는 이유는 무엇입니까?



답변

연결을 끊는 것이 아닙니다. EF는 연결을 올바르게 관리합니다. 이 문제에 대한 나의 이해는 단일 연결에서 여러 개의 데이터 검색 명령 (또는 여러 개의 선택이있는 단일 명령)이 실행되고 다음 DataReader가 첫 번째 읽기가 완료되기 전에 실행된다는 것입니다. 예외를 피하는 유일한 방법은 여러 개의 중첩 된 DataReaders = MultipleActiveResultSets를 켜는 것입니다. 이 상황이 항상 발생하는 또 다른 시나리오는 쿼리 결과를 반복하고 (IQueryable) 반복 내에서로드 된 엔티티에 대한 지연로드를 트리거하는 것입니다.


답변

MARS (MultipleActiveResultSets)를 사용하는 대신 여러 결과 집합을 열지 않도록 코드를 작성할 수 있습니다.

당신이 할 수있는 일은 데이터를 메모리로 가져 오는 것입니다. 그러면 리더를 열지 않을 것입니다. 다른 결과 집합을 열려고하는 동안 결과 집합을 반복하여 발생하는 경우가 종종 있습니다.

샘플 코드 :

public class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
}

public class Blog
{
    public int BlogID { get; set; }
    public virtual ICollection<Post> Posts { get; set; }
}

public class Post
{
    public int PostID { get; set; }
    public virtual Blog Blog { get; set; }
    public string Text { get; set; }
}

다음을 포함하는 데이터베이스에서 조회를 수행한다고 가정 해 보겠습니다.

var context = new MyContext();

//here we have one resultset
var largeBlogs = context.Blogs.Where(b => b.Posts.Count > 5);

foreach (var blog in largeBlogs) //we use the result set here
{
     //here we try to get another result set while we are still reading the above set.
    var postsWithImportantText = blog.Posts.Where(p=>p.Text.Contains("Important Text"));
}

다음 과 같이 .ToList () 를 추가하여 간단한 해결책을 찾을 수 있습니다 .

var largeBlogs = context.Blogs.Where(b => b.Posts.Count > 5).ToList();

이것은 엔티티 프레임 워크가 목록을 메모리에로드하도록 강제하므로, foreach 루프에서 목록을 반복 할 때 더 이상 데이터 리더를 사용하여 목록을 열지 않고 대신 메모리에 있습니다.

예를 들어 일부 속성을 지연로드하려는 경우 이것이 바람직하지 않을 수 있음을 알고 있습니다. 이것은 주로이 문제가 어떻게 / 왜 발생하는지 설명하는 적절한 예이므로 그에 따라 결정을 내릴 수 있습니다.


답변

이 문제를 극복하는 또 다른 방법이 있습니다. 더 나은 방법인지 여부는 상황에 따라 다릅니다.

이 문제는 게으른 로딩으로 인해 발생하므로 다음을 포함하여 지연 로딩을 피하는 것이 좋습니다.

var results = myContext.Customers
    .Include(x => x.Orders)
    .Include(x => x.Addresses)
    .Include(x => x.PaymentMethods);

적절한를 사용하면 IncludeMARS를 사용하지 않아도됩니다. 그러나 하나를 놓치면 오류가 발생하므로 MARS를 사용하는 것이 가장 쉬운 방법입니다.


답변

반복하려는 컬렉션이 지연 로딩 (IQueriable) 인 경우이 오류가 발생합니다.

foreach (var user in _dbContext.Users)
{
}

IQueriable 컬렉션을 다른 열거 가능한 컬렉션으로 변환하면이 문제가 해결됩니다. 예

_dbContext.Users.ToList()

참고 : .ToList ()는 매번 새 세트를 작성하므로 대용량 데이터를 처리하는 경우 성능 문제가 발생할 수 있습니다.


답변

옵션을 생성자에 추가하여 문제를 쉽게 (실용적으로) 해결했습니다. 따라서 필요할 때만 사용합니다.

public class Something : DbContext
{
    public Something(bool MultipleActiveResultSets = false)
    {
        this.Database
            .Connection
            .ConnectionString = Shared.ConnectionString /* your connection string */
                              + (MultipleActiveResultSets ? ";MultipleActiveResultSets=true;" : "");
    }
...


답변

연결 문자열을 설정하여 시도하십시오 MultipleActiveResultSets=true. 데이터베이스에서 멀티 태스킹이 가능합니다.

Server=yourserver ;AttachDbFilename=database;User Id=sa;Password=blah ;MultipleActiveResultSets=true;App=EntityFramework

app.config의 연결 또는 프로그래밍 방식으로 설정했는지 여부에 관계없이 도움이됩니다.


답변

원래 내 API 클래스에서 정적 필드를 사용하여 MyDataContext 객체 (MyDataContext가 EF5 Context 객체 인 경우)의 인스턴스를 참조하기로 결정했지만 문제가 발생한 것 같습니다. 모든 API 메소드에 다음과 같은 코드를 추가하여 문제를 해결했습니다.

using(MyDBContext db = new MyDBContext())
{
    //Do some linq queries
}

다른 사람들이 언급했듯이 EF 데이터 컨텍스트 개체는 스레드로부터 안전하지 않습니다. 따라서 정적 개체에 배치하면 올바른 조건에서 “데이터 판독기”오류가 발생합니다.

필자의 원래 가정은 개체의 인스턴스를 하나만 만드는 것이 더 효율적이고 더 나은 메모리 관리를 제공한다는 것입니다. 내가이 문제를 연구하면서 수집 한 것에서는 그렇지 않습니다. 실제로 API에 대한 각 호출을 격리 된 스레드 안전 이벤트로 처리하는 것이 더 효율적인 것 같습니다. 개체가 범위를 벗어날 때 모든 리소스가 올바르게 해제되도록합니다.

이는 API를 WebService 또는 REST API로 공개 할 다음 자연스럽게 진행하는 경우 특히 의미가 있습니다.

폭로

  • 운영체제 : Windows Server 2012
  • .NET : 4.0을 사용하여 4.5, Project 설치
  • 데이터 소스 : MySQL
  • 응용 프로그램 프레임 워크 : MVC3
  • 인증 : 양식