The MSDN Docs on table-valued Sql Clr functions states:
Transact-SQL table-valued functions materialize the results of calling the function into an intermediate table. ... In contrast, CLR table-valued functions represent a streaming alternative. There is no requirement that the entire set of results be materialized in a single table. The IEnumerable object returned by the managed function is directly called by the execution plan of the query that calls the table-valued function, and the results are consumed in an incremental manner. ... It is also a better alternative if you have very large numbers of rows returned, because they do not have to be materialized in memory as a whole.
Then I find out that no data access is allowed in the 'Fill row' method. This means that you still have to do all of your data access in the init method and keep it in memory, waiting for 'Fill row' to be called. Have I misunderstood something? If I don't force my results into an array or list, I get an error: 'ExecuteReader requires an open and available Connection. The connection's current state is closed.'
Code sample:
[<SqlFunction(DataAccess = DataAccessKind.Read, FillRowMethodName = "Example8Row")>]
static member InitExample8() : System.Collections.IEnumerable =
let c = cn() // opens a context connection
// I'd like to avoid forcing enumeration here:
let data = getData c |> Array.ofSeq
data :> System.Collections.IEnumerable
static member Example8Row ((obj : Object),(ssn: SqlChars byref)) =
do ssn <- new SqlChars(new SqlString(obj :?> string))
()
I'm dealing with several million rows here. Is there any way to do this lazily?