Comment utiliser la fonction NOLOCK
sur Entity Framework? XML est-il le seul moyen de le faire?
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 relève de la transaction.
Si cela ressemble à ce que vous voulez, voici comment vous pouvez le faire ...
//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 de trouver le moins intrusif que de démarrer un transactioncope à chaque fois est de définir simplement le niveau d'isolation de transaction par défaut sur votre connexion après avoir créé votre contexte d'objet en exécutant cette commande simple:
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 cette commande à chaque fois pour l'ensemble de notre contexte, de sorte que nous sommes toujours en "lecture non validée" par défaut.
Bien que je sois tout à fait d’accord pour dire que l’utilisation du niveau d’isolation de transaction Lecture non validée est le meilleur choix, vous avez parfois été forcé d’utiliser NOLOCK hint à la demande du responsable ou du client, sans qu'aucune raison ne soit 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 éventuellement l'ajout de l'indicateur NOLOCK
dans les requêtes pour le fil actuel:
NoLockInterceptor.SuppressNoLock = true;
Enrichissement sur la réponse acceptée de Doctor 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
//...
//...
}
}
Pouvoir 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 tels qu'Oracle en tant que tels.
Pour résoudre ce problème, je crée une vue sur la base de données et applique NOLOCK à la requête de la vue. Je traite ensuite la vue comme une table dans EF.
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 s’adresse tout d’abord à votre modèle d’entité. Etant donné que EF prend en charge plusieurs bases de données, vous ne pouvez pas réellement envoyer de code SQL "natif" directement à votre système.
L'indicateur de requête NOLOCK est spécifique à SQL Server et ne fonctionnera pas avec les autres bases de données prises en charge (à moins qu'elles aient également implémenté le même indicateur, ce dont je doute fort).
Marc
Avec l'introduction de 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
}
Une option consiste à utiliser une procédure stockée (similaire à la solution d'affichage proposée par Ryan), puis à exécuter la procédure stockée à partir d'EF. De cette façon, la procédure stockée effectue la lecture modifiée alors que EF ne fait que diriger les résultats.