Skip to content

Commit bc4252f

Browse files
Issue214 (#218)
* work to address feature #214 Expose an additional Table type when ResultType.DataTable is used * add DesignTime.GetDataTableType to encapsulate creation of Table type (might make the type name a parameter in later consolidation, see note) * when ResultType.DataTable is used, add the ProvidedType (which was already of DataTable type) as a member to SqlCommandProvider provided type * add a simple unit test which shows that we can specify the type explicitly (I've removed importing ProgrammabilityTest as it is only needed for a single SqlProgrammabilityProvider definition, better to not couple test modules too much) Note: I've only changed SqlCommandProvider, but I noticed there is some code duplication already for the SqlClient provider itself in handling the DataRow and DataTable types. * fixes to exposing Table type * add a sample on how to refer to the table and row types in docs/contents/output.fsx * expose Row type as a inner type of Table type * changed SqlClient.Tests.fsproj to compile with * (minor) indentation to match codebase convention * removed unused ErasedToType field from ResultTypes
1 parent 1882804 commit bc4252f

File tree

5 files changed

+48
-15
lines changed

5 files changed

+48
-15
lines changed

docs/content/output.fsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,14 @@ let table = (new QueryPersonInfoSingletoneDataTable()).AsyncExecute(PersonId = 2
113113
for row in table.Rows do
114114
printfn "Person info:Id - %i,FirstName - %O,LastName - %O" row.PersonID row.FirstName row.LastName
115115

116+
// you can refer to the table type
117+
let table2 : QueryPersonInfoSingletoneDataTable.Table = (new QueryPersonInfoSingletoneDataTable()).Execute(PersonId = 2)
118+
119+
// you can refer to the row type
120+
for row : QueryPersonInfoSingletoneDataTable.Table.Row in table2.Rows do
121+
printfn "Person info:Id - %i,FirstName - %O,LastName - %O" row.PersonID row.FirstName row.LastName
122+
123+
116124
(**
117125
118126
* Same as previous but using `SqlProgrammabilityProvider<...>`

src/SqlClient.Tests/DataTablesTests.fs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ type ProductCostHistory = AdventureWorks.Production.Tables.ProductCostHistory
1717

1818
type GetRowCount = SqlCommandProvider<"SELECT COUNT(*) FROM HumanResources.Shift", ConnectionStrings.AdventureWorksNamed, SingleRow = true>
1919
type GetShiftTableData = SqlCommandProvider<"SELECT * FROM HumanResources.Shift", ConnectionStrings.AdventureWorksNamed, ResultType.DataReader>
20-
20+
type GetArbitraryDataAsDataTable = SqlCommandProvider<"select 1 a, 2 b, 3 c", ConnectionStrings.AdventureWorksNamed, ResultType.DataTable>
2121
type DataTablesTests() =
2222

2323
do
@@ -270,3 +270,14 @@ type DataTablesTests() =
270270
let t = new AdventureWorks.dbo.Tables.TableHavingColumnNamesWithSpaces()
271271
t.AddRow()
272272
Assert.Equal(1, t.Update())
273+
274+
[<Fact>]
275+
member __.``Can use Table type when ResultType = ResultType.DataTable`` () =
276+
let t : GetArbitraryDataAsDataTable.Table = (new GetArbitraryDataAsDataTable()).Execute()
277+
for (_: GetArbitraryDataAsDataTable.Table.Row) in t.Rows do
278+
()
279+
280+
Assert.NotNull(t)
281+
Assert.Equal(1, t.Rows.[0].a)
282+
Assert.Equal(2, t.Rows.[0].b)
283+
Assert.Equal(3, t.Rows.[0].c)

src/SqlClient/DesignTime.fs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,13 @@ open FSharp.Data
1212

1313
type internal ResultTypes = {
1414
ProvidedType : Type
15-
ErasedToType : Type
1615
ProvidedRowType : ProvidedTypeDefinition option
17-
ErasedToRowType : Type
16+
ErasedToRowType : Type
1817
RowMapping : Expr
1918
} with
2019

21-
static member SingleTypeResult(provided, ?erasedTo) = {
20+
static member SingleTypeResult( provided) = {
2221
ProvidedType = provided
23-
ErasedToType = defaultArg erasedTo provided
2422
ProvidedRowType = None
2523
ErasedToRowType = typeof<Void>
2624
RowMapping = Expr.Value Unchecked.defaultof<RowMapping>
@@ -175,6 +173,11 @@ type DesignTime private() =
175173

176174
rowType
177175

176+
static member internal GetDataTableType dataRowType =
177+
let tableType = ProvidedTypeBuilder.MakeGenericType(typedefof<_ DataTable>, [ dataRowType ])
178+
let tableProvidedType = ProvidedTypeDefinition("Table", Some tableType)
179+
tableProvidedType
180+
178181
static member internal GetOutputTypes (outputColumns: Column list, resultType, rank: ResultRank, hasOutputParameters) =
179182
if resultType = ResultType.DataReader
180183
then
@@ -185,10 +188,13 @@ type DesignTime private() =
185188
elif resultType = ResultType.DataTable
186189
then
187190
let dataRowType = DesignTime.GetDataRowType outputColumns
191+
let dataTableType = DesignTime.GetDataTableType dataRowType
192+
193+
// add .Row to .Table
194+
dataTableType.AddMember dataRowType
188195

189196
{
190-
ProvidedType = ProvidedTypeBuilder.MakeGenericType(typedefof<_ DataTable>, [ dataRowType ])
191-
ErasedToType = typeof<DataTable<DataRow>>
197+
ProvidedType = dataTableType
192198
ProvidedRowType = Some dataRowType
193199
ErasedToRowType = typeof<Void>
194200
RowMapping = Expr.Value Unchecked.defaultof<RowMapping>
@@ -226,7 +232,6 @@ type DesignTime private() =
226232

227233
let tupleTypeName = tupleType.PartialAssemblyQualifiedName
228234
None, tupleType, <@@ Microsoft.FSharp.Reflection.FSharpValue.PreComputeTupleConstructor (Type.GetType (tupleTypeName)) @@>
229-
//None, tupleType, <@@ fun values -> Type.GetType(tupleTypeName, throwOnError = true).GetConstructors().[0].Invoke(values) @@>
230235

231236
let nullsToOptions = QuotationsFactory.MapArrayNullableItems(outputColumns, "MapArrayObjItemToOption")
232237
let combineWithNullsToOptions = typeof<QuotationsFactory>.GetMethod("GetMapperWithNullsToOptions")
@@ -241,13 +246,12 @@ type DesignTime private() =
241246
Some( typedefof<_ option>), typedefof<_ option>.MakeGenericType([| erasedToRowType |])
242247
else //ResultRank.ScalarValue
243248
None, erasedToRowType
244-
249+
245250
{
246251
ProvidedType =
247252
if providedRowType.IsSome && genericOutputType.IsSome
248253
then ProvidedTypeBuilder.MakeGenericType(genericOutputType.Value, [ providedRowType.Value ])
249254
else erasedToType
250-
ErasedToType = erasedToType
251255
ProvidedRowType = providedRowType
252256
ErasedToRowType = erasedToRowType
253257
RowMapping = Expr.Call( combineWithNullsToOptions, [ nullsToOptions; rowMapping ])
@@ -499,4 +503,4 @@ type DesignTime private() =
499503
if factoryMethodName.IsSome
500504
then
501505
yield upcast ProvidedMethod(factoryMethodName.Value, parameters2, returnType = cmdProvidedType, IsStaticMethod = true, InvokeCode = body2)
502-
]
506+
]

src/SqlClient/SqlClientProvider.fs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,12 @@ type public SqlProgrammabilityProvider(config : TypeProviderConfig) as this =
540540
addRedirectToISqlCommandMethod typeof<string> "ToTraceString"
541541

542542
commands.AddMember cmdProvidedType
543-
output.ProvidedRowType |> Option.iter cmdProvidedType.AddMember
543+
if resultType = ResultType.DataTable then
544+
// if we don't do this, we get a compile error
545+
// Error The type provider 'FSharp.Data.SqlProgrammabilityProvider' reported an error: type 'Table' was not added as a member to a declaring type <type instanciation name>
546+
output.ProvidedType |> cmdProvidedType.AddMember
547+
else
548+
output.ProvidedRowType |> Option.iter cmdProvidedType.AddMember
544549

545550
let designTimeConfig =
546551
let expectedDataReaderColumns =

src/SqlClient/SqlCommandProvider.fs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,13 @@ type public SqlCommandProvider(config : TypeProviderConfig) as this =
141141
do
142142
cmdProvidedType.AddMember(ProvidedProperty("ConnectionStringOrName", typeof<string>, [], IsStatic = true, GetterCode = fun _ -> <@@ connectionStringOrName @@>))
143143

144-
do //Record
145-
output.ProvidedRowType |> Option.iter cmdProvidedType.AddMember
144+
do
145+
if resultType = ResultType.Records then
146+
// Add .Record
147+
output.ProvidedRowType |> Option.iter cmdProvidedType.AddMember
148+
elif resultType = ResultType.DataTable then
149+
// add .Table
150+
output.ProvidedType |> cmdProvidedType.AddMember
146151

147152
do //ctors
148153
let designTimeConfig =
@@ -218,4 +223,4 @@ type public SqlCommandProvider(config : TypeProviderConfig) as this =
218223
reader.ReadToEnd())
219224
if not (task.Wait(TimeSpan.FromSeconds(1.))) then failwithf "Couldn't read command from file %s" path
220225
task.Result, Some watcher
221-
| _ -> commandTextOrPath, None
226+
| _ -> commandTextOrPath, None

0 commit comments

Comments
 (0)