From 7e8c1bf3d5771c83151033743fd47a32fac8414e Mon Sep 17 00:00:00 2001 From: Sam Hanes Date: Wed, 17 Oct 2018 22:15:05 -0400 Subject: [PATCH] Split TP into separate design time and runtime assemblies --- SqlClient.sln | 15 +- Tests.sln | 19 +- build.fsx | 4 +- nuget/SqlClient.nuspec | 5 + paket.dependencies | 2 +- src/SqlClient.DesignTime/AssemblyInfo.fs | 20 ++ .../DesignTime.fs | 70 +----- .../DesignTimeConnectionString.fs | 72 ++++++ .../ProvidedTypesCache.fs | 0 .../QuotationsFactory.fs | 7 +- .../Scripts/ReverseLineOrderForNotex.fsx | 0 .../Scripts/Scratchpad.fsx | 0 .../Scripts/XE.fsx | 0 .../SingleFileChangeMonitor.fs | 0 .../SingleRootTypeProvider.fs | 4 +- .../SqlClient.DesignTime.fsproj | 54 +++++ .../SqlClientExtensions.fs | 160 +------------ .../SqlClientProvider.fs | 5 +- .../SqlCommandProvider.fs | 11 +- .../SqlEnumProvider.fs | 7 +- .../SqlFileProvider.fs | 0 src/SqlClient.DesignTime/paket.references | 6 + .../WebApi.Controllers.fsproj | 27 +-- src/SqlClient.Samples/WebApi/web.config | 15 -- .../WpfDataBinding/App.config | 15 -- src/SqlClient.Tests/ConfigurationTest.fs | 33 --- src/SqlClient.Tests/SpatialTypesTests.fs | 1 + .../DesignTimeConnectionStringTests.fs | 31 +++ .../SqlClient.DesignTime.Tests.fsproj | 26 +++ .../SqlClient.DesignTime.Tests/app.config | 68 ++++++ .../paket.references | 6 + .../SqlClient.Tests.NET40/Program.fs | 4 +- .../SqlClient.Tests.NET40.fsproj | 3 +- .../Uncomment.App.config | 4 +- .../SqlClient.Tests.NET40/paket.references | 2 +- src/SqlClient.Tests/TransactionTests.fs | 4 - src/SqlClient.Tests/TypeProviderTest.fs | 4 - src/SqlClient.Tests/app.config | 10 +- src/SqlClient/Configuration.fs | 85 ------- src/SqlClient/DataTable.fs | 1 - src/SqlClient/Extensions.fs | 63 ++++++ src/SqlClient/ISqlCommand.fs | 16 +- src/SqlClient/Runtime.fs | 7 + src/SqlClient/Shared.fs | 210 ++++++++++++++++++ src/SqlClient/SqlClient.fsproj | 36 +-- src/SqlClient/paket.references | 9 +- 46 files changed, 642 insertions(+), 499 deletions(-) create mode 100644 src/SqlClient.DesignTime/AssemblyInfo.fs rename src/{SqlClient => SqlClient.DesignTime}/DesignTime.fs (90%) create mode 100644 src/SqlClient.DesignTime/DesignTimeConnectionString.fs rename src/{SqlClient => SqlClient.DesignTime}/ProvidedTypesCache.fs (100%) rename src/{SqlClient => SqlClient.DesignTime}/QuotationsFactory.fs (94%) rename src/{SqlClient => SqlClient.DesignTime}/Scripts/ReverseLineOrderForNotex.fsx (100%) rename src/{SqlClient => SqlClient.DesignTime}/Scripts/Scratchpad.fsx (100%) rename src/{SqlClient => SqlClient.DesignTime}/Scripts/XE.fsx (100%) rename src/{SqlClient => SqlClient.DesignTime}/SingleFileChangeMonitor.fs (100%) rename src/{SqlClient => SqlClient.DesignTime}/SingleRootTypeProvider.fs (86%) create mode 100644 src/SqlClient.DesignTime/SqlClient.DesignTime.fsproj rename src/{SqlClient => SqlClient.DesignTime}/SqlClientExtensions.fs (79%) rename src/{SqlClient => SqlClient.DesignTime}/SqlClientProvider.fs (99%) rename src/{SqlClient => SqlClient.DesignTime}/SqlCommandProvider.fs (97%) rename src/{SqlClient => SqlClient.DesignTime}/SqlEnumProvider.fs (98%) rename src/{SqlClient => SqlClient.DesignTime}/SqlFileProvider.fs (100%) create mode 100644 src/SqlClient.DesignTime/paket.references create mode 100644 src/SqlClient.Tests/SqlClient.DesignTime.Tests/DesignTimeConnectionStringTests.fs create mode 100644 src/SqlClient.Tests/SqlClient.DesignTime.Tests/SqlClient.DesignTime.Tests.fsproj create mode 100644 src/SqlClient.Tests/SqlClient.DesignTime.Tests/app.config create mode 100644 src/SqlClient.Tests/SqlClient.DesignTime.Tests/paket.references create mode 100644 src/SqlClient/Extensions.fs create mode 100644 src/SqlClient/Runtime.fs create mode 100644 src/SqlClient/Shared.fs diff --git a/SqlClient.sln b/SqlClient.sln index 5f3d28d6..50995b49 100644 --- a/SqlClient.sln +++ b/SqlClient.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.24720.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.28010.2041 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".paket", ".paket", "{FD7933BD-2A90-49EB-A4B2-95F9D3076BBD}" ProjectSection(SolutionItems) = preProject @@ -51,7 +51,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "templates", "templates", "{ docs\tools\templates\template.cshtml = docs\tools\templates\template.cshtml EndProjectSection EndProject -Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "SqlClient", "src\SqlClient\SqlClient.fsproj", "{8974CE2E-79B4-4887-859A-B853C2624138}" +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "SqlClient.DesignTime", "src\SqlClient.DesignTime\SqlClient.DesignTime.fsproj", "{8974CE2E-79B4-4887-859A-B853C2624138}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "SqlClient", "src\SqlClient\SqlClient.fsproj", "{30BA2514-534A-42EA-A0E7-46DF9FDCA954}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -63,6 +65,10 @@ Global {8974CE2E-79B4-4887-859A-B853C2624138}.Debug|Any CPU.Build.0 = Debug|Any CPU {8974CE2E-79B4-4887-859A-B853C2624138}.Release|Any CPU.ActiveCfg = Release|Any CPU {8974CE2E-79B4-4887-859A-B853C2624138}.Release|Any CPU.Build.0 = Release|Any CPU + {30BA2514-534A-42EA-A0E7-46DF9FDCA954}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {30BA2514-534A-42EA-A0E7-46DF9FDCA954}.Debug|Any CPU.Build.0 = Debug|Any CPU + {30BA2514-534A-42EA-A0E7-46DF9FDCA954}.Release|Any CPU.ActiveCfg = Release|Any CPU + {30BA2514-534A-42EA-A0E7-46DF9FDCA954}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -72,4 +78,7 @@ Global {573DBBFB-0F97-4327-8614-6A4151CD70BF} = {61AC061E-5824-41B7-8E09-8D3A73D564E5} {CB79269B-025B-4D6A-AF84-0AD821F6A602} = {573DBBFB-0F97-4327-8614-6A4151CD70BF} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {82096CC6-31F2-47A5-8D46-E41C63B4DB58} + EndGlobalSection EndGlobal diff --git a/Tests.sln b/Tests.sln index ee56a1f6..8d138ddd 100644 --- a/Tests.sln +++ b/Tests.sln @@ -1,18 +1,20 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.28010.2041 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".paket", ".paket", "{E35ED000-5A6C-49E1-82CF-55CB8C16C2AB}" ProjectSection(SolutionItems) = preProject paket.dependencies = paket.dependencies EndProjectSection EndProject -Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "SqlClient.Tests", "src\SqlClient.Tests\SqlClient.Tests.fsproj", "{624C31FF-2003-4334-B02E-AD5A79FE9ED8}" +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "SqlClient.Tests", "src\SqlClient.Tests\SqlClient.Tests.fsproj", "{624C31FF-2003-4334-B02E-AD5A79FE9ED8}" EndProject -Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "SqlClient.Tests.NET40", "src\SqlClient.Tests\SqlClient.Tests.NET40\SqlClient.Tests.NET40.fsproj", "{DE2EC181-0452-415D-82C3-1D43168D4FF1}" +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "SqlClient.Tests.NET40", "src\SqlClient.Tests\SqlClient.Tests.NET40\SqlClient.Tests.NET40.fsproj", "{DE2EC181-0452-415D-82C3-1D43168D4FF1}" EndProject -Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Lib", "src\SqlClient.Tests\Lib\Lib.fsproj", "{17EE08F0-CE4C-4C17-A376-157249C9ABC2}" +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Lib", "src\SqlClient.Tests\Lib\Lib.fsproj", "{17EE08F0-CE4C-4C17-A376-157249C9ABC2}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "SqlClient.DesignTime.Tests", "src\SqlClient.Tests\SqlClient.DesignTime.Tests\SqlClient.DesignTime.Tests.fsproj", "{7B283341-7AE6-4D02-8BFE-F94C80617F90}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -32,8 +34,15 @@ Global {17EE08F0-CE4C-4C17-A376-157249C9ABC2}.Debug|Any CPU.Build.0 = Debug|Any CPU {17EE08F0-CE4C-4C17-A376-157249C9ABC2}.Release|Any CPU.ActiveCfg = Release|Any CPU {17EE08F0-CE4C-4C17-A376-157249C9ABC2}.Release|Any CPU.Build.0 = Release|Any CPU + {7B283341-7AE6-4D02-8BFE-F94C80617F90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7B283341-7AE6-4D02-8BFE-F94C80617F90}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7B283341-7AE6-4D02-8BFE-F94C80617F90}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7B283341-7AE6-4D02-8BFE-F94C80617F90}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BB177D8C-41D5-430D-94E8-485567A72F92} + EndGlobalSection EndGlobal diff --git a/build.fsx b/build.fsx index 83c16637..cbe8ee20 100644 --- a/build.fsx +++ b/build.fsx @@ -216,8 +216,8 @@ Target.create "All" Target.DoNothing open Fake.Core.TargetOperators // for ==> -"Clean" - ==> "AssemblyInfo" +//"Clean" +"AssemblyInfo" ==> "Build" ==> "DeployTestDB" ==> "BuildTests" diff --git a/nuget/SqlClient.nuspec b/nuget/SqlClient.nuspec index 5c1ae3a6..daa31b9c 100644 --- a/nuget/SqlClient.nuspec +++ b/nuget/SqlClient.nuspec @@ -25,8 +25,13 @@ + + + + + \ No newline at end of file diff --git a/paket.dependencies b/paket.dependencies index 24ea511a..084f1c79 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -32,7 +32,7 @@ group Test redirects: force nuget FSharp.Core = 4.1.2 - nuget FSharp.Configuration + nuget FSharp.Configuration = 1.4.0 nuget Newtonsoft.Json nuget xunit = 1.9.2 nuget xunit.runner.visualstudio = 2.1.0 diff --git a/src/SqlClient.DesignTime/AssemblyInfo.fs b/src/SqlClient.DesignTime/AssemblyInfo.fs new file mode 100644 index 00000000..910851af --- /dev/null +++ b/src/SqlClient.DesignTime/AssemblyInfo.fs @@ -0,0 +1,20 @@ +// Auto-Generated by FAKE; do not edit +namespace System +open System.Reflection +open System.Runtime.CompilerServices + +[] +[] +[] +[] +[] +[] +do () + +module internal AssemblyVersionInformation = + let [] AssemblyTitle = "SqlClient" + let [] AssemblyProduct = "FSharp.Data.SqlClient" + let [] AssemblyDescription = "SqlClient F# type providers" + let [] AssemblyVersion = "1.8.6" + let [] AssemblyFileVersion = "1.8.6" + let [] InternalsVisibleTo = "SqlClient.Tests" diff --git a/src/SqlClient/DesignTime.fs b/src/SqlClient.DesignTime/DesignTime.fs similarity index 90% rename from src/SqlClient/DesignTime.fs rename to src/SqlClient.DesignTime/DesignTime.fs index e46968d2..91216354 100644 --- a/src/SqlClient/DesignTime.fs +++ b/src/SqlClient.DesignTime/DesignTime.fs @@ -12,28 +12,6 @@ open ProviderImplementation.ProvidedTypes open FSharp.Data open System.Text.RegularExpressions -module RuntimeInternals = - let setupTableFromSerializedColumns (serializedSchema: string) (table: System.Data.DataTable) = - let primaryKey = ResizeArray() - for line in serializedSchema.Split('\n') do - let xs = line.Split('\t') - let col = new DataColumn() - col.ColumnName <- xs.[0] - col.DataType <- Type.GetType( xs.[1], throwOnError = true) - col.AllowDBNull <- Boolean.Parse xs.[2] - if col.DataType = typeof - then - col.MaxLength <- int xs.[3] - col.ReadOnly <- Boolean.Parse xs.[4] - col.AutoIncrement <- Boolean.Parse xs.[5] - if Boolean.Parse xs.[6] - then - primaryKey.Add col - table.Columns.Add col - - table.PrimaryKey <- Array.ofSeq primaryKey - - type internal RowType = { Provided: Type ErasedTo: Type @@ -76,47 +54,6 @@ module Prefixes = let tempTable = "##SQLCOMMANDPROVIDER_" let tableVar = "@SQLCOMMANDPROVIDER_" -type TempTableLoader(fieldCount, items: obj seq) = - let enumerator = items.GetEnumerator() - - interface IDataReader with - member this.FieldCount: int = fieldCount - member this.Read(): bool = enumerator.MoveNext() - member this.GetValue(i: int): obj = - let row : obj[] = unbox enumerator.Current - row.[i] - member this.Dispose(): unit = () - - member __.Close(): unit = invalidOp "NotImplementedException" - member __.Depth: int = invalidOp "NotImplementedException" - member __.GetBoolean(_: int): bool = invalidOp "NotImplementedException" - member __.GetByte(_ : int): byte = invalidOp "NotImplementedException" - member __.GetBytes(_ : int, _ : int64, _ : byte [], _ : int, _ : int): int64 = invalidOp "NotImplementedException" - member __.GetChar(_ : int): char = invalidOp "NotImplementedException" - member __.GetChars(_ : int, _ : int64, _ : char [], _ : int, _ : int): int64 = invalidOp "NotImplementedException" - member __.GetData(_ : int): IDataReader = invalidOp "NotImplementedException" - member __.GetDataTypeName(_ : int): string = invalidOp "NotImplementedException" - member __.GetDateTime(_ : int): System.DateTime = invalidOp "NotImplementedException" - member __.GetDecimal(_ : int): decimal = invalidOp "NotImplementedException" - member __.GetDouble(_ : int): float = invalidOp "NotImplementedException" - member __.GetFieldType(_ : int): System.Type = invalidOp "NotImplementedException" - member __.GetFloat(_ : int): float32 = invalidOp "NotImplementedException" - member __.GetGuid(_ : int): System.Guid = invalidOp "NotImplementedException" - member __.GetInt16(_ : int): int16 = invalidOp "NotImplementedException" - member __.GetInt32(_ : int): int = invalidOp "NotImplementedException" - member __.GetInt64(_ : int): int64 = invalidOp "NotImplementedException" - member __.GetName(_ : int): string = invalidOp "NotImplementedException" - member __.GetOrdinal(_ : string): int = invalidOp "NotImplementedException" - member __.GetSchemaTable(): DataTable = invalidOp "NotImplementedException" - member __.GetString(_ : int): string = invalidOp "NotImplementedException" - member __.GetValues(_ : obj []): int = invalidOp "NotImplementedException" - member __.IsClosed: bool = invalidOp "NotImplementedException" - member __.IsDBNull(_ : int): bool = invalidOp "NotImplementedException" - member __.Item with get (_ : int): obj = invalidOp "NotImplementedException" - member __.Item with get (_ : string): obj = invalidOp "NotImplementedException" - member __.NextResult(): bool = invalidOp "NotImplementedException" - member __.RecordsAffected: int = invalidOp "NotImplementedException" - type DesignTime private() = static member internal AddGeneratedMethod (sqlParameters: Parameter list, hasOutputParameters, executeArgs: ProvidedParameter list, erasedType, providedOutputType, name) = @@ -163,7 +100,7 @@ type DesignTime private() = if sqlParam.Direction.HasFlag( ParameterDirection.Output) then let mi = - typeof + typeof .GetMethod("SetRef") .MakeGenericMethod( sqlParam.TypeInfo.ClrType) Expr.Call(mi, [ argExpr; Expr.Var arr; Expr.Value index ]) |> Some @@ -199,9 +136,6 @@ type DesignTime private() = m - static member SetRef<'t>(r : byref<'t>, arr: (string * obj)[], i) = - r <- arr.[i] |> snd |> unbox - static member internal GetRecordType(columns: Column list, ?unitsOfMeasurePerSchema) = columns @@ -432,7 +366,7 @@ type DesignTime private() = providedType, erasedToTupleType, mapping let nullsToOptions = QuotationsFactory.MapArrayNullableItems(outputColumns, "MapArrayObjItemToOption") - let combineWithNullsToOptions = typeof.GetMethod("GetMapperWithNullsToOptions") + let combineWithNullsToOptions = typeof.GetMethod("GetMapperWithNullsToOptions") { Single = diff --git a/src/SqlClient.DesignTime/DesignTimeConnectionString.fs b/src/SqlClient.DesignTime/DesignTimeConnectionString.fs new file mode 100644 index 00000000..45ab18df --- /dev/null +++ b/src/SqlClient.DesignTime/DesignTimeConnectionString.fs @@ -0,0 +1,72 @@ +namespace FSharp.Data.SqlClient + +open System.Configuration +open System.IO +open System +open System.Collections.Generic +open System.Diagnostics + +[] +type internal DesignTimeConnectionString = + | Literal of string + | NameInConfig of name: string * value: string * provider: string + + static member Parse(s: string, resolutionFolder, fileName) = + match s.Trim().Split([|'='|], 2, StringSplitOptions.RemoveEmptyEntries) with + | [| "" |] -> invalidArg "ConnectionStringOrName" "Value is empty!" + | [| prefix; tail |] when prefix.Trim().ToLower() = "name" -> + let name = tail.Trim() + let value, provider = DesignTimeConnectionString.ReadFromConfig( name, resolutionFolder, fileName) + NameInConfig( name, value, provider) + | _ -> + Literal s + + static member ReadFromConfig(name, resolutionFolder, fileName) = + let configFilename = + if fileName <> "" + then + let path = Path.Combine(resolutionFolder, fileName) + if not <| File.Exists path + then raise <| FileNotFoundException( sprintf "Could not find config file '%s'." path) + else path + else + let appConfig = Path.Combine(resolutionFolder, "app.config") + let webConfig = Path.Combine(resolutionFolder, "web.config") + + if File.Exists appConfig then appConfig + elif File.Exists webConfig then webConfig + else failwithf "Cannot find either app.config or web.config." + + let map = ExeConfigurationFileMap() + map.ExeConfigFilename <- configFilename + let configSection = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None).ConnectionStrings.ConnectionStrings + match configSection, lazy configSection.[name] with + | null, _ | _, Lazy null -> raise <| KeyNotFoundException(message = sprintf "Cannot find name %s in section of %s file." name configFilename) + | _, Lazy x -> + let providerName = if String.IsNullOrEmpty x.ProviderName then "System.Data.SqlClient" else x.ProviderName + x.ConnectionString, providerName + + member this.Value = + match this with + | Literal value -> value + | NameInConfig(_, value, _) -> value + + member this.RunTimeValueExpr isHostedExecution = + match this with + | Literal value -> <@@ value @@> + | NameInConfig(name, value, _) -> + <@@ + let hostProcess = Process.GetCurrentProcess().ProcessName.ToUpper() + if isHostedExecution + || (Environment.Is64BitProcess && hostProcess = "FSIANYCPU") + || (not Environment.Is64BitProcess && hostProcess = "FSI") + then + value + else + let section = ConfigurationManager.ConnectionStrings.[name] + if section = null + then raise <| KeyNotFoundException(message = sprintf "Cannot find name %s in section of config file." name) + else section.ConnectionString + @@> + + member this.IsDefinedByLiteral = match this with | Literal _ -> true | _ -> false diff --git a/src/SqlClient/ProvidedTypesCache.fs b/src/SqlClient.DesignTime/ProvidedTypesCache.fs similarity index 100% rename from src/SqlClient/ProvidedTypesCache.fs rename to src/SqlClient.DesignTime/ProvidedTypesCache.fs diff --git a/src/SqlClient/QuotationsFactory.fs b/src/SqlClient.DesignTime/QuotationsFactory.fs similarity index 94% rename from src/SqlClient/QuotationsFactory.fs rename to src/SqlClient.DesignTime/QuotationsFactory.fs index 5eb2ba7d..d31f2d68 100644 --- a/src/SqlClient/QuotationsFactory.fs +++ b/src/SqlClient.DesignTime/QuotationsFactory.fs @@ -50,7 +50,7 @@ type QuotationsFactory private() = x @@> - static member internal OptionToObj<'T> value = <@@ match %%value with Some (x : 'T) -> box x | None -> Extensions.DbNull @@> + static member internal OptionToObj<'T> value = <@@ match %%value with Some (x : 'T) -> box x | None -> DbNull @@> static member internal MapArrayOptionItemToObj<'T>(arr, index) = <@ @@ -103,11 +103,6 @@ type QuotationsFactory private() = (%%exprArgs.[0] : DataRow).[name] <- match (%%exprArgs.[1] : option<'T>) with None -> DbNull | Some value -> box value @> - static member GetMapperWithNullsToOptions(nullsToOptions, mapper: obj[] -> obj) = - fun values -> - nullsToOptions values - mapper values - static member private GetNonNullableValueFromDataRow<'T>(exprArgs : Expr list, name: string) = <@ (%%exprArgs.[0] : DataRow).[name] @> diff --git a/src/SqlClient/Scripts/ReverseLineOrderForNotex.fsx b/src/SqlClient.DesignTime/Scripts/ReverseLineOrderForNotex.fsx similarity index 100% rename from src/SqlClient/Scripts/ReverseLineOrderForNotex.fsx rename to src/SqlClient.DesignTime/Scripts/ReverseLineOrderForNotex.fsx diff --git a/src/SqlClient/Scripts/Scratchpad.fsx b/src/SqlClient.DesignTime/Scripts/Scratchpad.fsx similarity index 100% rename from src/SqlClient/Scripts/Scratchpad.fsx rename to src/SqlClient.DesignTime/Scripts/Scratchpad.fsx diff --git a/src/SqlClient/Scripts/XE.fsx b/src/SqlClient.DesignTime/Scripts/XE.fsx similarity index 100% rename from src/SqlClient/Scripts/XE.fsx rename to src/SqlClient.DesignTime/Scripts/XE.fsx diff --git a/src/SqlClient/SingleFileChangeMonitor.fs b/src/SqlClient.DesignTime/SingleFileChangeMonitor.fs similarity index 100% rename from src/SqlClient/SingleFileChangeMonitor.fs rename to src/SqlClient.DesignTime/SingleFileChangeMonitor.fs diff --git a/src/SqlClient/SingleRootTypeProvider.fs b/src/SqlClient.DesignTime/SingleRootTypeProvider.fs similarity index 86% rename from src/SqlClient/SingleRootTypeProvider.fs rename to src/SqlClient.DesignTime/SingleRootTypeProvider.fs index d784d421..a17d98e0 100644 --- a/src/SqlClient/SingleRootTypeProvider.fs +++ b/src/SqlClient.DesignTime/SingleRootTypeProvider.fs @@ -8,13 +8,13 @@ open System [] [] type SingleRootTypeProvider(config: TypeProviderConfig, providerName, parameters, ?isErased) as this = - inherit TypeProviderForNamespaces(config, addDefaultProbingLocation = true) + inherit TypeProviderForNamespaces (config, assemblyReplacementMap=[("FSharp.Data.SqlClient.DesignTime", "FSharp.Data.SqlClient")], addDefaultProbingLocation=true) let cache = new Cache() do let isErased = defaultArg isErased true let nameSpace = this.GetType().Namespace - let assembly = Assembly.LoadFrom( config.RuntimeAssembly) + let assembly = Assembly.GetExecutingAssembly() let providerType = ProvidedTypeDefinition(assembly, nameSpace, providerName, Some typeof, hideObjectMethods = true, isErased = isErased) diff --git a/src/SqlClient.DesignTime/SqlClient.DesignTime.fsproj b/src/SqlClient.DesignTime/SqlClient.DesignTime.fsproj new file mode 100644 index 00000000..6d443af9 --- /dev/null +++ b/src/SqlClient.DesignTime/SqlClient.DesignTime.fsproj @@ -0,0 +1,54 @@ + + + + net40 + FSharp.Data.SqlClient.DesignTime + false + true + ..\..\bin + 101 + + + + + + + + + + + + + + True + ProvidedTypes.fsi + + + True + ProvidedTypes.fs + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/SqlClient/SqlClientExtensions.fs b/src/SqlClient.DesignTime/SqlClientExtensions.fs similarity index 79% rename from src/SqlClient/SqlClientExtensions.fs rename to src/SqlClient.DesignTime/SqlClientExtensions.fs index 60b76d1f..9d37cfc0 100644 --- a/src/SqlClient/SqlClientExtensions.fs +++ b/src/SqlClient.DesignTime/SqlClientExtensions.fs @@ -2,156 +2,12 @@ module FSharp.Data.SqlClient.Extensions open System -open System.Text open System.Data open System.Collections.Generic open System.IO open System.Threading.Tasks open System.Data.SqlClient -open System.Reflection - -type SqlCommand with - member this.AsyncExecuteReader behavior = - Async.FromBeginEnd((fun(callback, state) -> this.BeginExecuteReader(callback, state, behavior)), this.EndExecuteReader) - - member this.AsyncExecuteNonQuery() = - Async.FromBeginEnd(this.BeginExecuteNonQuery, this.EndExecuteNonQuery) - - static member internal DefaultTimeout = (new SqlCommand()).CommandTimeout - - member internal this.ExecuteQuery mapper = - seq { - use cursor = this.ExecuteReader() - while cursor.Read() do - yield mapper cursor - } - -type SqlDataReader with - member internal this.MapRowValues<'TItem>( rowMapping) = - seq { - use _ = this - let values = Array.zeroCreate this.FieldCount - while this.Read() do - this.GetValues(values) |> ignore - yield values |> rowMapping |> unbox<'TItem> - } - -type SqlDataReader with - member internal this.TryGetValue(name: string) = - let value = this.[name] - if Convert.IsDBNull value then None else Some(unbox<'a> value) - member internal this.GetValueOrDefault<'a>(name: string, defaultValue) = - let value = this.[name] - if Convert.IsDBNull value then defaultValue else unbox<'a> value - -let DbNull = box DBNull.Value - -type Column = { - Name: string - TypeInfo: TypeInfo - Nullable: bool - MaxLength: int - ReadOnly: bool - Identity: bool - PartOfUniqueKey: bool - DefaultConstraint: string - Description: string -} with - - member this.ErasedToType = - if this.Nullable - then typedefof<_ option>.MakeGenericType this.TypeInfo.ClrType - else this.TypeInfo.ClrType - - member this.GetProvidedType(?unitsOfMeasurePerSchema: Dictionary) = - let typeConsideringUOM: Type = - if this.TypeInfo.IsUnitOfMeasure && unitsOfMeasurePerSchema.IsSome - then - assert(unitsOfMeasurePerSchema.IsSome) - let uomType = unitsOfMeasurePerSchema.Value.[this.TypeInfo.Schema] |> List.find (fun x -> x.Name = this.TypeInfo.UnitOfMeasureName) - ProviderImplementation.ProvidedTypes.ProvidedMeasureBuilder.AnnotateType(this.TypeInfo.ClrType, [ uomType ]) - else - this.TypeInfo.ClrType - - if this.Nullable - then - //ProviderImplementation.ProvidedTypes.ProvidedTypeBuilder.MakeGenericType(typedefof<_ option>, [ typeConsideringUOM ]) - typedefof<_ option>.MakeGenericType typeConsideringUOM - else - typeConsideringUOM - - member this.HasDefaultConstraint = this.DefaultConstraint <> "" - member this.NullableParameter = this.Nullable || this.HasDefaultConstraint - - static member Parse(cursor: SqlDataReader, typeLookup: int * int option -> TypeInfo, ?defaultValue, ?description) = { - Name = unbox cursor.["name"] - TypeInfo = - let system_type_id = unbox cursor.["system_type_id"] |> int - let user_type_id = cursor.TryGetValue "user_type_id" - typeLookup(system_type_id, user_type_id) - Nullable = unbox cursor.["is_nullable"] - MaxLength = cursor.["max_length"] |> unbox |> int - ReadOnly = not( cursor.GetValueOrDefault("is_updateable", false)) - Identity = cursor.GetValueOrDefault( "is_identity_column", false) - PartOfUniqueKey = unbox cursor.["is_part_of_unique_key"] - DefaultConstraint = defaultArg defaultValue "" - Description = defaultArg description "" - } - - override this.ToString() = - sprintf "%s\t%s\t%b\t%i\t%b\t%b\t%b\t%s\t%s" - this.Name - this.TypeInfo.ClrTypeFullName - this.Nullable - this.MaxLength - this.ReadOnly - this.Identity - this.PartOfUniqueKey - this.DefaultConstraint - this.Description - -and TypeInfo = { - TypeName: string - Schema: string - SqlEngineTypeId: int - UserTypeId: int - SqlDbType: SqlDbType - IsFixedLength: bool - ClrTypeFullName: string - UdttName: string - TableTypeColumns: Column[] Lazy -} with - member this.ClrType: Type = Type.GetType( this.ClrTypeFullName, throwOnError = true) - member this.TableType = this.SqlDbType = SqlDbType.Structured - member this.IsValueType = not this.TableType && this.ClrType.IsValueType - member this.IsUnitOfMeasure = this.TypeName.StartsWith("<") && this.TypeName.EndsWith(">") - member this.UnitOfMeasureName = this.TypeName.TrimStart('<').TrimEnd('>') - -type Parameter = { - Name: string - TypeInfo: TypeInfo - Direction: ParameterDirection - MaxLength: int - Precision: byte - Scale : byte - DefaultValue: obj option - Optional: bool - Description: string -} with - - member this.Size = - match this.TypeInfo.SqlDbType with - | SqlDbType.NChar | SqlDbType.NText | SqlDbType.NVarChar -> this.MaxLength / 2 - | _ -> this.MaxLength - - member this.GetProvidedType(?unitsOfMeasurePerSchema: Dictionary) = - if this.TypeInfo.IsUnitOfMeasure && unitsOfMeasurePerSchema.IsSome - then - assert(unitsOfMeasurePerSchema.IsSome) - let uomType = unitsOfMeasurePerSchema.Value.[this.TypeInfo.Schema] |> List.find (fun x -> x.Name = this.TypeInfo.UnitOfMeasureName) - ProviderImplementation.ProvidedTypes.ProvidedMeasureBuilder.AnnotateType(this.TypeInfo.ClrType, [ uomType ]) - else - this.TypeInfo.ClrType +open FSharp.Data let private dataTypeMappings = Dictionary() @@ -287,25 +143,13 @@ type SqlConnection with //address an issue when regular Dispose on SqlConnection needed for async computation //wipes out all properties like ConnectionString in addition to closing connection to db - member this.UseLocally(?privateConnection) = - if this.State = ConnectionState.Closed - && defaultArg privateConnection true - then - this.Open() - { new IDisposable with member __.Dispose() = this.Close() } - else { new IDisposable with member __.Dispose() = () } - + member internal this.CheckVersion() = assert (this.State = ConnectionState.Open) let majorVersion = this.ServerVersion.Split('.').[0] if int majorVersion < 11 then failwithf "Minimal supported major version is 11 (SQL Server 2012 and higher or Azure SQL Database). Currently used: %s" this.ServerVersion - member this.IsSqlAzure = - assert (this.State = ConnectionState.Open) - use cmd = new SqlCommand("SELECT SERVERPROPERTY('edition')", this) - cmd.ExecuteScalar().Equals("SQL Azure") - member internal this.GetUserSchemas() = use __ = this.UseLocally() use cmd = new SqlCommand("SELECT name FROM sys.schemas WHERE principal_id = 1", this) diff --git a/src/SqlClient/SqlClientProvider.fs b/src/SqlClient.DesignTime/SqlClientProvider.fs similarity index 99% rename from src/SqlClient/SqlClientProvider.fs rename to src/SqlClient.DesignTime/SqlClientProvider.fs index 3c883e72..4c18a3e8 100644 --- a/src/SqlClient/SqlClientProvider.fs +++ b/src/SqlClient.DesignTime/SqlClientProvider.fs @@ -17,9 +17,9 @@ open FSharp.Data.SqlClient [] [] type SqlProgrammabilityProvider(config : TypeProviderConfig) as this = - inherit TypeProviderForNamespaces(config, addDefaultProbingLocation = true) + inherit TypeProviderForNamespaces (config, assemblyReplacementMap=[("FSharp.Data.SqlClient.DesignTime", "FSharp.Data.SqlClient")], addDefaultProbingLocation=true) - let assembly = Assembly.LoadFrom( config.RuntimeAssembly) + let assembly = Assembly.GetExecutingAssembly() let nameSpace = this.GetType().Namespace let providerType = ProvidedTypeDefinition(assembly, nameSpace, "SqlProgrammabilityProvider", Some typeof, hideObjectMethods = true) @@ -159,7 +159,6 @@ type SqlProgrammabilityProvider(config : TypeProviderConfig) as this = let isSqlAzure = conn.IsSqlAzure let routines = conn.GetRoutines( schema, isSqlAzure) for routine in routines do - let cmdProvidedType = ProvidedTypeDefinition(snd routine.TwoPartName, Some typeof<``ISqlCommand Implementation``>, hideObjectMethods = true) do diff --git a/src/SqlClient/SqlCommandProvider.fs b/src/SqlClient.DesignTime/SqlCommandProvider.fs similarity index 97% rename from src/SqlClient/SqlCommandProvider.fs rename to src/SqlClient.DesignTime/SqlCommandProvider.fs index febb502a..cdc4229a 100644 --- a/src/SqlClient/SqlCommandProvider.fs +++ b/src/SqlClient.DesignTime/SqlCommandProvider.fs @@ -5,7 +5,6 @@ open System.IO open System.Data.SqlClient open System.Reflection open System.Runtime.CompilerServices - open Microsoft.FSharp.Core.CompilerServices open Microsoft.FSharp.Quotations @@ -14,9 +13,7 @@ open FSharp.Data.SqlClient open ProviderImplementation.ProvidedTypes [] -#if DEBUG -[] -#endif +[] do() module X = @@ -26,10 +23,10 @@ module X = [] [] type SqlCommandProvider(config : TypeProviderConfig) as this = - inherit TypeProviderForNamespaces(config, addDefaultProbingLocation = true) - + inherit TypeProviderForNamespaces (config, assemblyReplacementMap=[("FSharp.Data.SqlClient.DesignTime", "FSharp.Data.SqlClient")], addDefaultProbingLocation=true) + let nameSpace = this.GetType().Namespace - let assembly = Assembly.LoadFrom( config.RuntimeAssembly) + let assembly = Assembly.GetExecutingAssembly() let providerType = ProvidedTypeDefinition(assembly, nameSpace, "SqlCommandProvider", Some typeof, hideObjectMethods = true) let cache = new Cache() diff --git a/src/SqlClient/SqlEnumProvider.fs b/src/SqlClient.DesignTime/SqlEnumProvider.fs similarity index 98% rename from src/SqlClient/SqlEnumProvider.fs rename to src/SqlClient.DesignTime/SqlEnumProvider.fs index 28d8b769..7749d44b 100644 --- a/src/SqlClient/SqlEnumProvider.fs +++ b/src/SqlClient.DesignTime/SqlEnumProvider.fs @@ -14,15 +14,10 @@ open ProviderImplementation.ProvidedTypes open FSharp.Data.SqlClient -type SqlEnumKind = - | Default = 0 - | CLI = 1 - | UnitsOfMeasure = 2 - [] [] type SqlEnumProvider(config : TypeProviderConfig) as this = - inherit TypeProviderForNamespaces(config, addDefaultProbingLocation = true) + inherit TypeProviderForNamespaces (config, assemblyReplacementMap=[("FSharp.Data.SqlClient.DesignTime", "FSharp.Data.SqlClient")], addDefaultProbingLocation=true) let nameSpace = this.GetType().Namespace let assembly = Assembly.GetExecutingAssembly() diff --git a/src/SqlClient/SqlFileProvider.fs b/src/SqlClient.DesignTime/SqlFileProvider.fs similarity index 100% rename from src/SqlClient/SqlFileProvider.fs rename to src/SqlClient.DesignTime/SqlFileProvider.fs diff --git a/src/SqlClient.DesignTime/paket.references b/src/SqlClient.DesignTime/paket.references new file mode 100644 index 00000000..fb34ddd2 --- /dev/null +++ b/src/SqlClient.DesignTime/paket.references @@ -0,0 +1,6 @@ +FSharp.Core +Microsoft.SqlServer.TransactSql.ScriptDom +Microsoft.SqlServer.Types + +File: ProvidedTypes.fsi . +File: ProvidedTypes.fs . diff --git a/src/SqlClient.Samples/WebApi.Controllers/WebApi.Controllers.fsproj b/src/SqlClient.Samples/WebApi.Controllers/WebApi.Controllers.fsproj index bb6e2c8a..52c9c59d 100644 --- a/src/SqlClient.Samples/WebApi.Controllers/WebApi.Controllers.fsproj +++ b/src/SqlClient.Samples/WebApi.Controllers/WebApi.Controllers.fsproj @@ -79,16 +79,7 @@ --> - - - - ..\..\..\packages\FSharp.Core\lib\net40\FSharp.Core.dll - True - True - - - - + ..\..\..\packages\FSharp.Core\lib\net45\FSharp.Core.dll @@ -408,6 +399,7 @@ + ..\..\..\packages\samples\FSharp.Data.SqlClient\lib\net40\FSharp.Data.SqlClient.dll @@ -418,7 +410,7 @@ - + ..\..\..\packages\samples\Microsoft.AspNet.WebApi.Client\lib\net45\System.Net.Http.Formatting.dll @@ -429,7 +421,7 @@ - + ..\..\..\packages\samples\Microsoft.AspNet.WebApi.Core\lib\net45\System.Web.Http.dll @@ -440,16 +432,7 @@ - - - - ..\..\..\packages\samples\Newtonsoft.Json\lib\net40\Newtonsoft.Json.dll - True - True - - - - + ..\..\..\packages\samples\Newtonsoft.Json\lib\net45\Newtonsoft.Json.dll diff --git a/src/SqlClient.Samples/WebApi/web.config b/src/SqlClient.Samples/WebApi/web.config index 130b4170..4f7ef217 100644 --- a/src/SqlClient.Samples/WebApi/web.config +++ b/src/SqlClient.Samples/WebApi/web.config @@ -24,21 +24,6 @@ - - True - - - - - True - - - - - True - - - True diff --git a/src/SqlClient.Samples/WpfDataBinding/App.config b/src/SqlClient.Samples/WpfDataBinding/App.config index 97970b57..ce1f79db 100644 --- a/src/SqlClient.Samples/WpfDataBinding/App.config +++ b/src/SqlClient.Samples/WpfDataBinding/App.config @@ -23,19 +23,4 @@ - - True - - - - - True - - - - - True - - - \ No newline at end of file diff --git a/src/SqlClient.Tests/ConfigurationTest.fs b/src/SqlClient.Tests/ConfigurationTest.fs index c416e1b6..be3fb469 100644 --- a/src/SqlClient.Tests/ConfigurationTest.fs +++ b/src/SqlClient.Tests/ConfigurationTest.fs @@ -1,12 +1,6 @@ module FSharp.Data.SqlClient.ConfigurationTests -#if DEBUG -#nowarn "101" -#endif - open Xunit -open System.Configuration -open System.IO open FSharp.Data let adventureWorks = FSharp.Configuration.AppSettings<"app.config">.ConnectionStrings.AdventureWorks @@ -26,30 +20,3 @@ let SqlFiles() = use cmd1 = new SqlCommandProvider() use cmd2 = new SqlCommandProvider() Assert.Equal<_ seq>(cmd1.Execute() |> Seq.toArray, cmd2.Execute() |> Seq.toArray) - -#if DEBUG - -[] -let ``Wrong config file name`` () = - Assert.Throws( - fun() -> - DesignTimeConnectionString.Parse("name=AdventureWorks", resolutionFolder = "", fileName = "non_existent") |> box - ) |> ignore - -[] -let ``From config file`` () = - let x = DesignTimeConnectionString.Parse("name=AdventureWorks", __SOURCE_DIRECTORY__, "app.config") - match x with - | NameInConfig(name, value, _) -> - Assert.Equal("AdventureWorks", name) - Assert.Equal(adventureWorks, value) - | _ -> failwith "Unexpected" - -[] -let RuntimeConfig() = - let x = DesignTimeConnectionString.Parse("name=AdventureWorks", __SOURCE_DIRECTORY__, "app.config") - let actual = Linq.RuntimeHelpers.LeafExpressionConverter.EvaluateQuotation( x.RunTimeValueExpr(isHostedExecution = false)) |> unbox - Assert.Equal( adventureWorks, actual) - -#endif - diff --git a/src/SqlClient.Tests/SpatialTypesTests.fs b/src/SqlClient.Tests/SpatialTypesTests.fs index 38260a5e..c26af29c 100644 --- a/src/SqlClient.Tests/SpatialTypesTests.fs +++ b/src/SqlClient.Tests/SpatialTypesTests.fs @@ -8,6 +8,7 @@ open System.Data.SqlTypes let connectionString = ConnectionStrings.AdventureWorksNamed type GetEmployeeByLevel = SqlCommandProvider<"SELECT OrganizationNode FROM HumanResources.Employee WHERE OrganizationNode = @OrganizationNode", connectionString, SingleRow = true> + [] let SqlHierarchyIdParam() = let getEmployeeByLevel = new GetEmployeeByLevel() diff --git a/src/SqlClient.Tests/SqlClient.DesignTime.Tests/DesignTimeConnectionStringTests.fs b/src/SqlClient.Tests/SqlClient.DesignTime.Tests/DesignTimeConnectionStringTests.fs new file mode 100644 index 00000000..eaaab4c8 --- /dev/null +++ b/src/SqlClient.Tests/SqlClient.DesignTime.Tests/DesignTimeConnectionStringTests.fs @@ -0,0 +1,31 @@ +module DesignTimeConnectionStringTests + +open Xunit +open System.IO +open System.Configuration +open FSharp.Data.Configuration +open FSharp.Data.SqlClient + +let adventureWorks = FSharp.Configuration.AppSettings<"app.config">.ConnectionStrings.AdventureWorks + +[] +let ``Wrong config file name`` () = + Assert.Throws( + fun() -> + DesignTimeConnectionString.Parse("name=AdventureWorks", resolutionFolder = "", fileName = "non_existent") |> box + ) |> ignore + +[] +let ``From config file`` () = + let x = DesignTimeConnectionString.Parse("name=AdventureWorks", __SOURCE_DIRECTORY__, "app.config") + match x with + | NameInConfig(name, value, _) -> + Assert.Equal("AdventureWorks", name) + Assert.Equal(adventureWorks, value) + | _ -> failwith "Unexpected" + +[] +let RuntimeConfig() = + let x = DesignTimeConnectionString.Parse("name=AdventureWorks", __SOURCE_DIRECTORY__, "app.config") + let actual = Linq.RuntimeHelpers.LeafExpressionConverter.EvaluateQuotation( x.RunTimeValueExpr(isHostedExecution = false)) |> unbox + Assert.Equal( adventureWorks, actual) diff --git a/src/SqlClient.Tests/SqlClient.DesignTime.Tests/SqlClient.DesignTime.Tests.fsproj b/src/SqlClient.Tests/SqlClient.DesignTime.Tests/SqlClient.DesignTime.Tests.fsproj new file mode 100644 index 00000000..a09bed1a --- /dev/null +++ b/src/SqlClient.Tests/SqlClient.DesignTime.Tests/SqlClient.DesignTime.Tests.fsproj @@ -0,0 +1,26 @@ + + + + net451 + SqlClient.DesignTime.Tests + false + true + 101 + + + + + + + + + ..\..\..\bin\net40\FSharp.Data.SqlClient.DesignTime.dll + + + + + + + + + \ No newline at end of file diff --git a/src/SqlClient.Tests/SqlClient.DesignTime.Tests/app.config b/src/SqlClient.Tests/SqlClient.DesignTime.Tests/app.config new file mode 100644 index 00000000..678ee047 --- /dev/null +++ b/src/SqlClient.Tests/SqlClient.DesignTime.Tests/app.config @@ -0,0 +1,68 @@ + + + +
+ + + + + + True + + + + + True + + + + + True + + + + + True + + + + + True + + + + + True + + + + + True + + + + + True + + + + + True + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/SqlClient.Tests/SqlClient.DesignTime.Tests/paket.references b/src/SqlClient.Tests/SqlClient.DesignTime.Tests/paket.references new file mode 100644 index 00000000..5879d670 --- /dev/null +++ b/src/SqlClient.Tests/SqlClient.DesignTime.Tests/paket.references @@ -0,0 +1,6 @@ +FSharp.Core + +group Test + xunit + xunit.runner.visualstudio + FSharp.Configuration diff --git a/src/SqlClient.Tests/SqlClient.Tests.NET40/Program.fs b/src/SqlClient.Tests/SqlClient.Tests.NET40/Program.fs index d4c7eef3..6e2a4c0e 100644 --- a/src/SqlClient.Tests/SqlClient.Tests.NET40/Program.fs +++ b/src/SqlClient.Tests/SqlClient.Tests.NET40/Program.fs @@ -1,6 +1,6 @@ - + open FSharp.Data let get42 = new SqlCommandProvider<"SELECT 42", "Server=.;Integrated Security=True">("Server=.;Integrated Security=True") -get42.Execute() |> Seq.toArray |> printfn "%A" +get42.Execute() |> Seq.toArray |> printfn "%A" diff --git a/src/SqlClient.Tests/SqlClient.Tests.NET40/SqlClient.Tests.NET40.fsproj b/src/SqlClient.Tests/SqlClient.Tests.NET40/SqlClient.Tests.NET40.fsproj index 1fbfc94a..35712b20 100644 --- a/src/SqlClient.Tests/SqlClient.Tests.NET40/SqlClient.Tests.NET40.fsproj +++ b/src/SqlClient.Tests/SqlClient.Tests.NET40/SqlClient.Tests.NET40.fsproj @@ -1,8 +1,9 @@ - + Exe net40 + net451 SqlClient.Tests.NET40 false true diff --git a/src/SqlClient.Tests/SqlClient.Tests.NET40/Uncomment.App.config b/src/SqlClient.Tests/SqlClient.Tests.NET40/Uncomment.App.config index 64324f5a..89c477f1 100644 --- a/src/SqlClient.Tests/SqlClient.Tests.NET40/Uncomment.App.config +++ b/src/SqlClient.Tests/SqlClient.Tests.NET40/Uncomment.App.config @@ -1,4 +1,4 @@ - + @@ -12,4 +12,4 @@ - + \ No newline at end of file diff --git a/src/SqlClient.Tests/SqlClient.Tests.NET40/paket.references b/src/SqlClient.Tests/SqlClient.Tests.NET40/paket.references index 640cf914..6f627f42 100644 --- a/src/SqlClient.Tests/SqlClient.Tests.NET40/paket.references +++ b/src/SqlClient.Tests/SqlClient.Tests.NET40/paket.references @@ -1 +1 @@ -FSharp.Core \ No newline at end of file +FSharp.Core diff --git a/src/SqlClient.Tests/TransactionTests.fs b/src/SqlClient.Tests/TransactionTests.fs index 18aa7157..12eaad4e 100644 --- a/src/SqlClient.Tests/TransactionTests.fs +++ b/src/SqlClient.Tests/TransactionTests.fs @@ -1,9 +1,5 @@ module FSharp.Data.TransactionTests -#if DEBUG -#nowarn "101" -#endif - open System open System.Data open System.Transactions diff --git a/src/SqlClient.Tests/TypeProviderTest.fs b/src/SqlClient.Tests/TypeProviderTest.fs index 0a274000..34be83bc 100644 --- a/src/SqlClient.Tests/TypeProviderTest.fs +++ b/src/SqlClient.Tests/TypeProviderTest.fs @@ -1,9 +1,5 @@ module FSharp.Data.TypeProviderTest -#if DEBUG -#nowarn "101" -#endif - open System open System.Data open System.Data.SqlClient diff --git a/src/SqlClient.Tests/app.config b/src/SqlClient.Tests/app.config index 89c373c5..5b68fa48 100644 --- a/src/SqlClient.Tests/app.config +++ b/src/SqlClient.Tests/app.config @@ -6,11 +6,6 @@ - - True - - - True @@ -36,6 +31,11 @@ + + True + + + True diff --git a/src/SqlClient/Configuration.fs b/src/SqlClient/Configuration.fs index afb821b2..236a2ee0 100644 --- a/src/SqlClient/Configuration.fs +++ b/src/SqlClient/Configuration.fs @@ -1,90 +1,5 @@ -namespace FSharp.Data - -///Enum describing output type -type ResultType = -///Sequence of custom records with properties matching column names and types - | Records = 0 -///Sequence of tuples matching column types with the same order - | Tuples = 1 -///Typed DataTable - | DataTable = 2 -///raw DataReader - | DataReader = 3 - namespace FSharp.Data.SqlClient -open System.Configuration -open System.IO -open System -open System.Threading.Tasks -open System.Collections.Generic -open System.Diagnostics - -[] -type internal DesignTimeConnectionString = - | Literal of string - | NameInConfig of name: string * value: string * provider: string - - static member Parse(s: string, resolutionFolder, fileName) = - match s.Trim().Split([|'='|], 2, StringSplitOptions.RemoveEmptyEntries) with - | [| "" |] -> invalidArg "ConnectionStringOrName" "Value is empty!" - | [| prefix; tail |] when prefix.Trim().ToLower() = "name" -> - let name = tail.Trim() - let value, provider = DesignTimeConnectionString.ReadFromConfig( name, resolutionFolder, fileName) - NameInConfig( name, value, provider) - | _ -> - Literal s - - static member ReadFromConfig(name, resolutionFolder, fileName) = - let configFilename = - if fileName <> "" - then - let path = Path.Combine(resolutionFolder, fileName) - if not <| File.Exists path - then raise <| FileNotFoundException( sprintf "Could not find config file '%s'." path) - else path - else - let appConfig = Path.Combine(resolutionFolder, "app.config") - let webConfig = Path.Combine(resolutionFolder, "web.config") - - if File.Exists appConfig then appConfig - elif File.Exists webConfig then webConfig - else failwithf "Cannot find either app.config or web.config." - - let map = ExeConfigurationFileMap() - map.ExeConfigFilename <- configFilename - let configSection = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None).ConnectionStrings.ConnectionStrings - match configSection, lazy configSection.[name] with - | null, _ | _, Lazy null -> raise <| KeyNotFoundException(message = sprintf "Cannot find name %s in section of %s file." name configFilename) - | _, Lazy x -> - let providerName = if String.IsNullOrEmpty x.ProviderName then "System.Data.SqlClient" else x.ProviderName - x.ConnectionString, providerName - - member this.Value = - match this with - | Literal value -> value - | NameInConfig(_, value, _) -> value - - member this.RunTimeValueExpr isHostedExecution = - match this with - | Literal value -> <@@ value @@> - | NameInConfig(name, value, _) -> - <@@ - let hostProcess = Process.GetCurrentProcess().ProcessName.ToUpper() - if isHostedExecution - || (Environment.Is64BitProcess && hostProcess = "FSIANYCPU") - || (not Environment.Is64BitProcess && hostProcess = "FSI") - then - value - else - let section = ConfigurationManager.ConnectionStrings.[name] - if section = null - then raise <| KeyNotFoundException(message = sprintf "Cannot find name %s in section of config file." name) - else section.ConnectionString - @@> - - member this.IsDefinedByLiteral = match this with | Literal _ -> true | _ -> false - //this is mess. Clean up later. type Configuration = { ResultsetRuntimeVerification: bool diff --git a/src/SqlClient/DataTable.fs b/src/SqlClient/DataTable.fs index 646bed4a..d3540a74 100644 --- a/src/SqlClient/DataTable.fs +++ b/src/SqlClient/DataTable.fs @@ -4,7 +4,6 @@ open System open System.Data open System.Data.SqlClient open System.Collections.Generic -open FSharp.Data.SqlClient [] [] diff --git a/src/SqlClient/Extensions.fs b/src/SqlClient/Extensions.fs new file mode 100644 index 00000000..971ce706 --- /dev/null +++ b/src/SqlClient/Extensions.fs @@ -0,0 +1,63 @@ +namespace FSharp.Data + +open System +open System.Data +open System.Data.SqlClient + +[] +module Extensions = + + type SqlDataReader with + member internal this.MapRowValues<'TItem>( rowMapping) = + seq { + use _ = this + let values = Array.zeroCreate this.FieldCount + while this.Read() do + this.GetValues(values) |> ignore + yield values |> rowMapping |> unbox<'TItem> + } + + type SqlDataReader with + member internal this.TryGetValue(name: string) = + let value = this.[name] + if Convert.IsDBNull value then None else Some(unbox<'a> value) + member internal this.GetValueOrDefault<'a>(name: string, defaultValue) = + let value = this.[name] + if Convert.IsDBNull value then defaultValue else unbox<'a> value + + type SqlCommand with + member this.AsyncExecuteReader (behavior:CommandBehavior) = + Async.FromBeginEnd((fun(callback, state) -> this.BeginExecuteReader(callback, state, behavior)), this.EndExecuteReader) + // Async.AwaitTask(this.ExecuteReaderAsync(behavior)) + // can change back when we move to netstandard? - or possibly an #if here? + + member this.AsyncExecuteNonQuery() = + Async.FromBeginEnd(this.BeginExecuteNonQuery, this.EndExecuteNonQuery) + //Async.AwaitTask(this.ExecuteNonQueryAsync()) + // see above + + static member internal DefaultTimeout = (new SqlCommand()).CommandTimeout + + member internal this.ExecuteQuery mapper = + seq { + use cursor = this.ExecuteReader() + while cursor.Read() do + yield mapper cursor + } + + type SqlConnection with + + //address an issue when regular Dispose on SqlConnection needed for async computation + //wipes out all properties like ConnectionString in addition to closing connection to db + member this.UseLocally(?privateConnection) = + if this.State = ConnectionState.Closed + && defaultArg privateConnection true + then + this.Open() + { new IDisposable with member __.Dispose() = this.Close() } + else { new IDisposable with member __.Dispose() = () } + + member this.IsSqlAzure = + assert (this.State = ConnectionState.Open) + use cmd = new SqlCommand("SELECT SERVERPROPERTY('edition')", this) + cmd.ExecuteScalar().Equals("SQL Azure") diff --git a/src/SqlClient/ISqlCommand.fs b/src/SqlClient/ISqlCommand.fs index 357f7141..475bc750 100644 --- a/src/SqlClient/ISqlCommand.fs +++ b/src/SqlClient/ISqlCommand.fs @@ -4,12 +4,11 @@ open System open System.Data open System.Data.SqlClient open System.Reflection -open System.Configuration -open System.Collections.Specialized open FSharp.Data.SqlClient open System.Linq + [] type ISqlCommand = @@ -30,13 +29,6 @@ module Seq = | [| x |] -> Some x | _ -> invalidArg "source" "The input sequence contains more than one element." -[] -[] -type ResultRank = - | Sequence = 0 - | SingleRow = 1 - | ScalarValue = 2 - [] type RowMapping = obj[] -> obj @@ -57,7 +49,6 @@ type internal Connection = Choice [] type ``ISqlCommand Implementation``(cfg: DesignTimeConfig, connection: Connection, commandTimeout) = - let cmd = new SqlCommand(cfg.SqlStatement, CommandTimeout = commandTimeout) let manageConnection = match connection with @@ -205,7 +196,7 @@ type ``ISqlCommand Implementation``(cfg: DesignTimeConfig, connection: Connectio elif p.Direction.HasFlag(ParameterDirection.Output) && value :? Array then p.Size <- (value :?> Array).Length -//Execute/AsyncExecute versions + //Execute/AsyncExecute versions static member internal VerifyResultsetColumns(cursor: SqlDataReader, expected) = if Configuration.Current.ResultsetRuntimeVerification @@ -333,6 +324,3 @@ type ``ISqlCommand Implementation``(cfg: DesignTimeConfig, connection: Connectio use _ = cmd.Connection.UseLocally(manageConnection ) return! cmd.AsyncExecuteNonQuery() } - - - diff --git a/src/SqlClient/Runtime.fs b/src/SqlClient/Runtime.fs new file mode 100644 index 00000000..622df2df --- /dev/null +++ b/src/SqlClient/Runtime.fs @@ -0,0 +1,7 @@ +namespace FSharp.Data + +#if !IS_DESIGNTIME +// Put the TypeProviderAssemblyAttribute in the runtime DLL, pointing to the design-time DLL +[] +do () +#endif \ No newline at end of file diff --git a/src/SqlClient/Shared.fs b/src/SqlClient/Shared.fs new file mode 100644 index 00000000..bfa92845 --- /dev/null +++ b/src/SqlClient/Shared.fs @@ -0,0 +1,210 @@ +namespace FSharp.Data + +open System +open System.Data +open System.Collections.Generic +open System.Data.SqlClient + +///Enum describing output type +type ResultType = +///Sequence of custom records with properties matching column names and types + | Records = 0 +///Sequence of tuples matching column types with the same order + | Tuples = 1 +///Typed DataTable + | DataTable = 2 +///raw DataReader + | DataReader = 3 + +type SqlEnumKind = +| Default = 0 +| CLI = 1 +| UnitsOfMeasure = 2 + +[] +[] +type ResultRank = + | Sequence = 0 + | SingleRow = 1 + | ScalarValue = 2 + +type Mapper private() = + static member GetMapperWithNullsToOptions(nullsToOptions, mapper: obj[] -> obj) = + fun values -> + nullsToOptions values + mapper values + + static member SetRef<'t>(r : byref<'t>, arr: (string * obj)[], i) = + r <- arr.[i] |> snd |> unbox + +type Column = { + Name: string + TypeInfo: TypeInfo + Nullable: bool + MaxLength: int + ReadOnly: bool + Identity: bool + PartOfUniqueKey: bool + DefaultConstraint: string + Description: string +} with + member this.ErasedToType = + if this.Nullable + then typedefof<_ option>.MakeGenericType this.TypeInfo.ClrType + else this.TypeInfo.ClrType + + member this.GetProvidedType(?unitsOfMeasurePerSchema: Dictionary) = + let typeConsideringUOM: Type = + if this.TypeInfo.IsUnitOfMeasure && unitsOfMeasurePerSchema.IsSome + then + assert(unitsOfMeasurePerSchema.IsSome) + let uomType = unitsOfMeasurePerSchema.Value.[this.TypeInfo.Schema] |> List.find (fun x -> x.Name = this.TypeInfo.UnitOfMeasureName) + ProviderImplementation.ProvidedTypes.ProvidedMeasureBuilder.AnnotateType(this.TypeInfo.ClrType, [ uomType ]) + else + this.TypeInfo.ClrType + + if this.Nullable + then + //ProviderImplementation.ProvidedTypes.ProvidedTypeBuilder.MakeGenericType(typedefof<_ option>, [ typeConsideringUOM ]) + typedefof<_ option>.MakeGenericType typeConsideringUOM + else + typeConsideringUOM + + member this.HasDefaultConstraint = this.DefaultConstraint <> "" + member this.NullableParameter = this.Nullable || this.HasDefaultConstraint + + static member Parse(cursor: SqlDataReader, typeLookup: int * int option -> TypeInfo, ?defaultValue, ?description) = { + Name = unbox cursor.["name"] + TypeInfo = + let system_type_id = unbox cursor.["system_type_id"] |> int + let user_type_id = cursor.TryGetValue "user_type_id" + typeLookup(system_type_id, user_type_id) + Nullable = unbox cursor.["is_nullable"] + MaxLength = cursor.["max_length"] |> unbox |> int + ReadOnly = not( cursor.GetValueOrDefault("is_updateable", false)) + Identity = cursor.GetValueOrDefault( "is_identity_column", false) + PartOfUniqueKey = unbox cursor.["is_part_of_unique_key"] + DefaultConstraint = defaultArg defaultValue "" + Description = defaultArg description "" + } + + override this.ToString() = + sprintf "%s\t%s\t%b\t%i\t%b\t%b\t%b\t%s\t%s" + this.Name + this.TypeInfo.ClrTypeFullName + this.Nullable + this.MaxLength + this.ReadOnly + this.Identity + this.PartOfUniqueKey + this.DefaultConstraint + this.Description + +and TypeInfo = { + TypeName: string + Schema: string + SqlEngineTypeId: int + UserTypeId: int + SqlDbType: SqlDbType + IsFixedLength: bool + ClrTypeFullName: string + UdttName: string + TableTypeColumns: Column[] Lazy +} with + member this.ClrType: Type = Type.GetType( this.ClrTypeFullName, throwOnError = true) + member this.TableType = this.SqlDbType = SqlDbType.Structured + member this.IsValueType = not this.TableType && this.ClrType.IsValueType + member this.IsUnitOfMeasure = this.TypeName.StartsWith("<") && this.TypeName.EndsWith(">") + member this.UnitOfMeasureName = this.TypeName.TrimStart('<').TrimEnd('>') + +type Parameter = { + Name: string + TypeInfo: TypeInfo + Direction: ParameterDirection + MaxLength: int + Precision: byte + Scale : byte + DefaultValue: obj option + Optional: bool + Description: string +} with + + member this.Size = + match this.TypeInfo.SqlDbType with + | SqlDbType.NChar | SqlDbType.NText | SqlDbType.NVarChar -> this.MaxLength / 2 + | _ -> this.MaxLength + + member this.GetProvidedType(?unitsOfMeasurePerSchema: Dictionary) = + if this.TypeInfo.IsUnitOfMeasure && unitsOfMeasurePerSchema.IsSome + then + assert(unitsOfMeasurePerSchema.IsSome) + let uomType = unitsOfMeasurePerSchema.Value.[this.TypeInfo.Schema] |> List.find (fun x -> x.Name = this.TypeInfo.UnitOfMeasureName) + ProviderImplementation.ProvidedTypes.ProvidedMeasureBuilder.AnnotateType(this.TypeInfo.ClrType, [ uomType ]) + else + this.TypeInfo.ClrType + +type TempTableLoader(fieldCount, items: obj seq) = + let enumerator = items.GetEnumerator() + + interface IDataReader with + member this.FieldCount: int = fieldCount + member this.Read(): bool = enumerator.MoveNext() + member this.GetValue(i: int): obj = + let row : obj[] = unbox enumerator.Current + row.[i] + member this.Dispose(): unit = () + + member __.Close(): unit = invalidOp "NotImplementedException" + member __.Depth: int = invalidOp "NotImplementedException" + member __.GetBoolean(_: int): bool = invalidOp "NotImplementedException" + member __.GetByte(_ : int): byte = invalidOp "NotImplementedException" + member __.GetBytes(_ : int, _ : int64, _ : byte [], _ : int, _ : int): int64 = invalidOp "NotImplementedException" + member __.GetChar(_ : int): char = invalidOp "NotImplementedException" + member __.GetChars(_ : int, _ : int64, _ : char [], _ : int, _ : int): int64 = invalidOp "NotImplementedException" + member __.GetData(_ : int): IDataReader = invalidOp "NotImplementedException" + member __.GetDataTypeName(_ : int): string = invalidOp "NotImplementedException" + member __.GetDateTime(_ : int): System.DateTime = invalidOp "NotImplementedException" + member __.GetDecimal(_ : int): decimal = invalidOp "NotImplementedException" + member __.GetDouble(_ : int): float = invalidOp "NotImplementedException" + member __.GetFieldType(_ : int): System.Type = invalidOp "NotImplementedException" + member __.GetFloat(_ : int): float32 = invalidOp "NotImplementedException" + member __.GetGuid(_ : int): System.Guid = invalidOp "NotImplementedException" + member __.GetInt16(_ : int): int16 = invalidOp "NotImplementedException" + member __.GetInt32(_ : int): int = invalidOp "NotImplementedException" + member __.GetInt64(_ : int): int64 = invalidOp "NotImplementedException" + member __.GetName(_ : int): string = invalidOp "NotImplementedException" + member __.GetOrdinal(_ : string): int = invalidOp "NotImplementedException" + member __.GetSchemaTable(): DataTable = invalidOp "NotImplementedException" + member __.GetString(_ : int): string = invalidOp "NotImplementedException" + member __.GetValues(_ : obj []): int = invalidOp "NotImplementedException" + member __.IsClosed: bool = invalidOp "NotImplementedException" + member __.IsDBNull(_ : int): bool = invalidOp "NotImplementedException" + member __.Item with get (_ : int): obj = invalidOp "NotImplementedException" + member __.Item with get (_ : string): obj = invalidOp "NotImplementedException" + member __.NextResult(): bool = invalidOp "NotImplementedException" + member __.RecordsAffected: int = invalidOp "NotImplementedException" + +module RuntimeInternals = + let setupTableFromSerializedColumns (serializedSchema: string) (table: System.Data.DataTable) = + let primaryKey = ResizeArray() + for line in serializedSchema.Split('\n') do + let xs = line.Split('\t') + let col = new DataColumn() + col.ColumnName <- xs.[0] + col.DataType <- Type.GetType( xs.[1], throwOnError = true) + col.AllowDBNull <- Boolean.Parse xs.[2] + if col.DataType = typeof + then + col.MaxLength <- int xs.[3] + col.ReadOnly <- Boolean.Parse xs.[4] + col.AutoIncrement <- Boolean.Parse xs.[5] + if Boolean.Parse xs.[6] + then + primaryKey.Add col + table.Columns.Add col + + table.PrimaryKey <- Array.ofSeq primaryKey + +[] +module Shared = + let DbNull = box DBNull.Value diff --git a/src/SqlClient/SqlClient.fsproj b/src/SqlClient/SqlClient.fsproj index 14b93d92..1fa348c4 100644 --- a/src/SqlClient/SqlClient.fsproj +++ b/src/SqlClient/SqlClient.fsproj @@ -1,7 +1,7 @@  - net40 + net40 FSharp.Data.SqlClient false true @@ -16,46 +16,26 @@ ..\..\bin\net40\FSharp.Data.SqlClient.XML - - - - - - - True - ProvidedTypesTesting.fs - True - ProvidedTypes.fsi + paket-files/ProvidedTypes.fsi True - ProvidedTypes.fs + paket-files/ProvidedTypes.fs - + + + - - - + - - - - - - - - - - - - + \ No newline at end of file diff --git a/src/SqlClient/paket.references b/src/SqlClient/paket.references index 55d67ada..5aa5e55d 100644 --- a/src/SqlClient/paket.references +++ b/src/SqlClient/paket.references @@ -1,7 +1,4 @@ -FSharp.Core -Microsoft.SqlServer.TransactSql.ScriptDom -Microsoft.SqlServer.Types +FSharp.Core -File: ProvidedTypes.fsi . -File: ProvidedTypes.fs . -File: ProvidedTypesTesting.fs . \ No newline at end of file +File:ProvidedTypes.fsi +File:ProvidedTypes.fs