Comment puis-je utiliser la NOLOCK
fonction sur Entity Framework? XML est-il le seul moyen de le faire?
Comment puis-je utiliser la NOLOCK
fonction sur Entity Framework? XML est-il le seul moyen de le faire?
Réponses:
Non, mais vous pouvez démarrer une transaction et définir le niveau d'isolation sur lecture non validée . Cela fait essentiellement la même chose que NOLOCK, mais au lieu de le faire par table, il le fera pour tout ce qui est dans le cadre de la transaction.
Si cela ressemble à ce que vous voulez, voici comment procéder ...
//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();
}
Les méthodes d'extension peuvent rendre cela plus facile
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;
}
}
Si vous avez besoin de quelque chose en général, la meilleure façon dont nous avons trouvé qui est moins intrusif que de démarrer réellement une transactioncope à chaque fois, est de simplement définir le niveau d'isolation de transaction par défaut sur votre connexion après avoir créé votre contexte d'objet en exécutant cette simple commande:
this.context.ExecuteStoreCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
http://msdn.microsoft.com/en-us/library/aa259216(v=sql.80).aspx
Avec cette technique, nous avons pu créer un fournisseur EF simple qui crée le contexte pour nous et exécute réellement cette commande à chaque fois pour l'ensemble de notre contexte afin que nous soyons toujours en «lecture non validée» par défaut.
Transactions running at the READ UNCOMMITTED level do not issue shared locks
. Cela implique que vous devez exécuter une transaction pour en tirer profit. (extrait de msdn.microsoft.com/en-gb/library/ms173763.aspx ). Votre approche est peut-être moins intrusive, mais elle n'apportera rien si vous n'utilisez pas de transaction.
SET TRANSACTION ISOLATION LEVEL...
commande affecte une propriété au niveau de la connexion et par conséquent affecte toutes les instructions SQL effectuées à partir de ce point (pour CETTE connexion), sauf si elle est remplacée par une indication de requête. Ce comportement existe depuis au moins SQL Server 2000 et probablement avant.
CREATE TABLE ##Test(Col1 INT); BEGIN TRAN; SELECT * FROM ##Test WITH (TABLOCK, XLOCK);
. Ouvrez une autre requête (n ° 2) et exécutez: SELECT * FROM ##Test;
. Le SELECT ne reviendra pas car il est bloqué par la transaction toujours ouverte dans l'onglet n ° 1 qui utilise un verrou exclusif. Annulez le SELECT dans # 2. Exécutez SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
une fois dans l'onglet # 2. Exécutez juste le SELECT à nouveau dans l'onglet # 2 et il reviendra. Assurez-vous d'exécuter ROLLBACK
dans l'onglet # 1.
Bien que je sois tout à fait d'accord pour dire que l'utilisation du niveau d'isolation de transaction Read Uncommitted est le meilleur choix, mais vous avez parfois été obligé d'utiliser l'indication NOLOCK à la demande du gestionnaire ou du client et aucune raison ne l'a acceptée.
Avec Entity Framework 6, vous pouvez implémenter votre propre DbCommandInterceptor comme ceci:
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)");
}
}
}
Avec cette classe en place, vous pouvez l'appliquer au démarrage de l'application:
DbInterception.Add(new NoLockInterceptor());
Et désactivez conditionnellement l'ajout d' NOLOCK
indices dans les requêtes pour le thread actuel:
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); }
Amélioration de la réponse acceptée par le docteur Jones et utilisation de PostSharp ;
Premier " 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();
}
}
}
}
Puis chaque fois que vous en avez besoin,
[ReadUncommitedTransactionScope()]
public static SomeEntities[] GetSomeEntities()
{
using (var context = new MyEntityConnection())
{
//any reads we do here will also read uncomitted data
//...
//...
}
}
Être capable d'ajouter "NOLOCK" avec un intercepteur est également agréable mais ne fonctionnera pas lors de la connexion à d'autres systèmes de base de données comme Oracle en tant que tel.
Pour contourner cela, je crée une vue sur la base de données et j'applique NOLOCK sur la requête de la vue. Je traite ensuite la vue comme une table dans EF.
Avec l'introduction d'EF6, Microsoft recommande d'utiliser la méthode BeginTransaction ().
Vous pouvez utiliser BeginTransaction au lieu de TransactionScope dans EF6 + et 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
}
Non, pas vraiment - Entity Framework est fondamentalement une couche assez stricte au-dessus de votre base de données réelle. Vos requêtes sont formulées dans ESQL - Entity SQL - qui est tout d'abord ciblé vers votre modèle d'entité, et comme EF prend en charge plusieurs backends de base de données, vous ne pouvez pas vraiment envoyer du SQL "natif" directement à votre backend.
L'indicateur de requête NOLOCK est une chose spécifique à SQL Server et ne fonctionnera sur aucune des autres bases de données prises en charge (à moins qu'elles aient également implémenté le même indice - ce dont je doute fortement).
Marc
Database.ExecuteSqlCommand()
ou DbSet<T>.SqlQuery()
.
(NOLOCK)
toute façon - voir Bad Habits to kick - mettre NOLOCK partout - il n'est PAS RECOMMANDÉ de l'utiliser partout - bien au contraire!