Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/SqlClient.Tests/ProgrammabilityTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ open FSharp.Data.SqlClient

type AdventureWorks = SqlProgrammabilityProvider<ConnectionStrings.AdventureWorksNamed>

type AdventureWorksDataTables = SqlProgrammabilityProvider<ConnectionStrings.AdventureWorksLiteral, ResultType = ResultType.DataTable>
type GetContactInformation = AdventureWorks.dbo.ufnGetContactInformation

[<Fact>]
Expand Down Expand Up @@ -259,5 +260,9 @@ let PassingImageAsParamDoesntGetCut() =
Assert.Equal(existing.LargePhoto.Value.Length, inserted.LargePhoto.Value.Length)
Assert.Equal(existing.LargePhoto, inserted.LargePhoto)



[<Fact>]
let ``honors result type parameter: datatable`` () =
let command = new AdventureWorksDataTables.Sales.GetUKSalesOrders(ConnectionStrings.AdventureWorksLiteral)
let gbp = 1.0M<AdventureWorksDataTables.Sales.``Units of Measure``.GBP>
let table : AdventureWorksDataTables.Sales.GetUKSalesOrders.Table = command.Execute(gbp)
Assert.Equal<string>("Year", table.Columns.Year.ColumnName)
10 changes: 10 additions & 0 deletions src/SqlClient/DesignTime.fs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ type internal ReturnType = {
| Some x -> Expr.Value( x.ErasedTo.AssemblyQualifiedName)
| None -> <@@ null: string @@>

module internal SharedLogic =
/// Adds .Record or .Table inner type depending on resultType
let alterReturnTypeAccordingToResultType (returnType: ReturnType) (cmdProvidedType: ProvidedTypeDefinition) resultType =
if resultType = ResultType.Records then
// Add .Record
returnType.PerRow |> Option.iter (fun x -> cmdProvidedType.AddMember x.Provided)
elif resultType = ResultType.DataTable then
// add .Table
returnType.Single |> cmdProvidedType.AddMember

type DesignTime private() =
static member internal AddGeneratedMethod
(sqlParameters: Parameter list, hasOutputParameters, executeArgs: ProvidedParameter list, erasedType, providedOutputType, name) =
Expand Down
22 changes: 12 additions & 10 deletions src/SqlClient/SqlClientProvider.fs
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,21 @@ type SqlProgrammabilityProvider(config : TypeProviderConfig) as this =
ProvidedStaticParameter("ConfigFile", typeof<string>, "")
ProvidedStaticParameter("DataDirectory", typeof<string>, "")
ProvidedStaticParameter("UseReturnValue", typeof<bool>, false)
],
ProvidedStaticParameter("ResultType", typeof<ResultType>, ResultType.Records)
],
instantiationFunction = (fun typeName args ->
let root = lazy this.CreateRootType(typeName, unbox args.[0], unbox args.[1], unbox args.[2], unbox args.[3])
let root = lazy this.CreateRootType(typeName, unbox args.[0], unbox args.[1], unbox args.[2], unbox args.[3], unbox args.[4])
cache.GetOrAdd(typeName, root)
)
)

providerType.AddXmlDoc """
<summary>Typed access to SQL Server programmable objects: stored procedures, functions and user defined table types.</summary>
<param name='ConnectionStringOrName'>String used to open a SQL Server database or the name of the connection string in the configuration file in the form of “name=&lt;connection string name&gt;”.</param>
<param name='ResultType'>A value that defines structure of result: Records, Tuples, DataTable, or SqlDataReader.</param>
<param name='ConfigFile'>The name of the configuration file that’s used for connection strings at DESIGN-TIME. The default value is app.config or web.config.</param>
<param name='DataDirectory'>The name of the data directory that replaces |DataDirectory| in connection strings. The default value is the project or script directory.</param>
<param name='UseReturnValue'>To be documented.</param>
<param name='ResultType'>A value that defines structure of result: Records, Tuples, DataTable, or SqlDataReader, this affects only Stored Procedures.</param>
"""

this.AddNamespace(nameSpace, [ providerType ])
Expand All @@ -68,7 +70,7 @@ type SqlProgrammabilityProvider(config : TypeProviderConfig) as this =
|> defaultArg
<| base.ResolveAssembly args

member internal this.CreateRootType( typeName, connectionStringOrName, configFile, dataDirectory, useReturnValue) =
member internal this.CreateRootType( typeName, connectionStringOrName, configFile, dataDirectory, useReturnValue, resultType) =
if String.IsNullOrWhiteSpace connectionStringOrName then invalidArg "ConnectionStringOrName" "Value is empty!"

let designTimeConnectionString = DesignTimeConnectionString.Parse(connectionStringOrName, config.ResolutionFolder, configFile)
Expand Down Expand Up @@ -122,7 +124,7 @@ type SqlProgrammabilityProvider(config : TypeProviderConfig) as this =

schemaType.AddMembersDelayed <| fun() ->
[
let routines = this.Routines(conn, schemaType.Name, udttsPerSchema, ResultType.Records, designTimeConnectionString, useReturnValue, uomPerSchema)
let routines = this.Routines(conn, schemaType.Name, udttsPerSchema, resultType, designTimeConnectionString, useReturnValue, uomPerSchema)
routines |> List.iter tagProvidedType
yield! routines

Expand Down Expand Up @@ -182,8 +184,8 @@ type SqlProgrammabilityProvider(config : TypeProviderConfig) as this =

let returnType = DesignTime.GetOutputTypes(outputColumns, resultType, rank, hasOutputParameters, unitsOfMeasurePerSchema)

do //Record
returnType.PerRow |> Option.iter (fun x -> cmdProvidedType.AddMember x.Provided)
do
SharedLogic.alterReturnTypeAccordingToResultType returnType cmdProvidedType resultType

//ctors
let sqlParameters = Expr.NewArray( typeof<SqlParameter>, parameters |> List.map QuotationsFactory.ToSqlParam)
Expand Down Expand Up @@ -218,17 +220,17 @@ type SqlProgrammabilityProvider(config : TypeProviderConfig) as this =
yield upcast DesignTime.AddGeneratedMethod(parameters, hasOutputParameters, executeArgs, cmdProvidedType.BaseType, returnType.Single, "Execute")

if not hasOutputParameters
then
then
let asyncReturnType = ProvidedTypeBuilder.MakeGenericType(typedefof<_ Async>, [ returnType.Single ])
yield upcast DesignTime.AddGeneratedMethod(parameters, hasOutputParameters, executeArgs, cmdProvidedType.BaseType, asyncReturnType, "AsyncExecute")

if returnType.PerRow.IsSome
then
then
let providedReturnType = ProvidedTypeBuilder.MakeGenericType(typedefof<_ option>, [ returnType.PerRow.Value.Provided ])
let providedAsyncReturnType = ProvidedTypeBuilder.MakeGenericType(typedefof<_ Async>, [ providedReturnType ])

if not hasOutputParameters
then
then
yield upcast DesignTime.AddGeneratedMethod(parameters, hasOutputParameters, executeArgs, cmdProvidedType.BaseType, providedReturnType, "ExecuteSingle")
yield upcast DesignTime.AddGeneratedMethod(parameters, hasOutputParameters, executeArgs, cmdProvidedType.BaseType, providedAsyncReturnType, "AsyncExecuteSingle")
]
Expand Down
7 changes: 1 addition & 6 deletions src/SqlClient/SqlCommandProvider.fs
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,7 @@ type SqlCommandProvider(config : TypeProviderConfig) as this =
cmdProvidedType.AddMember(ProvidedProperty("ConnectionStringOrName", typeof<string>, [], IsStatic = true, GetterCode = fun _ -> <@@ connectionStringOrName @@>))

do
if resultType = ResultType.Records then
// Add .Record
returnType.PerRow |> Option.iter (fun x -> cmdProvidedType.AddMember x.Provided)
elif resultType = ResultType.DataTable then
// add .Table
returnType.Single |> cmdProvidedType.AddMember
SharedLogic.alterReturnTypeAccordingToResultType returnType cmdProvidedType resultType

do //ctors
let designTimeConfig =
Expand Down