EF6.TagWith
1.3.0-preview1
dotnet add package EF6.TagWith --version 1.3.0-preview1
NuGet\Install-Package EF6.TagWith -Version 1.3.0-preview1
<PackageReference Include="EF6.TagWith" Version="1.3.0-preview1" />
paket add EF6.TagWith --version 1.3.0-preview1
#r "nuget: EF6.TagWith, 1.3.0-preview1"
// Install EF6.TagWith as a Cake Addin #addin nuget:?package=EF6.TagWith&version=1.3.0-preview1&prerelease // Install EF6.TagWith as a Cake Tool #tool nuget:?package=EF6.TagWith&version=1.3.0-preview1&prerelease
EF6-TagWith
Tag your Entity Framework 6 LINQ queries, and see these tags in the SQL commands sent to the database. This will allow you to easily identify queries while using tools like SQL Profiler or Azure's performance and diagnostic features.
(Yes, it is a simple implementation of Entity Framework Core's query tags feature.)
Usage (SQL Server)
Install the NuGet package in your project.
PM> install-package EF6.TagWith
Add the query interceptor. The easiest way is just to add the following code somewhere in your application startup code:
DbInterception.Add(new QueryTaggerInterceptor(new SqlServerTagger()));
Starting at version 1.2, you may also use the
TagWith.Initialize()
helper method as follows:TagWith.Initialize<SqlServerTagger>();
This method is the recommended option because it allows you to specify some tagging options, as explained below.
Use the
TagWith()
extension to tag your queries:var query = context.Friends .OrderByDescending(friend => friend.Age) .Take(10) .Select(friend => new { FriendName = friend.Name, friend.Age, CountryName = friend.Country.Name } ) .TagWith("Get top 10 older friends with country");
By default, the query sent to the database will be as follows:
-- Get top 10 older friends with country SELECT TOP(@__p_0) [friend].[Name] AS [FriendName], [friend].[Age], [friend.Country].[Name] AS [CountryName] FROM [Friends] AS [friend] LEFT JOIN [Countries] AS [friend.Country] ON [friend].[CountryId] = [friend.Country].[Id] ORDER BY [friend].[Age] DESC
How this works
The TagWith()
extension method adds a special predicate to the query, so it can be easily identified in the final SQL.
Later on, just before sending the SQL command to the database, we use EF 6 interceptors to extract this predicate and insert the tag as a comment, just using a bit of string wizardry.
Prefix vs Infix
By default, TagWith inserts the tags before the SQL command. However, there are some query tracing tools, such as SQL Query Store or Azure SQL Performance Monitor, that remove initial comments so the tags are not shown. For these cases, we can force the tags (comments) to be inserted after the SELECT
command.
For example, the above SQL command would look like this:
SELECT -- Get top 10 older friends with country
TOP(@__p_0) [friend].[Name] AS [FriendName], [friend].[Age],
[friend.Country].[Name] AS [CountryName]
FROM [Friends] AS [friend]
LEFT JOIN [Countries] AS [friend.Country]
ON [friend].[CountryId] = [friend.Country].[Id]
ORDER BY [friend].[Age] DESC
The "infix" mode must be specified during the component initialization this way:
TagWith.Initialize<SqlServerTagger>(
new TaggingOptions() {
TagMode = TagMode.Infix
});
Predicate expression
By default, the predicate included in the query specification when you use the method TagWith()
looks like this:
// LINQ query:
var query = _ctx.Friends.TagWith("My friends").Where(f => f.Id < 10);
Console.WriteLine(query.ToString());
// Output:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Country_Id] AS [Country_Id]
FROM [dbo].[Friends] AS [Extent1]
WHERE (N'!__tag!' = N'My friends') AND ([Extent1].[Id] < 10)
Later on, just before the query is sent to the database, the predicate will be removed and the tag will be added as a comment.
As suggested by d-a-s, the problem with this approach is that if you try to get the SQL using ToString()
, as seen above, and try to execute it against the database, you'll never get any results because the predicate is always evaluated as false
.
Starting at version 1.3.0, you can change this behaviour by specifying a different predicate expression during the component initialization.
TagWith.Initialize<SqlServerTagger>(
new TaggingOptions() {
PredicateExpression = PredicateExpression.NotEquals
});
In this case, the query specification will be as follows, so you could run it against the database because the inserted predicate will be always true
(note the distinct '<>' operator usage):
// LINQ query:
var query = _ctx.Friends.TagWith("My friends").Where(f => f.Id < 10);
Console.WriteLine(query.ToString());
// Output:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Country_Id] AS [Country_Id]
FROM [dbo].[Friends] AS [Extent1]
WHERE (N'!__tag!' <> N'My friends') AND ([Extent1].[Id] < 10)
Another tagging options
Apart from using TagWith()
in the query, you may also use the extension method TagWithSource()
, that will include automatically the caller member name and source code file, like in the following example:
// Initialization:
TagWith.Initialize<SqlServerTagger>();
// Query:
private List<Friend> GetFriends()
{
var friends = ctx.Friends.OrderBy(f => f.Name).Take(10).TagWithSource("Getting friends").ToList();
return friends;
}
Then, the SQL query sent to the database will be as follows:
-- Getting friends - GetFriends - C:\repos\TagWithDemo\FriendRepository.cs:20
SELECT
[Limit1].[Id] AS [Id],
[Limit1].[Name] AS [Name],
[Limit1].[Country_Id] AS [Country_Id]
FROM ( SELECT TOP (10) [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name],
[Extent1].[Country_Id] AS [Country_Id]
FROM [dbo].[Friends] AS [Extent1]
ORDER BY [Extent1].[Name] ASC
) AS [Limit1]
ORDER BY [Limit1].[Name] ASC
.NET versions supported
EF6.TagWith should work with any .NET Framework version higher than 4.0, as well as with any version of .NET Core thanks to .NET Standard. So in theory you can use it wherever you are using Entity Framework 6.x.
If you find issues, please let me know, detailing what .NET version are you using and what kind of problems are you experiencing.
Known issues
- The component only supports SQL Server, but can be easily adapted to support another providers just creating a new implementation of
ISqlTagger
and using it in the interceptor configuration. - If you are mocking a
DbSet
that is queried usingTagWith()
you may find problems. Please read this issue from @jsgoupil to see how to solve it.
Contributions
Feel free to send issues, comments or pull requests. The following users have contributed to this project so far:
Thank you!
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.1 is compatible. |
.NET Framework | net40 is compatible. net403 was computed. net45 is compatible. net451 was computed. net452 was computed. net46 was computed. net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.0
- EntityFramework (>= 6.0.0)
-
.NETFramework 4.5
- EntityFramework (>= 6.0.0)
-
.NETStandard 2.1
- EntityFramework (>= 6.4.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories (1)
Showing the top 1 popular GitHub repositories that depend on EF6.TagWith:
Repository | Stars |
---|---|
SparkDevNetwork/Rock
An open source CMS, Relationship Management System (RMS) and Church Management System (ChMS) all rolled into one.
|
Version | Downloads | Last updated |
---|---|---|
1.3.0-preview1 | 622 | 4/2/2023 |
1.2.1 | 91,106 | 1/24/2021 |
1.2.0 | 853 | 1/11/2021 |
1.1.0-preview2 | 786 | 3/29/2020 |
1.1.0-preview1 | 716 | 3/28/2020 |
1.0.4 | 14,786 | 8/13/2019 |
1.0.3 | 883 | 8/6/2019 |
1.0.2 | 909 | 6/19/2019 |
1.0.1 | 893 | 6/16/2019 |
1.0.0 | 948 | 6/14/2019 |
Added in this release:
- Bug fixes