Jak mogę używać tej NOLOCK
funkcji w Entity Framework? Czy XML to jedyny sposób, aby to zrobić?
Jak mogę używać tej NOLOCK
funkcji w Entity Framework? Czy XML to jedyny sposób, aby to zrobić?
Odpowiedzi:
Nie, ale możesz rozpocząć transakcję i ustawić poziom odseparowania na odczyt niezatwierdzonych . Zasadniczo robi to to samo co NOLOCK, ale zamiast robić to na podstawie tabeli, zrobi to dla wszystkiego w zakresie transakcji.
Jeśli to brzmi tak, jak chcesz, oto jak możesz to zrobić ...
//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();
}
Metody rozszerzające mogą to ułatwić
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;
}
}
Jeśli potrzebujesz czegoś ogólnie, najlepszym sposobem, jaki znaleźliśmy, który jest mniej uciążliwy niż faktyczne rozpoczynanie zakresu transakcji za każdym razem, jest po prostu ustawienie domyślnego poziomu izolacji transakcji w połączeniu po utworzeniu kontekstu obiektu, uruchamiając to proste polecenie:
this.context.ExecuteStoreCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
http://msdn.microsoft.com/en-us/library/aa259216(v=sql.80).aspx
Dzięki tej technice byliśmy w stanie stworzyć prostego dostawcę EF, który tworzy dla nas kontekst i faktycznie uruchamia to polecenie za każdym razem dla całego naszego kontekstu, dzięki czemu zawsze jesteśmy w stanie „czytaj niezatwierdzone”.
Transactions running at the READ UNCOMMITTED level do not issue shared locks
. Oznacza to, że musisz działać w ramach transakcji, aby uzyskać korzyść. (pobrane z msdn.microsoft.com/en-gb/library/ms173763.aspx ). Twoje podejście może być mniej inwazyjne, ale nic nie da, jeśli nie wykorzystasz transakcji.
SET TRANSACTION ISOLATION LEVEL...
Komenda wpływa na właściwość Połączenie poziomie, a tym samym wpływa na wszystkie instrukcje SQL wykonane z tego punktu do przodu (do tego połączenia), chyba że zostaną zamienione przez nutą zapytania. To zachowanie występuje od co najmniej SQL Server 2000 i prawdopodobnie wcześniej.
CREATE TABLE ##Test(Col1 INT); BEGIN TRAN; SELECT * FROM ##Test WITH (TABLOCK, XLOCK);
. Otworzyć kolejne zapytanie (# 2) i wykonaj: SELECT * FROM ##Test;
. SELECT nie powróci, ponieważ jest blokowany przez wciąż otwartą transakcję na karcie 1, która używa blokady na wyłączność. Anuluj WYBÓR w # 2. Uruchom SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
raz w zakładce # 2. Uruchom ponownie SELECT w zakładce # 2 i wróci. Pamiętaj, aby uruchomić ROLLBACK
w zakładce nr 1.
Chociaż absolutnie zgodziłem się, że użycie poziomu izolacji transakcji Odczyt niezatwierdzonych jest najlepszym wyborem, ale od jakiegoś czasu zmuszony byłeś użyć wskazówki NOLOCK na prośbę menedżera lub klienta i nie zaakceptowano żadnych powodów przeciwko temu.
Za pomocą Entity Framework 6 można zaimplementować własne DbCommandInterceptor w następujący sposób:
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)");
}
}
}
Mając tę klasę, możesz ją zastosować na początku aplikacji:
DbInterception.Add(new NoLockInterceptor());
I warunkowo wyłącz dodawanie NOLOCK
podpowiedzi do zapytań dotyczących bieżącego wątku:
NoLockInterceptor.SuppressNoLock = true;
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); }
Ulepszanie zaakceptowanej odpowiedzi Doktora Jonesa i używanie PostSharp ;
Pierwszy „ 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();
}
}
}
}
Wtedy kiedy będziesz tego potrzebować,
[ReadUncommitedTransactionScope()]
public static SomeEntities[] GetSomeEntities()
{
using (var context = new MyEntityConnection())
{
//any reads we do here will also read uncomitted data
//...
//...
}
}
Możliwość dodania „NOLOCK” z przechwytywaczem jest również przyjemna, ale nie będzie działać podczas łączenia się z innymi systemami baz danych, takimi jak Oracle.
Aby to obejść, tworzę widok w bazie danych i stosuję NOLOCK w zapytaniu widoku. Następnie traktuję widok jako tabelę w EF.
Wraz z wprowadzeniem EF6 firma Microsoft zaleca używanie metody BeginTransaction ().
Możesz użyć BeginTransaction zamiast TransactionScope w EF6 + i EF Core
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
}
Nie, nie do końca - Entity Framework jest w zasadzie dość ścisłą warstwą nad rzeczywistą bazą danych. Twoje zapytania są formułowane w ESQL - Entity SQL - który jest przede wszystkim ukierunkowany na Twój model encji, a ponieważ EF obsługuje wiele backendów baz danych, nie możesz tak naprawdę wysyłać „natywnego” SQL bezpośrednio do swojego zaplecza.
Wskazówka dotycząca zapytania NOLOCK jest specyficzna dla SQL Server i nie będzie działać na żadnej z innych obsługiwanych baz danych (chyba że zaimplementowały one również tę samą wskazówkę - w co mocno wątpię).
Marc
Database.ExecuteSqlCommand()
lub DbSet<T>.SqlQuery()
.
(NOLOCK)
- zobacz Złe nawyki do kopania - umieszczanie NOLOCK wszędzie - NIE ZALECA SIĘ używać tego wszędzie - wręcz przeciwnie!