Linq在路上(外一)获取SQL语句
Linq方便了我们对数据库的操作,直接用C#的语法操作数据库。但Linq也隐藏了实际执行的SQL语句,封装是好事,可是有时候还是不得不了解Linq具体对数据库的操作。比如,只有查看实际的SQL语句才能对数据库或查询语句做优化,而且在调试时不能查看SQL是很郁闷的事情。
有下面几种方法来挖掘出操作中所使用的Sql语句:
1、获取Query所对应的SqlCommand对象:
在开发过程中,我们可以通过Query获得对应的Sql Command对象。请看如下代码:
AdventureWorksDataContext db = new AdventureWorksDataContext();
var products = from p in db.Products
where p.ProductID == 3
select new { p.ProductID, p.Name };
foreach (var p in products)
{
Console.WriteLine(p);
}
DbCommand cmd = db.GetCommand(products);
Console.WriteLine("------------");
Console.WriteLine("Command Text: \n{0}", cmd.CommandText);
Console.WriteLine("------------");
Console.WriteLine("Command Type: \n{0}", cmd.CommandType);
Console.WriteLine("------------");
Console.WriteLine("Command Parameters:");
foreach (DbParameter p in cmd.Parameters)
{
Console.WriteLine("{0}: {1}", p.ParameterName, p.Value);
}
Console.ReadLine();
var products = from p in db.Products
where p.ProductID == 3
select new { p.ProductID, p.Name };
foreach (var p in products)
{
Console.WriteLine(p);
}
DbCommand cmd = db.GetCommand(products);
Console.WriteLine("------------");
Console.WriteLine("Command Text: \n{0}", cmd.CommandText);
Console.WriteLine("------------");
Console.WriteLine("Command Type: \n{0}", cmd.CommandType);
Console.WriteLine("------------");
Console.WriteLine("Command Parameters:");
foreach (DbParameter p in cmd.Parameters)
{
Console.WriteLine("{0}: {1}", p.ParameterName, p.Value);
}
Console.ReadLine();
输出结果如下:
Command Text:
SELECT [t0].[ProductID], [t0].[Name]
FROM [Production].[Product] AS [t0]
WHERE [t0].[ProductID] = @p0
------------
Command Type:
Text
------------
Command Parameters:
@p0: 3
SELECT [t0].[ProductID], [t0].[Name]
FROM [Production].[Product] AS [t0]
WHERE [t0].[ProductID] = @p0
------------
Command Type:
Text
------------
Command Parameters:
@p0: 3
可以看到,无论是Sql语句或是参数都被打印了出来。事实上,由于我们得到了完整的SqlCommand对象,我们可以获取的信息并不止上述这些。
2、使用LINQ to SQL Debug Visualizer:
使用LINQ to SQL Debug Visiualizer,我们可以在调试程序时直观地获得Query所对应的Sql语句以及参数,而不必获得SqlCommand对象并打印信息。具体使用方法详见Scott Gu的这篇博文。
3、使用DataContext的Log功能:
DataContext自带的Log属性为一个TextWriter类型的对象,如果我们设置了这个属性,则DataContext所有的
操作将会通过这个TextWriter对象输出。与比上述两种方法相比,这个方法的优势在于DataContext所执行的所有语句,无论SELECT、
INSERT、UPDATE或者是DELETE都会被输出;而上面的两种做法只能得到Query的信息,也就是SQL语句的SELECT操作。
请看如下代码,下面的代码将AdventureWorksDataContext对象的所有操作输出至Console:
AdventureWorksDataContext db = new AdventureWorksDataContext();
db.Log = Console.Out;
Product product = (from p in db.Products
where p.ProductID == 1
select p).First();
product.Name = "Hello World";
db.SubmitChanges();
Console.ReadLine();
db.Log = Console.Out;
Product product = (from p in db.Products
where p.ProductID == 1
select p).First();
product.Name = "Hello World";
db.SubmitChanges();
Console.ReadLine();
输出结果如下:
SELECT TOP (1) [t0].[ProductID], [t0].[Name], [t0].[ProductNumber], [t0].[MakeFl
...
edDate], [t0].[rowguid], [t0].[ModifiedDate]
FROM [Production].[Product] AS [t0]
WHERE [t0].[ProductID] = @p0
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21004.1
UPDATE [Production].[Product]
SET [Name] = @p11
WHERE ([ProductID] = @p0) AND ([Name] = @p1) AND ([ProductNumber] = @p2) AND (NO
...
NULL) AND ([rowguid] = @p9) AND ([ModifiedDate] = @p10)
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
...
-- @p11: Input NVarChar (Size = 11; Prec = 0; Scale = 0) [Hello World]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21004.1
...
edDate], [t0].[rowguid], [t0].[ModifiedDate]
FROM [Production].[Product] AS [t0]
WHERE [t0].[ProductID] = @p0
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21004.1
UPDATE [Production].[Product]
SET [Name] = @p11
WHERE ([ProductID] = @p0) AND ([Name] = @p1) AND ([ProductNumber] = @p2) AND (NO
...
NULL) AND ([rowguid] = @p9) AND ([ModifiedDate] = @p10)
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
...
-- @p11: Input NVarChar (Size = 11; Prec = 0; Scale = 0) [Hello World]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21004.1
在这里我省略了大部分的输出,不过从上面的片断中我们已经可以看到SELECT和UPDATE操作所使用的Sql语句以及参数都被打印了出来。这就是我们可以利用的调试信息。
4、使用Sql Server Profiler:
这个是我推荐的方法,比1,3方便,而且不可能漏掉任何信息。Sql Profiler的用法就不说了,大家应该都用过,但和监视一般的SQL还是有点地方需要注意。
上面这段代码Linq不是直接将它转换成"select id from aaa where....."而是转换成
aaaDataContext db = new aaaDataContext();
aaa a = new aaa();
var q =
from p in context.aaa
where p.a.StartsWith("a") == true
select p.id;
int? a1 = q.First();
aaa a = new aaa();
var q =
from p in context.aaa
where p.a.StartsWith("a") == true
select p.id;
int? a1 = q.First();
上面这段代码Linq不是直接将它转换成"select id from aaa where....."而是转换成
exec sp_executesql N'SELECT TOP (1) [t0].[id]
FROM [dbo].[aaa] AS [t0]
WHERE [t0].[a] LIKE @p0',N'@p0 nvarchar(2)',@p0=N'a%'
FROM [dbo].[aaa] AS [t0]
WHERE [t0].[a] LIKE @p0',N'@p0 nvarchar(2)',@p0=N'a%'
使用了存储过程sp_executesql以及动态代码的方式,因此,想要获取上述这段SQL需要打开profiler事件选择中stored procedures下的RPC:Completed和SP:Completed事件。而且由于Linq使用延迟加载机制来减少性能消耗,因此只有在int? a1 = q.First();执行时,sql语句才真正被执行。
鼓励作者写出更好的文章
QQ讨论群612280956
每个人都会经过这个阶段,见到一座山,就想知道山后面是什么。我很想告诉他,可能翻过山后面,你会发现没什么特别。回望之下,可能会觉得这一边更好。 每个人都会坚持自己的信念,在别人看来,是浪费时间,她却觉得很重要。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端