Wie kann ich die NOLOCK
Funktion in Entity Framework verwenden? Ist XML der einzige Weg, dies zu tun?
Wie kann ich die NOLOCK
Funktion in Entity Framework verwenden? Ist XML der einzige Weg, dies zu tun?
Antworten:
Nein, aber Sie können eine Transaktion starten und die Isolationsstufe so einstellen , dass sie nicht festgeschrieben wird . Dies macht im Wesentlichen dasselbe wie NOLOCK, aber anstatt es pro Tabelle zu tun, wird es für alles im Rahmen der Transaktion getan.
Wenn das so klingt, wie Sie es möchten, können Sie es folgendermaßen tun ...
//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();
}
Erweiterungsmethoden können dies erleichtern
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;
}
}
Wenn Sie etwas im Allgemeinen benötigen, ist es am besten, die Standardtransaktionsisolationsstufe für Ihre Verbindung festzulegen, nachdem Sie Ihren Objektkontext erstellt haben, indem Sie diesen einfachen Befehl ausführen:
this.context.ExecuteStoreCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
http://msdn.microsoft.com/en-us/library/aa259216(v=sql.80).aspx
Mit dieser Technik konnten wir einen einfachen EF-Anbieter erstellen, der den Kontext für uns erstellt und diesen Befehl jedes Mal für den gesamten Kontext ausführt, sodass wir standardmäßig immer "nicht festgeschrieben" lesen.
Transactions running at the READ UNCOMMITTED level do not issue shared locks
. Dies bedeutet, dass Sie innerhalb einer Transaktion ausgeführt werden müssen, um den Vorteil zu erhalten. (entnommen aus msdn.microsoft.com/en-gb/library/ms173763.aspx ). Ihr Ansatz ist zwar weniger aufdringlich, bringt jedoch nichts, wenn Sie keine Transaktion verwenden.
SET TRANSACTION ISOLATION LEVEL...
Befehl wirkt sich auf eine Eigenschaft auf Verbindungsebene aus und wirkt sich daher auf alle SQL-Anweisungen aus, die ab diesem Zeitpunkt (für DIESE Verbindung) ausgeführt werden, sofern sie nicht durch einen Abfragehinweis überschrieben werden. Dieses Verhalten gibt es seit mindestens SQL Server 2000 und wahrscheinlich schon früher.
CREATE TABLE ##Test(Col1 INT); BEGIN TRAN; SELECT * FROM ##Test WITH (TABLOCK, XLOCK);
. Öffnen Sie eine andere Abfrage (Nr. 2) und führen Sie Folgendes aus : SELECT * FROM ##Test;
. Das SELECT wird nicht zurückgegeben, da es durch die noch geöffnete Transaktion in Registerkarte 1 blockiert wird, die eine exklusive Sperre verwendet. Brechen Sie die AUSWAHL in # 2 ab. Führen Sie SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
einmal in Tab. 2 aus. Führen Sie nur SELECT erneut in Tab. 2 aus, und es wird wieder angezeigt. Stellen Sie sicher, dass Sie ROLLBACK
in Registerkarte 1 ausgeführt werden.
Ich stimmte zwar absolut zu, dass die Verwendung der Isolationsstufe für nicht festgeschriebene Transaktionen die beste Wahl ist, aber einige Zeit haben Sie gezwungen, den NOLOCK-Hinweis auf Anfrage des Managers oder Kunden zu verwenden, und es wurden keine Gründe dafür akzeptiert.
Mit Entity Framework 6 können Sie Ihren eigenen DbCommandInterceptor wie folgt implementieren:
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)");
}
}
}
Mit dieser Klasse können Sie sie beim Start der Anwendung anwenden:
DbInterception.Add(new NoLockInterceptor());
Deaktivieren Sie unter bestimmten Bedingungen das Hinzufügen von NOLOCK
Hinweisen zu Abfragen für den aktuellen Thread:
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); }
Verbesserung der akzeptierten Antwort von Doktor Jones und Verwendung von PostSharp ;
Zuerst " 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();
}
}
}
}
Dann, wann immer Sie es brauchen,
[ReadUncommitedTransactionScope()]
public static SomeEntities[] GetSomeEntities()
{
using (var context = new MyEntityConnection())
{
//any reads we do here will also read uncomitted data
//...
//...
}
}
Die Möglichkeit, "NOLOCK" mit einem Interceptor hinzuzufügen, ist ebenfalls hilfreich, funktioniert jedoch nicht, wenn eine Verbindung zu anderen Datenbanksystemen wie Oracle als solches hergestellt wird.
Um dies zu umgehen, erstelle ich eine Ansicht in der Datenbank und wende NOLOCK auf die Abfrage der Ansicht an. Ich behandle die Ansicht dann als Tabelle in EF.
Mit der Einführung von EF6 empfiehlt Microsoft die Verwendung der BeginTransaction () -Methode.
Sie können BeginTransaction anstelle von TransactionScope in EF6 + und EF Core verwenden
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
}
Nein, nicht wirklich - Entity Framework ist im Grunde eine ziemlich strenge Schicht über Ihrer eigentlichen Datenbank. Ihre Abfragen werden in ESQL - Entity SQL - formuliert, das in erster Linie auf Ihr Entitätsmodell ausgerichtet ist. Da EF mehrere Datenbank-Backends unterstützt, können Sie "natives" SQL nicht direkt an Ihr Backend senden.
Der NOLOCK-Abfragehinweis ist eine SQL Server-spezifische Sache und funktioniert in keiner der anderen unterstützten Datenbanken (es sei denn, sie haben denselben Hinweis ebenfalls implementiert - was ich stark bezweifle).
Marc
Database.ExecuteSqlCommand()
oder ausführen DbSet<T>.SqlQuery()
.
(NOLOCK)
- siehe Bad Habits to kick - NOLOCK überall einsetzen - es wird NICHT EMPFOHLEN , dies überall zu verwenden - ganz im Gegenteil!
Eine Möglichkeit besteht darin, eine gespeicherte Prozedur zu verwenden (ähnlich der von Ryan vorgeschlagenen Ansichtslösung) und dann die gespeicherte Prozedur von EF auszuführen. Auf diese Weise führt die gespeicherte Prozedur den Dirty Read durch, während EF nur die Ergebnisse weiterleitet.