Entity Framework 6에서 아래 SELECT 문에 NOLOCK 사용 안 함
MVC 5 프로젝트에서 엔티티 프레임워크 6을 사용하고 있습니다. 아다시피시,아피시.SELECT의 쿼리는 SQL Server의 쿼리를 사용할 더 됩니다.WITH (NOLOCK) 문 몇 NOLOCK.Entity Framework 6에 포함된 SQL SELECT 문이 하나도 없습니다.
가져오기 작업에서 트랜잭션을 사용하여 커밋되지 않은 트랜잭션을 읽지 않습니다.
어떻게 하면 EF 6이 아래에 생성된 SELECT 문에서 NOLOCK을 사용하도록 강제할 수 있습니까?
무엇보다도...모든 SQL 문에 대해 절대 NOLOCK을 사용해서는 안 됩니다.데이터의 무결성이 손상될 수 있습니다.
이는 다른 쿼리 힌트와 마찬가지로 일반적이지 않은 작업을 수행할 때만 사용해야 하는 메커니즘입니다.
EF 공급자에게 NoLock 힌트를 렌더링하도록 지시할 방법이 없습니다.커밋되지 않은 데이터를 정말로 읽어야 하는 경우 다음 옵션이 있습니다.
자체 엔티티 프레임워크 공급자를 작성합니다.
명령 인터셉트카를 사용하여 명령문을 실행하기 전에 수정합니다.http://msdn.microsoft.com/en-us/data/dn469464.aspx
트랜잭션 범위와 분리 사용Level.ReadUncommitted.
트랜잭션을 사용하고 싶지 않다고 말씀하셨지만 커밋되지 않은 데이터를 바로 읽을 수 있는 유일한 방법입니다.또한 SQL Server의 각 문이 "암묵적으로" 트랜잭션에서 실행되므로 오버헤드가 많이 발생하지 않습니다.
using (new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions
{
IsolationLevel = IsolationLevel.ReadUncommitted
}))
{
using (var db = new MyDbContext()) {
// query
}
}
편집: 업데이트 및 삭제에 대한 NOLOCK(선택 사항은 그대로 유지됨)는 SQL Server 2016 이후 Microsoft에서 더 이상 사용되지 않으며 'a' 이후 릴리스에서 제거될 예정입니다.
각 쿼리에 대해 트랜잭션 범위를 사용하지 않는 해결 방법을 사용할 수 있습니다.아래 코드를 실행하면 ef는 동일한 서버 프로세스 ID에 대해 동일한 트랜잭션 격리 수준을 사용합니다.서버 프로세스 ID는 동일한 요청에서 변경되지 않으므로 각 요청에 대해 하나의 호출만 수행하면 됩니다.이는 EF Core에서도 작동합니다.
this.Database.ExecuteSqlCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
저는 Codeworx가 Read Uncommitted가 위험할 수 있다고 말하는 것에 동의합니다.더러운 책을 읽으며 살 수 있다면, 해보세요.
저는 현재 쿼리에서 아무것도 변경하지 않고 이 작업을 수행할 수 있는 방법을 찾았습니다.
DbCommand를 생성해야 합니다.다음과 같은 가로채기:
public class IsolationLevelInterceptor : DbCommandInterceptor
{
private IsolationLevel _isolationLevel;
public IsolationLevelInterceptor(IsolationLevel level)
{
_isolationLevel = level;
}
//[ThreadStatic]
//private DbCommand _command;
public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
SetTransaction(command);
}
public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
SetTransaction(command);
}
public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
SetTransaction(command);
}
private void SetTransaction(DbCommand command)
{
if (command != null)
{
if (command.Transaction == null)
{
var t = command.Connection.BeginTransaction(_isolationLevel);
command.Transaction = t;
//_command = command;
}
}
}
}
그런 다음 cctor(데이터베이스 컨텍스트의 정적 생성자)에서 엔티티 프레임워크 컬렉션의 Db Infrastructure에 인터셉터를 추가하면 됩니다.
DbInterception.Add(new IsolationLevelInterceptor(System.Data.IsolationLevel.ReadUncommitted));
EF가 저장소로 보내는 각 명령에 대해 이 의지를 사용하여 해당 분리 수준의 트랜잭션을 덮어씁니다.
저의 경우는 데이터베이스의 판독값을 기반으로 하지 않는 API를 통해 데이터를 작성하기 때문에 잘 작동했습니다.(더러운 읽기로 인해 데이터가 손상될 수 있음) 따라서 정상적으로 작동했습니다.
우리의 프로젝트에서는 @Cem Mutlu와 @anotherNeo가 제안한 두 번째와 세 번째 솔루션의 조합을 사용합니다.
Sql Profiler를 사용한 실험에서 다음과 같은 명령 쌍을 사용해야 한다는 것을 알 수 있었습니다.
- 읽기 커밋되지 않음
- 읽기 커밋됨
SqlConnectionPool을 통한 NET 연결 재사용
internal class NoLockInterceptor : DbCommandInterceptor
{
public static readonly string SET_READ_UNCOMMITED = "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";
public static readonly string SET_READ_COMMITED = "SET TRANSACTION ISOLATION LEVEL READ COMMITTED";
public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
if (!interceptionContext.DbContexts.OfType<IFortisDataStoreNoLockContext>().Any())
{
return;
}
ExecutingBase(command);
}
public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
if (!interceptionContext.DbContexts.OfType<IFortisDataStoreNoLockContext>().Any())
{
return;
}
ExecutingBase(command);
}
private static void ExecutingBase(DbCommand command)
{
var text = command.CommandText;
command.CommandText = $"{SET_READ_UNCOMMITED} {Environment.NewLine} {text} {Environment.NewLine} {SET_READ_COMMITED}";
}
}
먼저 gds03의 답변을 올려주세요.그것이 없었다면 저는 여기까지 오지 못했을 것이기 때문입니다.
"거래를 종료"하고 IDataReader/DbDataReader 상황에 맞는 타이밍을 잡는 데도 기여했습니다.기본적으로 IDataReader/DbDataReader 상황에서는 "ReaderExecuted"(Async) 메서드(실행됨의 "ed" 강조)에 대한 트랜잭션을 닫지 않고 "fall through"에서 DataReaderDisposition(데이터 판독기 폐기)로 "override"되도록 합니다.
하지만 다른 답변(여기)(및 의견)을 읽어보면 SetEndTransaction은 ...의 중요한 부분이라고 생각합니다.연결 풀에서 부두를 가져오지 않습니다(만약 당신이 (나를 위해) 읽기 미사용 트랜잭션을 닫지 않는다면).
using System.Data;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Diagnostics;
namespace My.Interceptors
{
public class IsolationLevelInterceptor : DbCommandInterceptor
{
private IsolationLevel _isolationLevel;
public IsolationLevelInterceptor(IsolationLevel level)
{
_isolationLevel = level;
}
//[ThreadStatic]
//private DbCommand _command;
public override InterceptionResult DataReaderDisposing(DbCommand command, DataReaderDisposingEventData eventData, InterceptionResult result)
{
InterceptionResult returnItem = base.DataReaderDisposing(command, eventData, result);
SetEndTransaction(command);
return returnItem;
}
public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
{
SetStartTransaction(command);
InterceptionResult<DbDataReader> returnItem = base.ReaderExecuting(command, eventData, result);
return returnItem;
}
public override DbDataReader ReaderExecuted(DbCommand command, CommandExecutedEventData eventData, DbDataReader result)
{
DbDataReader returnItem = base.ReaderExecuted(command, eventData, result);
//SetEndTransaction(command); // DO NOT DO THIS HERE .. datareader still open and working .. fall back on DataReaderDisposing... you don't really need this override, but left in to show the possible issue.
return returnItem;
}
public override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result, CancellationToken cancellationToken = default)
{
SetStartTransaction(command);
ValueTask<InterceptionResult<DbDataReader>> returnItem = base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
return returnItem;
}
public override ValueTask<DbDataReader> ReaderExecutedAsync(DbCommand command, CommandExecutedEventData eventData, DbDataReader result, CancellationToken cancellationToken = default)
{
ValueTask<DbDataReader> returnItem = base.ReaderExecutedAsync(command, eventData, result, cancellationToken);
//SetEndTransaction(command); // DO NOT DO THIS HERE .. datareader still open and working .. fall back on DataReaderDisposing... you don't really need this override, but left in to show the possible issue.
return returnItem;
}
public override InterceptionResult<object> ScalarExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<object> interceptionContext)
{
SetStartTransaction(command);
InterceptionResult<object> returnItem = base.ScalarExecuting(command, eventData, interceptionContext);
return returnItem;
}
public override object ScalarExecuted(DbCommand command, CommandExecutedEventData eventData, object result)
{
SetEndTransaction(command);
object returnItem = base.ScalarExecuted(command, eventData, result);
return returnItem;
}
public override ValueTask<InterceptionResult<object>> ScalarExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<object> result, CancellationToken cancellationToken = default)
{
SetStartTransaction(command);
ValueTask<InterceptionResult<object>> returnItem = base.ScalarExecutingAsync(command, eventData, result, cancellationToken);
return returnItem;
}
public override ValueTask<object> ScalarExecutedAsync(DbCommand command, CommandExecutedEventData eventData, object result, CancellationToken cancellationToken = default)
{
SetEndTransaction(command);
ValueTask<object> returnItem = base.ScalarExecutedAsync(command, eventData, result, cancellationToken);
return returnItem;
}
/* start maybe not needed on queries that only do "reading", but listed here anyways */
public override InterceptionResult<int> NonQueryExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<int> result)
{
SetStartTransaction(command);
InterceptionResult<int> returnItem = base.NonQueryExecuting(command, eventData, result);
return returnItem;
}
public override int NonQueryExecuted(DbCommand command, CommandExecutedEventData eventData, int result)
{
int returnValue = base.NonQueryExecuted(command, eventData, result);
SetEndTransaction(command);
return returnValue;
}
public override ValueTask<InterceptionResult<int>> NonQueryExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
{
SetStartTransaction(command);
ValueTask<InterceptionResult<int>> returnItem = base.NonQueryExecutingAsync(command, eventData, result, cancellationToken);
return returnItem;
}
public override ValueTask<int> NonQueryExecutedAsync(DbCommand command, CommandExecutedEventData eventData, int result, CancellationToken cancellationToken = default)
{
ValueTask<int> returnValue = base.NonQueryExecutedAsync(command, eventData, result, cancellationToken);
SetEndTransaction(command);
return returnValue;
}
/* end maybe not needed on queries that only do "reading", but listed here anyways */
private void SetStartTransaction(DbCommand command)
{
if (command != null)
{
if (command.Transaction == null)
{
DbTransaction t = command.Connection.BeginTransaction(_isolationLevel);
command.Transaction = t;
//_command = command;
}
}
}
private void SetEndTransaction(DbCommand command)
{
if (command != null)
{
if (command.Transaction != null)
{
command.Transaction.Commit();
//_command = command;
}
command.Dispose();
}
}
}
}
이 기사는 모든 "ing" 및 "ed" 방법을 "보기"하는 데 도움이 되었습니다.
https://lizzy-gallagher.github.io/query-interception-entity-framework/
제 대답은 EntityFrameworkCore(3.1.+)이지만, EF-for-DN-Framework에 "백포트"가 될 것 같습니다.
제 답변에서 더 중요한 부분은 특정 메서드, 특히 IDataReader/DbDataReader 메서드의 "타이밍"입니다.
언급URL : https://stackoverflow.com/questions/24684914/get-entity-framework-6-use-nolock-in-its-underneath-select-statements
'programing' 카테고리의 다른 글
| '{}' 유형을 '기록' 유형에 할당할 수 없습니다. (0) | 2023.07.07 |
|---|---|
| ggplot gem_text 글꼴 크기 컨트롤 (0) | 2023.07.07 |
| "델타를 해결하고 있다"고 표시될 때 깃은 무엇을 하고 있습니까? (0) | 2023.07.02 |
| 오류 - ORA-22835: 버퍼가 너무 작아서 CLOB에서 CHAR 또는 BLOB에서 RAW로 변환할 수 없습니다. (0) | 2023.07.02 |
| Oracle에 CASE 문이 있는 조건부 WHERE 절 (0) | 2023.07.02 |