FSharp.Data.SqlClient库来连接SQL Server 2016。我想调用一个存储过程,该过程返回多个表格,并将这些表格转换为相应的记录。在这种情况下,第一个表格由items组成,第二个表格由customers
组成。
我的直觉是应该像这样:
let items, customers = cmd.Execute()
我的想法是items
应该是一个IEnumerable<item>
,而customers
应该是一个IEnumerable<customer>
,其中item
和customer
都是记录类型。然而看起来FSharp.Data.SqlClient
只能看到存储过程返回的第一个表格。我正在使用 SQL Server 2016 Developer 实例,并且以下是设置示例的 T-SQL:
create table Item (
ItemID int identity(1, 1) primary key,
ItemName nvarchar(50)
)
go
create table Customer (
CustomerID int identity(1, 1) primary key,
CustomerName nvarchar(50)
)
go
insert into Item (ItemName) values ('A');
insert into Item (ItemName) values ('B');
insert into Item (ItemName) values ('C');
insert into Customer (CustomerName) values ('Gary');
insert into Customer (CustomerName) values ('Sergei');
insert into Customer (CustomerName) values ('Elise');
go
create procedure dbo.ExampleProcedure
as
begin
set nocount on;
select
ItemID,
ItemName
from Item
select
CustomerID,
CustomerName
from Customer
end;
这是我正在测试的 F# 脚本。它展示了我想要实现的内容,但是在最后一行我遇到了编译错误:
#r "../packages/FSharp.Data.SqlClient.1.8.2/lib/net40/FSharp.Data.SqlClient.dll"
#r "../packages/FSharp.Data.2.3.2/lib/net40/FSharp.Data.dll"
#r "System.Xml.Linq.dll"
open FSharp.Data
[<Literal>]
let connStr =
"Data Source=**connection string**;"
type queryExample = SqlProgrammabilityProvider<connStr>
do
use cmd = new queryExample.dbo.ExampleProcedure(connStr)
let items, customers = cmd.Execute()
我希望Items
与第一个返回的表相对应,而Customers
与第二个返回的表相对应。智能感知建议FSharp.Data.SqlClient只看到了第一个表。当我悬停在cmd.Execute()
上时,弹出窗口显示"此表达式预期为'a*'b'类型,但此处的类型为System.Collections.Generic.IEnumerable<SqlProgrammabilityProvider<...>.dbo.ExampleProcedure.Record>"
。如果我执行以下操作,我就可以访问存储过程中的Items
查询:
// Learn more about F# at http://fsharp.org. See the 'F# Tutorial' project
// for more guidance on F# programming.
#r "../packages/FSharp.Data.SqlClient.1.8.2/lib/net40/FSharp.Data.SqlClient.dll"
#r "../packages/FSharp.Data.2.3.2/lib/net40/FSharp.Data.dll"
#r "System.Xml.Linq.dll"
open FSharp.Data
[<Literal>]
let connStr =
"Data Source=**connection string**;"
type queryExample = SqlProgrammabilityProvider<connStr>
do
use cmd = new queryExample.dbo.ExampleProcedure(connStr)
for item in cmd.Execute() do
printfn "%A" item.ItemID
这可行吗?我的方法错了吗?我在这个用例上找不到清晰的文档,但我认为它应该是足够常见的,应该被覆盖。
更新
为了澄清我想要实现什么,我展示了我如何在C#中解决这个问题。在C#中,我创建一个DataSet对象,并将其填充存储过程的结果。从那里,我挑选出单独的表来处理。提取表后,我使用LINQ将行转换为相应的对象。通常看起来像以下内容:
using System.Data;
using System.Data.SqlClient;
var connStr = "**connection string**"
var sqlConnection = new SqlConnection(connStr );
var sqlCommand = new SqlCommand("ExampleProcedure", sqlConnection);
sqlCommand.CommandType = CommandType.StoredProcedure;
var dataSet = new DataSet();
var adapter = new SqlDataAdapter(sqlCommand);
adapter.Fill(dataSet);
var itemsTable = dataSet.Tables[0];
// Turn the itemsTable into a List<Item> using LINQ here
var customersTable = dataSet.Tables[1];
// Turn the customersTable into List<Customer> using LINQ here
我认为对于提取单个表格这样简单的事情来说,这种表述过于冗长,但也许我对代码杂乱过于敏感了。我知道F#一定有更优雅、更简洁的表达方式。