I'm assuming you're using SQL Server 2008. As mentioned by a Microsoft employee on this page, 2008 requires methods to be marked with DataAccessKind.Read much more frequently than 2005. One of those times is when the TVF participates in a transaction (which seemed to always be the case, when I tested). The solution is to specify enlist=false
in the connection string, which, alas, cannot be combined with context connection=true
. That means your connection string needs to be in typical client format: Data Source=.;Initial Catalog=MyDb;Integrated Security=sspi;Enlist=false
and your assembly must be created with permission_set=external_access
, at minimum. The following works:
using System;
using System.Collections;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
namespace SqlClrTest {
public static class Test {
[SqlFunction(
DataAccess = DataAccessKind.Read,
SystemDataAccess = SystemDataAccessKind.Read,
TableDefinition = "RowNumber int",
FillRowMethodName = "FillRow"
)]
public static IEnumerable MyTest(SqlInt32 databaseID) {
using (var con = new SqlConnection("data source=.;initial catalog=TEST;integrated security=sspi;enlist=false")) {
con.Open();
using (var cmd = new SqlCommand("select top (100) RowNumber from SSP1 where DatabaseID = @DatabaseID", con)) {
cmd.Parameters.AddWithValue("@DatabaseID", databaseID.IsNull ? (object)DBNull.Value : databaseID.Value);
using (var reader = cmd.ExecuteReader()) {
while (reader.Read())
yield return reader.GetInt32(0);
}
}
}
}
public static void FillRow(object obj, out SqlInt32 rowNumber) {
rowNumber = (int)obj;
}
}
}
Here's the same thing in F#:
namespace SqlClrTest
module Test =
open System
open System.Data
open System.Data.SqlClient
open System.Data.SqlTypes
open Microsoft.SqlServer.Server
[<SqlFunction(
DataAccess = DataAccessKind.Read,
SystemDataAccess = SystemDataAccessKind.Read,
TableDefinition = "RowNumber int",
FillRowMethodName = "FillRow"
)>]
let MyTest (databaseID:SqlInt32) =
seq {
use con = new SqlConnection("data source=.;initial catalog=TEST;integrated security=sspi;enlist=false")
con.Open()
use cmd = new SqlCommand("select top (100) RowNumber from SSP1 where DatabaseID = @DatabaseID", con)
cmd.Parameters.AddWithValue("@DatabaseID", if databaseID.IsNull then box DBNull.Value else box databaseID.Value) |> ignore
use reader = cmd.ExecuteReader()
while reader.Read() do
yield reader.GetInt32(0)
} :> System.Collections.IEnumerable
let FillRow (obj:obj) (rowNumber:SqlInt32 byref) =
rowNumber <- SqlInt32(unbox obj)
The good news is: Microsoft considers this a bug.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…