Retrieve LINQ to sql statement (IQueryable) WITH parameters - c#

I'm trying to figure out if there's a way to retrieve the (full) sql statement that gets executed on the database server.
I found something already, but it does not exactly what I would like:
IQueryable<SomeType> someQuery = ...
string command = dataContext.GetCommand(query).CommandText;
In my case this gives me a command string something like:
SELECT TOP (50) [t0].[ID], ....
FROM [dbo].[someTable] AS [t0]
WHERE ([t0].[someColumn] IS NOT NULL) AND (([t0].[someColumn]) IN (#p0))
On database there's executed:
exec sp_executesql N'SELECT TOP (50) [t0].[ID], ...
FROM [dbo].[someTable] AS [t0]
WHERE ([t0].[someColumn] IS NOT NULL) AND (([t0].[someColumn]) IN (#p0, #p1))',N'#p0 int,#p1 int',#p0=401,#p1=201
Is there a way to retrieve this 'full' statement (so also the parameter values) from C# code?

You can also see the generated sql query if you have an instance of
IQueryable<T> and call the .ToString() method.
For Example:
var db = new DbContext();
IQueryable<Blog> query = db.Blog.Where(tt=> tt.Id > 100).OrderByDescending(tt=>tt.Id);
var sqlString = query.ToString();
Console.WriteLine(sqlString);
This will generate an output of:
SELECT [Extent1].[Id] AS [Id],
[Extent1].[Title] AS [Title],
[Extent1].[Author] AS [Author],
[Extent1].[Text] AS [Text],
[Extent1].[CreatedAt] AS [CreatedAt],
[Extent1].[UpdatedAt] AS [UpdatedAt]
FROM [dbo].[Blogs] AS [Extent1]
WHERE [Extent1].[Id] > 100
ORDER BY [Extent1].[Id] DESC

Once you get the Command you can print the CommandText and then loop through the Parameters collection and print all the individual parameters.
Also there is the linq-to-sql debug visualizer which does the same in debug mode.
A really nice tool to view the queries as they are happening is the Linq-to-sql profiler

In the latest version of EF Core 5 ToQueryString,
query.ToQueryString()

(SqlCommand)dataContext.GetCommand(query)
will give you access to Parameters collection.

I'm using Datacontext.Log property to get the generated SQL Statement (it includes the statement text, and parameters).
Just set YourDataContext.Log = SomeTextWriter.
It can be written to a file (Log = new StreamWriter(#"c:\temp\linq.log")) or to debug window, see this post

When viewing the IQueryable in the Locals pane, you can access DebugView > Query, which will contain the SQL statement.

Related

Alter EF Generated query parameters

I'm using EF 6.4.4 to query a SQL view. Now the view is not really performing optimal, but i don't control it.
I'm executing the following code with a WHERE statement on a string/nvarchar property
_context.ViewObject
.Where(x => x.Name == nameFilter)
.ToList();
Similarly, i have the same SQL statement executed in SMSS
SELECT [Id]
, [Name]
, ...
FROM [View]
WHERE [Name] = '<nameFilter>'
My problem is that the EF variant is way slower than the direct SQL query.
When checking out the SQL query generated by EF i see the following:
SELECT [Id]
, [Name]
, ...
FROM [View]
WHERE [Name] = #p__linq__0
with parameter #p__linq__0 is of type NVARCHAR(4000) NULL
This even though that my input variable is not NULL and has a lenght of maximum 6 characters.
When i execute the same sql query with this parameter, it is slow in SMSS as well.
Apparently, this has somethign
So what i want to do is alter the SQL query parameter that EF is using to generate this query. This to make sure that my parameter is more accurately represented in the query and that i can get the same performance as directly in SMSS.
Is there a way to do this?
Whats going on: parameter sniffing
Execute the following in SSMS and you will propably see the same performance.
EXECUTE sp_executesql N'SELECT [Id]
, [Name]
, ...
FROM [View]
WHERE [Name] = #nameFilter'
,N'#nameFilter nvarchar(4000)'
,#nameFilter = '<namefilter>';
sp_executeSql is used by EF to execute queries against a database and thus, when you write .Where(x => x.Name == nameFilter) this is translated to the above statement.
Making you suffer from parameter sniffing.
You could fix this by adding recompile to your queries like described here But be aware that adding recompile to all queries might have negative impact on the other queries.
You can execute the following queries with actual execution plan to see the difference:
Query with WHERE Name = #NameFilter
Query with WHERE Name = '<NameFilter>'
Query with WHERE Name = #NameFilter OPTION(RECOMPILE)
If it's not parameter sniffing, it might be implicit conversions, but I'm guessing both types are NVARCHAR so this shouldn't matter.
99% of the time it's parameter sniffing.

Execution plan of query from profiler

I use SQL profiler to know how Entity Framework convert LINQ expression to sql use database. When query is 'heavy' then I try to optimalize it by examine execution plan.
Profiler (I use Profiler Express) give my query in this format
exec sp_executesql N'SELECT
[Project2].[Id] AS [Id], (... rest od query ... )
',#p__linq__0=N'test#mypage.com'
To see execution plan I have to convert (copy, paste code wrrr) to this format
DELARE #p__linq__0 NVARCHAR(100) =N'test#mypage.com'
SELECT
[Project2].[Id] AS [Id], (... rest od query ... )
It is boring ,irritating etc. Does someony know page or somethig which do it for me? Or I can just set it in options?
Enable "Show Actual Execution Plan" and run the query.

Linq-To-SQL Multiple Trips to the Database

I'm trying to make my Linq-to-SQL query more efficient by including child properties in one trip to the DB. I started by trying various linq queries to accomplish this. The queries were getting complex, so I tried the LoadWith() option:
The constructor of my DAL class sets the LoadWith() settings:
public TrackerJobData()
{
dataLoadOptions = new DataLoadOptions();
dataLoadOptions.LoadWith<TrackerJobRecord>(x => x.SpecBarcodeRecords);
dataLoadOptions.LoadWith<TrackerJobRecord>(x => x.TrackerJobEquipmentTriggerRecords);
dataLoadOptions.LoadWith<TrackerJobRecord>(x => x.EtaRecord);
this.Database.LoadOptions = dataLoadOptions;
}
And here is the query I'm using:
public TrackerJob GetItem(int trackerJobId)
{
TrackerJobRecord record =
(from trackerJob in this.Database.TrackerJobRecords
where trackerJob.TrackerJobId == trackerJobId
select trackerJob).FirstOrDefault();
return record.Map();
}
When I debug and F10 on just the linq query (not the return), I get this output in SQL Profiler:
Pardon my ignorance of SQL Profiler, but do the three highlighted lines mean there were three round trips from the client (my code) to the server? If so, why? Will SQL Server ever execute multiple sp_executesql calls in one trip?
And since I thought LoadWith() would eliminate multiple calls, what am I doing incorrectly?
EDIT
Here are the three statements within SQL Profiler:
exec sp_executesql N'SELECT TOP (1) [t0].[TrackerJobId], [t0].[Name], [t0].[EtaId], [t0].[SamplingProcessorTypeId], [t0].[Description], [t0].[LastModifiedUser], [t0].[LastModifiedTime], [t0].[VersionNumber], [t0].[Active], [t0].[Archived], [t1].[EtaId] AS [EtaId2], [t1].[EtaNumber], [t1].[Title], [t1].[State], [t1].[DateInitialized], [t1].[EtaOriginatorId], [t1].[Quantity], [t1].[Ehs], [t1].[Ship], [t1].[InternalUse], [t1].[DateClosed], [t1].[ExperimentId], [t1].[Disposition], [t1].[TestType], [t1].[LastModifiedUser] AS [LastModifiedUser2], [t1].[LastModifiedTime] AS [LastModifiedTime2], [t1].[VersionNumber] AS [VersionNumber2]
FROM [AutoTracker].[TrackerJob] AS [t0]
INNER JOIN [Global].[Eta] AS [t1] ON [t1].[EtaId] = [t0].[EtaId]
WHERE [t0].[TrackerJobId] = #p0',N'#p0 int',#p0=17
exec sp_executesql N'SELECT [t0].[SpecBarcodeId], [t0].[TrackerJobId], [t0].[EquipmentId], [t0].[StartTime], [t0].[EndTime], [t0].[LastModifiedUser], [t0].[LastModifiedTime], [t0].[VersionNumber]
FROM [AutoTracker].[SpecBarcode] AS [t0]
WHERE [t0].[TrackerJobId] = #x1',N'#x1 int',#x1=17
exec sp_executesql N'SELECT [t0].[TrackerJobId], [t0].[EquipmentId], [t0].[LastModifiedUser], [t0].[LastModifiedTime], [t0].[VersionNumber]
FROM [AutoTracker].[TrackerJobEquipmentTrigger] AS [t0]
WHERE [t0].[TrackerJobId] = #x1',N'#x1 int',#x1=17
Linq-2-sql LoadWith does not support multiple 1:N relationships.
http://weblogs.asp.net/zeeshanhirani/archive/2008/08/11/constraints-with-loadwith-when-loading-multiple-1-n-relationships.aspx
Linq2SQl eager load with multiple DataLoadOptions
Each of those SQL Profiler calls represents a single roundtrip from client to DB server instance. SQL Server does support returning data sets in a single roundtrip, but I'm not sure how you would do that with LINQ to SQL.

Improve query generated from entity framework

I have a query linq like this:
var query = from c in Context.Customers select c;
var result = query.ToList();
Linq query generate this tsql code:
exec sp_executesql N'SELECT
[Project1].[Id] AS [Id],
[Project1].[Name] AS [Name],
[Project1].[Email] AS [Email]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Email] AS [Email]
FROM [dbo].[Customers] AS [Extent1] ) AS [Project1]
Is there a way for not generate subquery?
Do you have any evidence that that query is causing performance problems? I imagine the query-optimizer would easily recognize that.
If you're certain after profiling that the query is a performance problem (doubtful) - and only then - you could simply turn the query into a stored procedure, and call that instead.
You use a tool like Linq because you don't want to write SQL, before abandoning that you should at least compare the query plan of your proposed SQL vs that generated by the tool. I don't have access to SQL Studio at the moment, but I would be a bit surprised if the query plans aren't identical...
EDIT: having had a chance to check out the query plans, they are in fact identical.
Short answer: No you cannot modify that query.
Long answer: If you want to reimplement Linq provider and query generator then perhaps there is a way but I doubt you want to do that. You can also implement custom EF provider wrapper which will take query passed from EF and reformat it but that will be hard as well - and slow. Are you going to write custom interpreter for SQL queries?

How do i modify Linq generated sql statement?

I have summarized my problem in following code.
NorthwindDataContext dc = new NorthwindDataContext();
var query = from c in dc.Customers
select c;
Above code is generating following sql statement
SELECT [t0].[ID], [t0].[FirstName], [t0].[LastName]
FROM [dbo].[Customer] AS [t0]
Now i want to modify the above generated query something like this
SELECT [t0].[ID], [t0].[FirstName], [t0].[LastName] FROM [dbo].[Customer]
AS [t0] WITH (nolock)
Is it possible in linq to modify the generated query?If yes then how?
You will not be able to modify the generated L2S T-SQL code directly, the way you want (unless you modify the transaction isolation level). However, we've dealt with situations like this, fairly simply, by creating a view with lock hints we want in place and querying the view, instead of the table directly.
I have found a very handy tips for modifying the linq generated sql statement.
NorthwindDataContext db = new NorthwindDataContext();
if (db.Connection.State == System.Data.ConnectionState.Closed)
db.Connection.Open();
var cmd = db.GetCommand(db.Customers.Where(p => p.ID == 1));
cmd.CommandText = cmd.CommandText.Replace("[Customers] AS [t0]", "[Customers] AS [t0] WITH (NOLOCK)");
var results = db.Translate(cmd.ExecuteReader());
Maybe these pages will help you..
http://www.infoq.com/news/2008/03/linq-nolock
http://coolthingoftheday.blogspot.com/2008/03/linq-to-sql-nolock.html
which refers hanselmans blog entry
http://www.hanselman.com/blog/GettingLINQToSQLAndLINQToEntitiesToUseNOLOCK.aspx
or check out this question
NOLOCK with Linq to SQL

Categories