Quantcast
Channel: CodeSection,代码区,SQL Server(mssql)数据库 技术分享 - CodeSec
Viewing all articles
Browse latest Browse all 3160

Using SQL Server Query Hints with Entity Framework

$
0
0

Query Hints should be used carefully, because they restrict the possible optimizations during the query compilation, and they may, later, force the optimizer to use poor strategies when the data changes in size or distribution: There are, however, occasions when they are required. This is a problem for anyone using Entity Framework because there isn’t a built-in way to specify query hints. In fact, Entity Framework is agnostic about the database server, because it uses providers that translate the generic queries to the native database language, so it’s not easy to include a feature such as query hints that is specific to SQL Server. However, the query-translation process is extensible. We can intercept the query-translation using a DbCommandInterceptor and thereby change the query text to include the query hint.

In this article, I will give a step-by-step account of how to build a command interceptor that will allow you to use any query hint with SQL Server. You can analyze the entire walkthrough or skip the first part and check how to use this library in the example later in the article.

You can download the source code of this solution on github using this link: https://github.com/DennesTorres/HintsInterceptor

Why are hints important?

Occasionally, query hints become very important, such as when you need to lock records in a query. Unless we find a way to use hints with Entity Framework , we are forced to use stored procedures with Entity Framework , which will increase the dependency on synchronised changes in the database when refectoring data objects in the application.

Query hints are needed in the following situations:

We are filtering the records by a field whose values are unevenly distributed in the table. Such type of queries will need the Option Recompile hint or they will suffer terrible performance problems. We need to block the access to a record we are reading until the business transaction finishes. Planning our Command Interceptor Am I forgetting something?

You may think I’m forgetting the most used query hint in many applications: NOLOCK . No, I’m not forgetting it but the developers should. Most developers think the only problem NOLOCK can cause is when a transaction is rolled back, causing dirty reads.

It’s not so simple. When using NOLOCK , the query will not respect even internal SQL Server operations, such as page splits. In a page split the records are moved from one page to another. If a query using NOLOCK is reading the pages, the result is unpredictable: it can read the same record twice or not read it at all.

This makes NOLOCK way more dangerous than most developers think and it should be avoided at all costs.

The solution for the contention problem, especially in reports, is the use of Read Commit Snapshot Isolation in the database, instead of using NOLOCK .

Once it has been configured in the Entity Framework context, a command interceptor is always executed, for every query. We can use this fact to develop the interceptor to apply a query hint to every query. We then just need a way to identify which hints we would like to apply to which queries.

A graceful solution would be to including the query hint in the LINQ expression, creating some extension method we could apply to our LINQ queries. However, the command interceptor doesn’t have access to the LINQ expression: it receives only the query text that will be sent to the database.

The solution will be to create a static collection of hints that we can fill before executing a query and clear just after the execution. It needs to be static to be accessible from anywhere in our code and it also needs to be thread safe, because two concurrent threads shouldn’t affect the hints of each other.

Our solution also needs to consider that there are two types of hints: Table hints, applied to a specific table and query hints, applied to the whole query. These hints are inserted in different parts of the query: The design of our solution needs to be suitably flexible for this.

The command interceptor will need to get the string representation of each hint in the collection, but the hints must to be separated according to whether they are query hints or table hints. We can create a class hierarchy that allows us to identify each hint type by their class type.

Finally, we can’t forget that some hints are incompatible. For example, we shouldn’t use UPDLOCK and SERIALIZABLE in the same query and table. The collection needs to check the compatibility between the hints and avoid having incompatible hints.

Environment for our solutions

We will need Visual Studio and SQL Server to build our solution, but any Visual Studio version from 2012 and any SQL Server version from 2008 will work.

We will also need the Northwnd sample database that you can download here: https://northwinddatabase.codeplex.com/

Creating the solution

Let’s do a walkthrough of the process of creating our new solution and project:

Creating the HintBase class

Each hint has custom behaviors such as the string representation and compatibility check, so each hint will need to be a custom class. However, we need a base class to specify the common behaviors for all the hints.

For now, let’s create an empty base class:

Creating the HintsCollection class

The collection of hints needs to check the compatibility of a new hint with the existing ones every time a new hint is included. Because of that, we need to build a custom collection, inheriting from Collection class, and intercept the methods to insert new items in the collection.

First, let’s create ‘HintsCollection’ class:

public class HintsCollection : Collection<HintBase> { }

Each time a new hint is added to the class, we need to check if this hint is compatible with the existing ones. The collection will start the compatibility check, but only the hint itself can tell whether it’s compatible or not.

The ‘HintBase’ class will need a method for this, let’s create it:

Add the method ‘CheckCompatibility’ in the ‘HintBase’ class using the following code: public abstract class HintBase { public abstract bool CheckCompatibility(HintsCollection hints); }

Now we can create a private method in ‘HintsCollection’ class to call the ‘CheckCompatibility’ :

Create the method ‘ValidateHint’ in the class ‘HintsCollection’ : public class HintsCollection : Collection<HintBase> { /// <summary> /// Method to validate a new hint /// against the existing ones /// </summary> /// <param name="newhint"></param> /// <returns></returns> private bool ValidateHint(HintBase newhint) { return newhint.CheckCompatibility(this); } }

We need to call the ‘ValidateHint’ method every time a new hint is included in the collection. Let’s override some Collection methods for this:

Create the method ‘

Viewing all articles
Browse latest Browse all 3160

Trending Articles