NOLOCK이있는 엔터티 프레임 워크


138

NOLOCKEntity Framework에서이 기능을 사용하려면 어떻게 해야합니까? XML이이를 수행 할 수있는 유일한 방법입니까?

답변:


207

아니요, 그러나 트랜잭션을 시작하고 격리 수준을 커밋되지 않은 읽기로 설정할 수 있습니다 . 이것은 본질적으로 NOLOCK과 동일하지만 테이블 단위로 수행하는 대신 트랜잭션 범위 내의 모든 항목에 대해 수행합니다.

그것이 원하는 것처럼 들리면 다음과 같이하십시오.

//declare the transaction options
var transactionOptions = new System.Transactions.TransactionOptions();
//set it to read uncommited
transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;
//create the transaction scope, passing our options in
using (var transactionScope = new System.Transactions.TransactionScope(
    System.Transactions.TransactionScopeOption.Required, 
    transactionOptions)
)

//declare our context
using (var context = new MyEntityConnection())
{
    //any reads we do here will also read uncomitted data
    //...
    //...
    //don't forget to complete the transaction scope
    transactionScope.Complete();
}

우수한 @DoctaJonez EF4에 새로 도입 된 것이 있습니까?
FMFF

@FMFF EF4에 새로 도입 된 것이 있는지 모르겠습니다. 위의 코드는 EFv1 이상에서 작동한다는 것을 알고 있습니다.
닥터 존스

결과는 어떻습니까? 누군가 위에서 언급 한 블록에서 transactionScope.Complete ()를 생략하면? 이것에 대해 다른 질문을 제출해야한다고 생각하십니까?
Eakan Gopalakrishnan 2012 년

@EakanGopalakrishnan이 메소드를 호출하지 못하면 트랜잭션 관리자가 트랜잭션을 시스템 장애 또는 트랜잭션 범위 내에서 발생한 예외로 해석하므로 트랜잭션이 중단됩니다. (MSDN msdn.microsoft.com/en-us/library/… 에서 가져옴 )
Jones Jones

1
@JsonStatham 이 풀 요청 에 추가되었습니다. 이정표는 2.1.0입니다.
Doctor Jones

83

확장 방법으로 이것을 쉽게 만들 수 있습니다

public static List<T> ToListReadUncommitted<T>(this IQueryable<T> query)
{
    using (var scope = new TransactionScope(
        TransactionScopeOption.Required, 
        new TransactionOptions() { 
            IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
    {
        List<T> toReturn = query.ToList();
        scope.Complete();
        return toReturn;
    }
}

public static int CountReadUncommitted<T>(this IQueryable<T> query)
{
    using (var scope = new TransactionScope(
        TransactionScopeOption.Required, 
        new TransactionOptions() { 
            IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
    {
        int toReturn = query.Count();
        scope.Complete();
        return toReturn;
    }
}

내 프로젝트에서 이것을 사용하면 연결 풀이 완전히 활용되어 예외가 발생합니다. 이유를 알 수 없습니다. 이 문제가 다른 사람이 있습니까? 어떤 제안?
벤 Tidman

1
문제 없음 Ben, 항상 연결 컨텍스트를 폐기하는 것을 잊지 마십시오.
Alexandre

가능한 원인으로 트랜잭션 범위를 제외하기 위해 문제를 좁힐 수있었습니다. 감사. 내 생성자에 있던 연결 재시 도와 관련이 있습니다.
벤 Tidman

범위는 TransactionScopeOption.Suppress
CodeGrue

@Alexandre 다른 ReadCommitted Transaction 내에서이 작업을 수행하면 어떻게됩니까? 예를 들어 데이터 저장을 시작하기 위해 트랜잭션을 생성했지만 더 많은 데이터를 쿼리하여 ReadUncommitted Transaction을 생성합니까? 이 "완료"라고하면 외부 거래도 완료됩니까? 친절하게 조언 :)
Jason Loki Smith

27

큰 무언가가 필요한 경우 실제로 매번 transactionscope를 시작하는 것보다 덜 방해가되는 가장 좋은 방법은 다음 간단한 명령을 실행하여 객체 컨텍스트를 만든 후 연결에서 기본 트랜잭션 격리 수준을 설정하는 것입니다.

this.context.ExecuteStoreCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");

http://msdn.microsoft.com/en-us/library/aa259216(v=sql.80).aspx

이 기술을 사용하여 컨텍스트를 작성하고 모든 컨텍스트에 대해 매번이 명령을 실행하여 기본적으로 항상 "커밋되지 않은 읽기"상태가되도록하는 간단한 EF 제공자를 작성할 수있었습니다.


2
트랜잭션 격리 수준 만 설정해도 아무런 영향을 미치지 않습니다. 실제로 트랜잭션 내에서 실행해야 효과가 있습니다. 커밋되지 않은 상태 읽기에 대한 MSDN 설명서 Transactions running at the READ UNCOMMITTED level do not issue shared locks. 즉, 혜택을 받으려면 트랜잭션 내에서 실행 중이어야합니다. ( msdn.microsoft.com/en-gb/library/ms173763.aspx 에서 가져옴 ). 접근 방식이 덜 어려울 수 있지만 트랜잭션을 사용하지 않으면 아무것도 달성되지 않습니다.
닥터 존스

3
MSDN 설명서에 "SQL Server에 연결하여 실행되는 Transact-SQL 문의 잠금 및 행 버전 관리 동작을 제어합니다." "문이 다른 트랜잭션에 의해 수정되었지만 아직 커밋되지 않은 행을 읽을 수 있도록 지정합니다." 내가 작성한이 문장은 모든 SQL 문에 영향을 미치며, 트랜잭션 내부에 있습니다. 나는 사람들을 온라인으로 모순하는 것을 좋아하지 않지만 대규모 프로덕션 환경 에서이 진술을 사용함에 따라 분명히 그 사람에 대해 틀렸다. 추측하지 말고 시도하십시오!
Frank.Germain

나는 그것들을 시험해 보았습니다. 우리는 이러한 트랜잭션 범위 중 하나에서 쿼리를 수행하지 않으면 일치하는 트랜잭션으로 인해 교착 상태가 발생하는로드 환경이 높습니다. 내 관찰은 SQL 2005 서버에서 수행되었으므로 이후 동작이 변경되었는지 알 수 없습니다. 그러므로 나는 이것을 추천한다; 커밋되지 않은 읽기 격리 수준을 지정했지만 교착 상태가 계속 발생하면 트랜잭션 내에 쿼리를 넣어보십시오. 거래를 만들지 않고 교착 상태가 발생하지 않으면 충분히 공정합니다.
닥터 존스

3
@DoctorJones-Microsoft SQL Server와 관련하여 모든 쿼리는 본질적으로 트랜잭션입니다. 명시 적 트랜잭션을 지정하는 것은 둘 이상의 명령문을 동일한 트랜잭션 으로 그룹화 하여 원자 단위 작업으로 간주 될 수 있는 수단입니다 . 이 SET TRANSACTION ISOLATION LEVEL...명령은 연결 레벨 특성에 영향을 미치므로 조회 힌트로 대체되지 않는 한 해당 시점부터 작성된 모든 SQL 문 (THAT 연결의 경우)에 영향을줍니다. 이 동작은 적어도 SQL Server 2000 이후로 발생했으며 이전에는 가능했을 것입니다.
솔로몬 루츠 키

5
@DoctorJones은 - 체크 아웃 : msdn.microsoft.com/en-us/library/ms173763.aspx를 . 다음은 테스트입니다. SSMS에서 쿼리 (# 1)를 열고 다음을 실행하십시오 CREATE TABLE ##Test(Col1 INT); BEGIN TRAN; SELECT * FROM ##Test WITH (TABLOCK, XLOCK);. 다른 쿼리 (# 2)를 열고 다음을 실행하십시오 SELECT * FROM ##Test;. 단독 잠금을 사용하는 탭 # 1의 여전히 열린 트랜잭션에 의해 차단되고 있으므로 SELECT가 반환되지 않습니다. # 2에서 SELECT를 취소하십시오. SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED탭 # 2에서 한 번 실행하십시오 . 탭 # 2에서 SELECT 만 다시 실행하면 다시 나타납니다. ROLLBACK탭 # 1에서 실행해야합니다 .
솔로몬 루츠

21

커밋되지 않은 읽기 트랜잭션 격리 수준을 사용하는 것이 최선의 선택이라는 데 전적으로 동의했지만 언젠가는 관리자 또는 클라이언트의 요청에 따라 NOLOCK 힌트를 사용해야했으며 이에 대한 이유도 없었습니다.

Entity Framework 6을 사용하면 다음과 같이 자체 DbCommandInterceptor를 구현할 수 있습니다.

public class NoLockInterceptor : DbCommandInterceptor
{
    private static readonly Regex _tableAliasRegex = 
        new Regex(@"(?<tableAlias>AS \[Extent\d+\](?! WITH \(NOLOCK\)))", 
            RegexOptions.Multiline | RegexOptions.IgnoreCase);

    [ThreadStatic]
    public static bool SuppressNoLock;

    public override void ScalarExecuting(DbCommand command, 
        DbCommandInterceptionContext<object> interceptionContext)
    {
        if (!SuppressNoLock)
        {
            command.CommandText = 
                _tableAliasRegex.Replace(command.CommandText, "${tableAlias} WITH (NOLOCK)");
        }
    }

    public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        if (!SuppressNoLock)
        {
            command.CommandText = 
                _tableAliasRegex.Replace(command.CommandText, "${tableAlias} WITH (NOLOCK)");
        }
    }
}

이 클래스를 사용하면 응용 프로그램 시작시 적용 할 수 있습니다.

DbInterception.Add(new NoLockInterceptor());

그리고 NOLOCK현재 스레드에 대한 쿼리 에 힌트 추가를 조건부로 해제하십시오 .

NoLockInterceptor.SuppressNoLock = true;

정규식을 약간 변경했지만이 솔루션이 마음에 듭니다.
Russ

2
(? <tableAlias>] AS [Extent \ d +] (?! WITH (NOLOCK))) 파생 테이블에 nolock을 추가하여 오류를 발생시키는 것을 방지합니다. :)
Russ

스레드 수준에서 SuppressNoLock을 설정하는 것이 편리한 방법이지만 부울 설정을 해제하는 것을 잊어 버리기 쉽습니다. IDisposable을 반환하는 함수를 사용해야합니다. Dispose 메서드는 부울을 다시 false로 다시 설정할 수 있습니다. 또한 ThreadStatic은 async / await와 실제로 호환되지 않습니다 : stackoverflow.com/questions/13010563/…
Jaap

또는 ISOLATION LEVEL을 사용하려는 경우 : public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) { if (!SuppressNoLock) command.CommandText = $"SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;{Environment.NewLine}{command.CommandText}"; base.ReaderExecuting(command, interceptionContext); }
Adi

데이터베이스 기능에도 nolock을 추가하고 있습니다. 기능을 피하는 방법?
Ivan Lewis

9

닥터 존스 의 대답을 향상 시키고 PostSharp 사용 ;

첫 번째 " ReadUncommitedTransactionScopeAttribute "

[Serializable]
public class ReadUncommitedTransactionScopeAttribute : MethodInterceptionAspect
{
    public override void OnInvoke(MethodInterceptionArgs args)
    {
        //declare the transaction options
        var transactionOptions = new TransactionOptions();
        //set it to read uncommited
        transactionOptions.IsolationLevel = IsolationLevel.ReadUncommitted;
        //create the transaction scope, passing our options in
        using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
        {
            //declare our context
            using (var scope = new TransactionScope())
            {
                args.Proceed();
                scope.Complete();
            }
        }
    }
}

그런 다음 필요할 때마다

    [ReadUncommitedTransactionScope()]
    public static SomeEntities[] GetSomeEntities()
    {
        using (var context = new MyEntityConnection())
        {
            //any reads we do here will also read uncomitted data
            //...
            //...

        }
    }

인터셉터로 "NOLOCK"을 추가 할 수 있다는 것도 좋지만 Oracle과 같은 다른 데이터베이스 시스템에 연결할 때는 작동하지 않습니다.


6

이 문제를 해결하기 위해 데이터베이스에서 뷰를 만들고 뷰 쿼리에 NOLOCK을 적용합니다. 그런 다음 뷰를 EF 내의 테이블로 취급합니다.


4

EF6이 도입되면 BeginTransaction () 메서드를 사용하는 것이 좋습니다.

EF6 + 및 EF Core에서 TransactionScope 대신 BeginTransaction을 사용할 수 있습니다.

using (var ctx = new ContractDbContext())
using (var transaction = ctx.Database.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted))
{
    //any reads we do here will also read uncommitted data
}

2

아닙니다. Entity Framework는 기본적으로 실제 데이터베이스보다 상당히 엄격한 계층입니다. 쿼리는 먼저 엔터티 모델을 대상으로하는 ESQL (Entity SQL)로 구성되며 EF는 여러 데이터베이스 백엔드를 지원하므로 실제로 "네이티브"SQL을 백엔드로 직접 보낼 수는 없습니다.

NOLOCK 쿼리 힌트는 SQL Server 고유의 것으로 다른 지원되는 데이터베이스에서는 작동하지 않습니다 (동일한 힌트를 구현하지 않은 한 강력하게 의심합니다).

마크


이 답변은 구식이다 - 다른 언급대로 NOLOCK을 사용할 수 있으며 사용 "네이티브"SQL을 실행할 수 있습니다 Database.ExecuteSqlCommand()또는 DbSet<T>.SqlQuery().
Dunc

1
@Dunc 다음 downvote 주셔서 감사합니다 - BTW : 당신이해야 하지 사용 (NOLOCK)어쨌든 - 볼 킥에 나쁜 습관 - 모든 지역 NOLOCK 퍼팅 -이되는 권장되지 사방이 사용하는 - 매우 반대!
marc_s

0

한 가지 옵션은 Ryan에서 제안한보기 솔루션과 유사한 저장 프로 시저를 사용한 다음 EF에서 저장 프로 시저를 실행하는 것입니다. 이렇게하면 저장 프로 시저가 더티 읽기를 수행하는 반면 EF는 결과를 파이프합니다.

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