Introduction
LINQ was added to .NET as a new powerful data manipulation language. LINQ to SQL allows you to talk with DBMS quite conveniently by using Entity Framework, for example. But often when using it developers forget to consider what kind of SQL query will be generated by the queryable provider (Entity Framework in our example case). In this article, we’ll look at how exactly you can optimize the performance of LINQ queries.
Implementation
Let’s examine the two most important points by using an example.
First, we’ll need to create the Test database in SQL Server. In this database, we’ll create two tables by running the following query.s
USE [TEST]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Ref](
[ID] [int] NOT NULL,
[ID2] [int] NOT NULL,
[Name] [nvarchar](255) NOT NULL,
[InsertUTCDate] [datetime] NOT NULL,
CONSTRAINT [PK_Ref] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Ref] ADD CONSTRAINT [DF_Ref_InsertUTCDate] DEFAULT (getutcdate()) FOR [InsertUTCDate]
GO
USE [TEST]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Customer](
[ID] [int] NOT NULL,
[Name] [nvarchar](255) NOT NULL,
[Ref_ID] [int] NOT NULL,
[InsertUTCDate] [datetime] NOT NULL,
[Ref_ID2] [int] NOT NULL,
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Customer] ADD CONSTRAINT [DF_Customer_Ref_ID] DEFAULT ((0)) FOR [Ref_ID]
GO
ALTER TABLE [dbo].[Customer] ADD CONSTRAINT [DF_Customer_InsertUTCDate] DEFAULT (getutcdate()) FOR [InsertUTCDate]
GO
Now, let’s fill the Ref table with the help of the following script. Wait... I just ran a script, but didn’t save it. In such cases, SQL Complete by Devart can be very convenient. It integrates with SSMS and Visual Studio and has the Execution History feature,

Fig 1. Execution History feature
This functionality displays the history of queries that were executed in SSMS,

Fig.2. History of queries
Take note of how the window consists of the following elements.
- Search box for filtering the results
- Date range box for filtering the results
- Results are presented in a table. You can sort data by this table’s columns (by using the SHIFT key, you can choose a set of columns for sorting)
- Code of the selected row
The result table contains a history of scripts executed in SSMS and comprises the following tables.
- Status: Shows whether the script was executed successfully
- Query Text: code of the script
- Size (Bytes): Size of the script in bytes
- Executed On: Date and time at which the script was executed
- Duration: How long it took for the script to be executed
- File: Name of a file or a tab in SSMS followed by the name of the SQL Server instance on which the script was executed
- Server: Name of the SQL Server instance on which the script was executed
- User: The login under which the script was executed
- Database: The database context in which the script was executed
We can find the required query in this history table.
USE [TEST]
GO
DECLARE @ind INT = 1;
WHILE (@ind < 1200000)
BEGIN
INSERT INTO [dbo].[Ref]
([ID]
,[ID2]
,[Name])
SELECT @ind,
@ind,
CAST(@ind AS NVARCHAR(255));
SET @ind = @ind + 1;
END
GO
In a similar way, we can populate the Customer table by using the following script
USE [TEST]
GO
DECLARE @ind INT = 1;
DECLARE @ind_ref INT = 1;
WHILE (@ind <= 12000000)
BEGIN
IF (@ind % 3 = 0)
SET @ind_ref = 1;
ELSE IF (@ind % 5 = 0)
SET @ind_ref = 2;
ELSE IF (@ind % 7 = 0)
SET @ind_ref = 3;
ELSE IF (@ind % 11 = 0)
SET @ind_ref = 4;
ELSE IF (@ind % 13 = 0)
SET @ind_ref = 5;
ELSE IF (@ind % 17 = 0)
SET @ind_ref = 6;
ELSE IF (@ind % 19 = 0)
SET @ind_ref = 7;
ELSE IF (@ind % 23 = 0)
SET @ind_ref = 8;
ELSE IF (@ind % 29 = 0)
SET @ind_ref = 9;
ELSE IF (@ind % 31 = 0)
SET @ind_ref = 10;
ELSE IF (@ind % 37 = 0)
SET @ind_ref = 11;
ELSE
SET @ind_ref = @ind % 1190000;
INSERT INTO [dbo].[Customer]
([ID]
,[Name]
,[Ref_ID]
,[Ref_ID2])
SELECT @ind,
CAST(@ind AS NVARCHAR(255)),
@ind_ref,
@ind_ref;
SET @ind = @ind + 1;
END
GO
The SQL Complete tool can help with keeping the code of your scripts neatly formatted.
In this way, we created two tables – one of them has over 1 million rows, and the other has more than 10 million.
Now we need to create a test project in Visual Studio. This one will be a Visual C# Console App (.NET Framework):
Next, we’ll need to add a library for the Entity Framework so that we can interact with the database.
To add this library, right-click the project and choose ‘Manage NuGet Packages...’ in the context menu,

Fig.3. ‘Manage NuGet Packages...’ in the context menu
In the window that will be opened, let’s enter ‘Entity Framework’ in the search box, choose the Entity Framework package, and install it:

Fig.4. Entity Framework package installation
Next, in the App.config file, we’ll add the following block after the configSections element.
<connectionStrings>
<add name="DBConnection" connectionString="data source=MSSQL_INSTANCE_NAME;Initial Catalog=TEST;Integrated Security=True;" providerName="System.Data.SqlClient" />
</connectionStrings>
Make sure the connection string is entered in connectionString.
Now, let’s create 3 interfaces in separate files,
IBaseEntityID
namespace TestLINQ
{
public interface IBaseEntityID
{
int ID { get; set; }
}
}
IBaseEntityName
namespace TestLINQ
{
public interface IBaseEntityName
{
string Name { get; set; }
}
}
IBaseNameInsertUTCDate
namespace TestLINQ
{
public interface IBaseNameInsertUTCDate
{
DateTime InsertUTCDate { get; set; }
}
}
In a separate file, create a basic class BaseEntity for our two entities, which will contain their common fields.
namespace TestLINQ
{
public class BaseEntity : IBaseEntityID, IBaseEntityName, IBaseNameInsertUTCDate
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime InsertUTCDate { get; set; }
}
}
Next, we’ll create our two entities, each one in a separate file,
Ref
using System.ComponentModel.DataAnnotations.Schema;
namespace TestLINQ
{
[Table("Ref")]
public class Ref : BaseEntity
{
public int ID2 { get; set; }
}
}
Customer
using System.ComponentModel.DataAnnotations.Schema;
namespace TestLINQ
{
[Table("Customer")]
public class Customer : BaseEntity
{
public int Ref_ID { get; set; }
public int Ref_ID2 { get; set; }
}
}







Join the conversation! Your thoughts help the community grow.