├── .editorconfig
├── .gitignore
├── Build
├── BuildNuspecs.ps1
├── Connection.png
├── FailedConnection.png
├── Pack.cmd
├── README.md
├── SetVersion.ps1
├── azure-pipelines.yml
├── header.xml
├── icon.png
├── linq2db.LINQPad.nuspec
└── nuget-vars.yml
├── Directory.Build.props
├── Directory.Packages.props
├── MIT-LICENSE.txt
├── README.md
├── Source
├── BannedSymbols.txt
├── Compat
│ ├── IReadOnlySet.cs
│ ├── ReadOnlyHashSet.cs
│ ├── ReadOnlySetExtensions.cs
│ └── StringBuilderExtensions.cs
├── Configuration
│ ├── AppConfig.cs
│ ├── ConnectionSettings.cs
│ └── CustomSerializers
│ │ └── IReadOnlySetConverter.cs
├── DatabaseProviders
│ ├── AccessProvider.cs
│ ├── ClickHouseProvider.cs
│ ├── DB2Provider.cs
│ ├── DatabaseProviderBase.cs
│ ├── DatabaseProviders.cs
│ ├── FirebirdProvider.cs
│ ├── IDatabaseProvider.cs
│ ├── InformixProvider.cs
│ ├── MySqlProvider.cs
│ ├── OracleProvider.cs
│ ├── PostgreSQLProvider.cs
│ ├── ProviderInfo.cs
│ ├── SQLiteProvider.cs
│ ├── SapHanaProvider.cs
│ ├── SqlCeProvider.cs
│ ├── SqlServerProvider.cs
│ └── SybaseAseProvider.cs
├── Drivers
│ ├── DriverHelper.cs
│ ├── DynamicLinqToDBDriver.cs
│ ├── DynamicSchemaGenerator.cs
│ ├── PasswordManager.cs
│ ├── Scaffold
│ │ ├── CSharpUtils.cs
│ │ ├── DataModelAugmentor.cs
│ │ └── ModelProviderInterceptor.cs
│ ├── StaticLinqToDBDriver.cs
│ ├── StaticSchemaGenerator.cs
│ └── ValueFormatter.cs
├── LINQPadDataConnection.cs
├── LinqToDBLinqPadException.cs
├── Properties
│ └── AssemblyInfo.cs
├── UI
│ ├── Model
│ │ ├── AboutModel.cs
│ │ ├── ConnectionModelBase.cs
│ │ ├── DynamicConnectionModel.cs
│ │ ├── LinqToDBModel.cs
│ │ ├── ModelBase.cs
│ │ ├── OptionalTabModelBase.cs
│ │ ├── ScaffoldModel.cs
│ │ ├── SchemaModel.cs
│ │ ├── SettingsModel.cs
│ │ ├── StaticConnectionModel.cs
│ │ ├── TabModelBase.cs
│ │ └── UniqueStringListModel.cs
│ ├── Notification.cs
│ └── Settings
│ │ ├── AboutTab.xaml
│ │ ├── AboutTab.xaml.cs
│ │ ├── CommandTimeoutConverter.cs
│ │ ├── DynamicConnectionTab.xaml
│ │ ├── DynamicConnectionTab.xaml.cs
│ │ ├── LinqToDBTab.xaml
│ │ ├── LinqToDBTab.xaml.cs
│ │ ├── ScaffoldTab.xaml
│ │ ├── ScaffoldTab.xaml.cs
│ │ ├── SchemaTab.xaml
│ │ ├── SchemaTab.xaml.cs
│ │ ├── SettingsDialog.xaml
│ │ ├── SettingsDialog.xaml.cs
│ │ ├── SharedConnectionOptions.xaml
│ │ ├── SharedConnectionOptions.xaml.cs
│ │ ├── StaticConnectionTab.xaml
│ │ ├── StaticConnectionTab.xaml.cs
│ │ ├── UniqueStringListControl.xaml
│ │ └── UniqueStringListControl.xaml.cs
└── linq2db.LINQPad.csproj
├── Testing
└── TypeRenderingTests.txt
├── linq2db.LINQPad.sln
├── nuget.config
└── release-notes.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .vs/
2 | bin/
3 | obj/
4 | *.zip
5 | *.lpx
6 | *.lpx6
7 | *.csproj.user
8 |
--------------------------------------------------------------------------------
/Build/BuildNuspecs.ps1:
--------------------------------------------------------------------------------
1 | Param(
2 | [Parameter(Mandatory=$true)][string]$path,
3 | [Parameter(Mandatory=$true)][string]$version,
4 | [Parameter(Mandatory=$false)][string]$branch
5 | )
6 |
7 | $ErrorActionPreference = "Stop"
8 | Set-StrictMode -Version Latest
9 |
10 | if ($version) {
11 |
12 | $nsUri = 'http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd'
13 | $authors = 'Linq To DB Team'
14 | $ns = @{ns=$nsUri}
15 | $dotlessVersion = $version -replace '\.',''
16 | $commit = (git rev-parse HEAD)
17 | if (-not $branch) {
18 | $branch = (git rev-parse --abbrev-ref HEAD)
19 | }
20 |
21 | Get-ChildItem $path | ForEach {
22 | $xmlPath = Resolve-Path $_.FullName
23 |
24 | $xml = [xml] (Get-Content "$xmlPath")
25 | $xml.PreserveWhitespace = $true
26 |
27 | Select-Xml -Xml $xml -XPath '//ns:metadata/ns:version' -Namespace $ns |
28 | Select -expand node |
29 | ForEach { $_.InnerText = $version }
30 |
31 | $child = $xml.CreateElement('version', $nsUri)
32 | $child.InnerText = $version
33 | $xml.package.metadata.AppendChild($child)
34 |
35 | $child = $xml.CreateElement('releaseNotes', $nsUri)
36 | $child.InnerText = 'https://github.com/linq2db/linq2db.LINQPad/blob/master/release-notes.md#release-' + $dotlessVersion
37 | $xml.package.metadata.AppendChild($child)
38 |
39 | $child = $xml.CreateElement('copyright', $nsUri)
40 | $child.InnerText = 'Copyright © 2016-2025 ' + $authors
41 | $xml.package.metadata.AppendChild($child)
42 |
43 | $child = $xml.CreateElement('authors', $nsUri)
44 | $child.InnerText = $authors
45 | $xml.package.metadata.AppendChild($child)
46 |
47 | $child = $xml.CreateElement('owners', $nsUri)
48 | $child.InnerText = $authors
49 | $xml.package.metadata.AppendChild($child)
50 |
51 | $child = $xml.CreateElement('license', $nsUri)
52 | $attr = $xml.CreateAttribute('type')
53 | $attr.Value = 'file'
54 | $child.Attributes.Append($attr)
55 | $child.InnerText = 'MIT-LICENSE.txt'
56 | $xml.package.metadata.AppendChild($child)
57 |
58 | $child = $xml.CreateElement('file', $nsUri)
59 | $attr = $xml.CreateAttribute('src')
60 | $attr.Value = '..\MIT-LICENSE.txt'
61 | $child.Attributes.Append($attr)
62 | $xml.package.files.AppendChild($child)
63 |
64 | $child = $xml.CreateElement('projectUrl', $nsUri)
65 | $child.InnerText = 'https://linq2db.github.io'
66 | $xml.package.metadata.AppendChild($child)
67 |
68 | # add icon + icon file
69 | $child = $xml.CreateElement('icon', $nsUri)
70 | $child.InnerText = 'images\icon.png'
71 | $xml.package.metadata.AppendChild($child)
72 |
73 | $child = $xml.CreateElement('file', $nsUri)
74 | $attr = $xml.CreateAttribute('src')
75 | $attr.Value = 'icon.png'
76 | $child.Attributes.Append($attr)
77 | $attr = $xml.CreateAttribute('target')
78 | $attr.Value = 'images\icon.png'
79 | $child.Attributes.Append($attr)
80 | $xml.package.files.AppendChild($child)
81 |
82 |
83 | $child = $xml.CreateElement('requireLicenseAcceptance', $nsUri)
84 | $child.InnerText = 'false'
85 | $xml.package.metadata.AppendChild($child)
86 |
87 | $child = $xml.CreateElement('repository', $nsUri)
88 | $attr = $xml.CreateAttribute('type')
89 | $attr.Value = 'git'
90 | $child.Attributes.Append($attr)
91 | $attr = $xml.CreateAttribute('url')
92 | $attr.Value = 'https://github.com/linq2db/linq2db.LINQPad.git'
93 | $child.Attributes.Append($attr)
94 | $attr = $xml.CreateAttribute('branch')
95 | $attr.Value = $branch
96 | $child.Attributes.Append($attr)
97 | $attr = $xml.CreateAttribute('commit')
98 | $attr.Value = $commit
99 | $child.Attributes.Append($attr)
100 | $xml.package.metadata.AppendChild($child)
101 |
102 | Write-Host "Patched $xmlPath"
103 | $xml.Save($xmlPath)
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/Build/Connection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linq2db/linq2db.LINQPad/de4b35b3dc6fe7973ddabc0112ef6376c15217df/Build/Connection.png
--------------------------------------------------------------------------------
/Build/FailedConnection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linq2db/linq2db.LINQPad/de4b35b3dc6fe7973ddabc0112ef6376c15217df/Build/FailedConnection.png
--------------------------------------------------------------------------------
/Build/Pack.cmd:
--------------------------------------------------------------------------------
1 | ECHO ON
2 | ECHO Packing %2
3 |
4 | SET RELDIR=%1..\..\..\..\releases
5 | SET RESDIR=%1..\..\..\..\..\Build
6 |
7 | DEL %RELDIR%\linq2db.LINQPad.%2
8 | DEL %RELDIR%\linq2db.LINQPad.%2.zip
9 |
10 | REM LINQPad 5 driver archive generation
11 | IF %2 EQU lpx (
12 | REM remove resource satellite assemblies
13 | RD /S /Q %1cs
14 | RD /S /Q %1de
15 | RD /S /Q %1es
16 | RD /S /Q %1fr
17 | RD /S /Q %1it
18 | RD /S /Q %1ja
19 | RD /S /Q %1ko
20 | RD /S /Q %1pl
21 | RD /S /Q %1pt
22 | RD /S /Q %1pt-BR
23 | RD /S /Q %1ru
24 | RD /S /Q %1tr
25 | RD /S /Q %1zh-Hans
26 | RD /S /Q %1zh-Hant
27 |
28 | REM remove not needed files
29 | DEL /Q %1linq2db.*.xml
30 | DEL /Q %1*.pdb
31 |
32 | "C:\Program Files\7-Zip\7z.exe" -r a %RELDIR%\linq2db.LINQPad.%2.zip %1*.* %RESDIR%\Connection.png %RESDIR%\FailedConnection.png %RESDIR%\header.xml
33 | )
34 |
35 | REM LINQPad 7 driver archive generation
36 | IF %2 EQU lpx6 ("C:\Program Files\7-Zip\7z.exe" a %RELDIR%\linq2db.LINQPad.%2.zip %1linq2db.LINQPad.dll %RESDIR%\Connection.png %RESDIR%\FailedConnection.png %1linq2db.LINQPad.deps.json)
37 |
38 | REN %RELDIR%\linq2db.LINQPad.%2.zip linq2db.LINQPad.%2
39 |
40 |
--------------------------------------------------------------------------------
/Build/README.md:
--------------------------------------------------------------------------------
1 | # LINQ to DB LINQPad 8 Driver
2 |
3 | This nuget package is a driver for [LINQPad 8](http://www.linqpad.net). Support for older versions of LINQPad is available via older versions drivers.
4 |
5 | Following databases supported:
6 |
7 | - **ClickHouse**: using Binary, HTTP and MySQL interfaces
8 | - **DB2** (LUW, z/OS, iSeries): x64-bit version of LINQPad only
9 | - **DB2 iSeries**: check release notes to see which version supports this database
10 | - **Firebird**
11 | - **Informix**: x64-bit version of LINQPad only
12 | - **Microsoft Access**: both OLE DB and ODBC drivers
13 | - **Microsoft SQL Server** 2005+ *(including **Microsoft SQL Azure**)*
14 | - **Microsoft SQL Server Compact (SQL CE)**
15 | - **MariaDB**
16 | - **MySql**
17 | - **Oracle**
18 | - **PostgreSQL**
19 | - **SAP HANA** *(client software must be installed, supports both Native and ODBC providers)*
20 | - **SAP/Sybase ASE**
21 | - **SQLite**
22 |
23 | ## Installation
24 |
25 | - Click "Add connection" in LINQPad.
26 | - In the "Choose Data Context" dialog, press the "View more drivers..." button.
27 | - In the "LINQPad NuGet Manager" dialog, find LINQ To DB driver in list of drivers and click the "Install" button.
28 | - Close "LINQPad NuGet Manager" dialog
29 | - In the "Choose Data Context" dialog, select the "LINQ to DB" driver and click the "Next" button.
30 | - In the "LINQ to DB connection" dialog, supply your connection information.
31 | - You're done.
32 |
--------------------------------------------------------------------------------
/Build/SetVersion.ps1:
--------------------------------------------------------------------------------
1 | Param(
2 | [Parameter(Mandatory=$true)][string]$path,
3 | [Parameter(Mandatory=$true)][string]$version
4 | )
5 |
6 | $ErrorActionPreference = "Stop"
7 | Set-StrictMode -Version Latest
8 |
9 | if ($version) {
10 |
11 | $xmlPath = Resolve-Path "$path"
12 |
13 | $xml = [XML](Get-Content "$xmlPath")
14 | $xml.PreserveWhitespace = $true
15 | $save = $false
16 |
17 | $xPath = "//PropertyGroup/Version"
18 | $nodes = $xml.SelectNodes($xPath)
19 | foreach($node in $nodes) {
20 | $node.InnerXml = $version
21 | $save = $true
22 | }
23 |
24 | if ($save) {
25 | Write-Host "Patched $xmlPath"
26 | $xml.Save($xmlPath)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Build/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | variables:
2 | solution: 'linq2db.LINQPad.sln'
3 | build_configuration: 'Release'
4 | assemblyVersion: 6.0.0
5 | nugetVersion: 6.0.0-preview.3
6 | nugetDevVersion: 6.0.0
7 | nugetPRVersion: 6.0.0
8 | artifact_lpx: 'lpx'
9 | artifact_lpx6: 'lpx6'
10 | artifact_nuget: 'nuget'
11 |
12 | # trigger for commits to master/release
13 | trigger:
14 | - master
15 | - release
16 | # trigger for PRs to master/release
17 | pr:
18 | - master
19 | - release
20 |
21 | stages:
22 | - stage: ''
23 | displayName: ''
24 | jobs:
25 | - job: build_job
26 | pool:
27 | vmImage: 'windows-2022'
28 | displayName: 'Build'
29 | variables:
30 | - template: nuget-vars.yml
31 |
32 | steps:
33 |
34 | - task: NuGetToolInstaller@1
35 |
36 | - task: PowerShell@2
37 | inputs:
38 | filePath: '$(Build.SourcesDirectory)/Build/SetVersion.ps1'
39 | workingDirectory: '$(Build.SourcesDirectory)'
40 | arguments: -path $(Build.SourcesDirectory)/Source/linq2db.LINQPad.csproj -version $(assemblyVersion)
41 | displayName: Update Assembly Version
42 |
43 | - task: DotNetCoreCLI@2
44 | inputs:
45 | command: build
46 | projects: '$(solution)'
47 | arguments: '--configuration $(build_configuration)'
48 | displayName: Build Solution
49 |
50 | - task: PublishPipelineArtifact@1
51 | inputs:
52 | path: '$(Build.SourcesDirectory)/.build/releases/linq2db.LINQPad.lpx'
53 | artifact: '$(artifact_lpx)'
54 | displayName: Publish .LPX to Artifacts
55 |
56 | - task: PublishPipelineArtifact@1
57 | inputs:
58 | path: '$(Build.SourcesDirectory)/.build/releases/linq2db.LINQPad.lpx6'
59 | artifact: '$(artifact_lpx6)'
60 | displayName: Publish .LPX6 to Artifacts
61 |
62 | ############################
63 | # Build and Publish nuget #
64 | ############################
65 |
66 | - powershell: echo "##vso[task.setvariable variable=packageVersion]$(packageVersion)-rc.$(Build.BuildId)"
67 | condition: ne(variables['Build.SourceBranchName'], 'release')
68 | displayName: Update nuget version
69 |
70 | - task: PowerShell@2
71 | inputs:
72 | filePath: '$(Build.SourcesDirectory)/Build/BuildNuspecs.ps1'
73 | workingDirectory: '$(Build.SourcesDirectory)'
74 | arguments: -path $(Build.SourcesDirectory)/Build/linq2db.LINQPad.nuspec -version $(packageVersion) -branch $(Build.SourceBranchName)
75 | displayName: Update .nuspec
76 |
77 | - task: NuGetCommand@2
78 | inputs:
79 | command: 'custom'
80 | arguments: 'pack "$(Build.SourcesDirectory)/Build/linq2db.LINQPad.nuspec" -OutputDirectory "$(Build.SourcesDirectory)/.build/nugets"'
81 | displayName: Generate linq2db.LINQPad.nupkg
82 |
83 | - task: PublishPipelineArtifact@1
84 | inputs:
85 | path: '$(Build.SourcesDirectory)/.build/nugets/'
86 | artifact: '$(artifact_nuget)'
87 | displayName: Publish nuget to Artifacts
88 |
89 | - task: NuGetCommand@2
90 | inputs:
91 | command: 'push'
92 | packagesToPush: '$(Build.SourcesDirectory)/.build/nugets/*.nupkg'
93 | nuGetFeedType: 'internal'
94 | publishVstsFeed: '0dcc414b-ea54-451e-a54f-d63f05367c4b/967a4107-9788-41a4-9f6d-a2318aab1410'
95 | displayName: Publish to Azure Artifacts feed
96 | condition: eq(variables['Build.SourceBranchName'], 'master')
97 |
98 | - task: NuGetCommand@2
99 | inputs:
100 | command: 'push'
101 | packagesToPush: '$(Build.SourcesDirectory)/.build/nugets/*.nupkg'
102 | nuGetFeedType: 'external'
103 | publishFeedCredentials: 'linq2db nuget.org feed'
104 | displayName: Publish to Nuget.org
105 | condition: eq(variables['Build.SourceBranchName'], 'release')
106 |
--------------------------------------------------------------------------------
/Build/header.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | linq2db.LINQPad.dll
4 | https://github.com/linq2db/linq2db.LINQPad
5 |
6 |
--------------------------------------------------------------------------------
/Build/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linq2db/linq2db.LINQPad/de4b35b3dc6fe7973ddabc0112ef6376c15217df/Build/icon.png
--------------------------------------------------------------------------------
/Build/linq2db.LINQPad.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | linq2db.LINQPad
5 | LINQ to DB driver for LINQPad 7
6 | Supported databases: IBM DB2 LUW/zOS, Firebird, IBM Informix, Microsoft Access, Microsoft Sql Server (+Azure), Microsoft Sql Server Compact, MySql, MariaDB, Oracle, PostgreSQL, SQLite, SAP HANA, SAP/Sybase ASE, ClickHouse.
7 |
8 |
9 | linqpaddriver linqpad linq2db linqtodb access msaccess db2 odbc oledb azure firebird informix mysql mariadb oracle postgres postgresql saphana sqlce sqlserverce sqlserver sybase ase sap sqlite database clickhouse iseries
10 |
11 | README.md
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/Build/nuget-vars.yml:
--------------------------------------------------------------------------------
1 | variables:
2 | ${{ if eq(variables['Build.SourceBranchName'], 'release') }}:
3 | packageVersion: $(nugetVersion)
4 | ${{ if ne(variables['Build.SourceBranchName'], 'release') }}:
5 | packageVersion: $(nugetDevVersion)
6 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | $(MSBuildThisFileDirectory).build
4 | $(Configuration)\$(TargetFramework)
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Directory.Packages.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/MIT-LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016-2025 Linq To DB Team
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # LINQ to DB LINQPad Driver
2 |
3 | [](https://www.nuget.org/profiles/LinqToDB.LINQPad) [](MIT-LICENSE.txt)
4 |
5 | [)](https://dev.azure.com/linq2db/linq2db/_build?definitionId=8&_a=summary) [)](https://dev.azure.com/linq2db/linq2db/_build?definitionId=8&_a=summary)
6 |
7 | linq2db.LINQPad is a driver for [LINQPad 5 (.NET Framework 4.8)](http://www.linqpad.net) and [LINQPad 8 (.NET 8+)](http://www.linqpad.net).
8 |
9 | Following databases supported (by all LINQPad versions if other not specified):
10 |
11 | - **ClickHouse**: using Binary (LINQPad 8), HTTP and MySQL interfaces
12 | - **DB2** (LUW, z/OS): LINQPad 8 x64 and LINQPad 5)
13 | - **DB2 iSeries**: LINQPad 8 and LINQPad 5 (check release notes to see which version supports this database)
14 | - **Firebird**
15 | - **Informix**: LINQPad 8 x64 and LINQPad 5
16 | - **Microsoft Access**: both OLE DB and ODBC drivers
17 | - **Microsoft SQL Server** 2005+ *(including **Microsoft SQL Azure**)*
18 | - **Microsoft SQL Server Compact (SQL CE)**
19 | - **MariaDB**
20 | - **MySql**
21 | - **Oracle**
22 | - **PostgreSQL**
23 | - **SAP HANA** *(client software must be installed, supports both Native and ODBC providers)*
24 | - **SAP/Sybase ASE**
25 | - **SQLite**
26 |
27 | ## Download
28 |
29 | Releases are hosted on [Github](https://github.com/linq2db/linq2db.LINQPad/releases) and on [nuget.org](https://www.nuget.org/packages/linq2db.LINQPad) for LINQPad 8 driver.
30 |
31 | Latest build is hosted on [Azure Artifacts](https://dev.azure.com/linq2db/linq2db/_packaging?_a=package&feed=linq2db%40Local&package=linq2db.LINQPad&protocolType=NuGet). Feed [URL](https://pkgs.dev.azure.com/linq2db/linq2db/_packaging/linq2db/nuget/v3/index.json) ([how to use](https://docs.microsoft.com/en-us/nuget/consume-packages/install-use-packages-visual-studio#package-sources)).
32 |
33 | ## Installation
34 |
35 | ### LINQPad 8 (NuGet)
36 |
37 | - Click "Add connection" in LINQPad.
38 | - In the "Choose Data Context" dialog, press the "View more drivers..." button.
39 | - In the "LINQPad NuGet Manager" dialog, find LINQ To DB driver in list of drivers and click the "Install" button.
40 | - Close "LINQPad NuGet Manager" dialog
41 | - In the "Choose Data Context" dialog, select the "LINQ to DB" driver and click the "Next" button.
42 | - In the "LINQ to DB connection" dialog, supply your connection information.
43 | - You're done.
44 |
45 | ### LINQPad 8 (Manual)
46 |
47 | - Download latest **.lpx6** file from the link provided above.
48 | - Click "Add connection" in LINQPad.
49 | - In the "Choose Data Context" dialog, press the "View more drivers..." button.
50 | - In the "LINQPad NuGet Manager" dialog, press the "Install driver from .LPX6 file..." button.
51 | - Select the downloaded file and click the "Open" button.
52 | - In the "Choose Data Context" dialog, select the "LINQ to DB" driver and click the "Next" button.
53 | - In the "LINQ to DB connection" dialog, supply your connection information.
54 | - You're done.
55 |
56 | ### LINQPad 5 (Choose a driver)
57 |
58 | - Click "Add connection" in LINQPad.
59 | - In the "Choose Data Context" dialog, press the "View more drivers..." button.
60 | - In the "Choose a Driver" dialog, search for "LINQ to DB Driver".
61 | - Click "Download & Enable Driver" link to install/update to latest driver release
62 | - In the "Choose Data Context" dialog, select the "LINQ to DB" driver and click the "Next" button.
63 | - In the "LINQ to DB connection" dialog, supply your connection information.
64 | - You're done.
65 |
66 | ### LINQPad 5 (Manual)
67 |
68 | - Download latest **.lpx** file from the link provided above.
69 | - Click "Add connection" in LINQPad.
70 | - In the "Choose Data Context" dialog, press the "View more drivers..." button.
71 | - In the "Choose a Driver" dialog, press the "Browse..." button.
72 | - Select the downloaded file and click the "Open" button.
73 | - In the "Choose Data Context" dialog, select the "LINQ to DB" driver and click the "Next" button.
74 | - In the "LINQ to DB connection" dialog, supply your connection information.
75 | - You're done.
76 |
--------------------------------------------------------------------------------
/Source/Compat/IReadOnlySet.cs:
--------------------------------------------------------------------------------
1 | #if !NET5_0_OR_GREATER
2 | // Licensed to the .NET Foundation under one or more agreements.
3 | // The .NET Foundation licenses this file to you under the MIT license.
4 |
5 | namespace System.Collections.Generic;
6 |
7 | ///
8 | /// Provides a readonly abstraction of a set.
9 | ///
10 | /// The type of elements in the set.
11 | internal interface IReadOnlySet : IReadOnlyCollection
12 | {
13 | ///
14 | /// Determines if the set contains a specific item
15 | ///
16 | /// The item to check if the set contains.
17 | /// if found; otherwise .
18 | bool Contains(T item);
19 | ///
20 | /// Determines whether the current set is a proper (strict) subset of a specified collection.
21 | ///
22 | /// The collection to compare to the current set.
23 | /// if the current set is a proper subset of other; otherwise .
24 | /// other is .
25 | bool IsProperSubsetOf(IEnumerable other);
26 | ///
27 | /// Determines whether the current set is a proper (strict) superset of a specified collection.
28 | ///
29 | /// The collection to compare to the current set.
30 | /// if the collection is a proper superset of other; otherwise .
31 | /// other is .
32 | bool IsProperSupersetOf(IEnumerable other);
33 | ///
34 | /// Determine whether the current set is a subset of a specified collection.
35 | ///
36 | /// The collection to compare to the current set.
37 | /// if the current set is a subset of other; otherwise .
38 | /// other is .
39 | bool IsSubsetOf(IEnumerable other);
40 | ///
41 | /// Determine whether the current set is a super set of a specified collection.
42 | ///
43 | /// The collection to compare to the current set
44 | /// if the current set is a subset of other; otherwise .
45 | /// other is .
46 | bool IsSupersetOf(IEnumerable other);
47 | ///
48 | /// Determines whether the current set overlaps with the specified collection.
49 | ///
50 | /// The collection to compare to the current set.
51 | /// if the current set and other share at least one common element; otherwise, .
52 | /// other is .
53 | bool Overlaps(IEnumerable other);
54 | ///
55 | /// Determines whether the current set and the specified collection contain the same elements.
56 | ///
57 | /// The collection to compare to the current set.
58 | /// if the current set is equal to other; otherwise, .
59 | /// other is .
60 | bool SetEquals(IEnumerable other);
61 | }
62 | #endif
63 |
--------------------------------------------------------------------------------
/Source/Compat/ReadOnlyHashSet.cs:
--------------------------------------------------------------------------------
1 | #if !NET5_0_OR_GREATER
2 | namespace System.Collections.Generic;
3 |
4 | internal sealed class ReadOnlyHashSet(ISet set) : IReadOnlySet
5 | {
6 | int IReadOnlyCollection.Count => set.Count;
7 |
8 | bool IReadOnlySet.Contains(T item) => set.Contains(item);
9 |
10 | IEnumerator IEnumerable.GetEnumerator() => set.GetEnumerator();
11 |
12 | IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)set).GetEnumerator();
13 |
14 | bool IReadOnlySet.IsProperSubsetOf(IEnumerable other) => set.IsProperSubsetOf(other);
15 |
16 | bool IReadOnlySet.IsProperSupersetOf(IEnumerable other) => set.IsProperSupersetOf(other);
17 |
18 | bool IReadOnlySet.IsSubsetOf(IEnumerable other) => set.IsSubsetOf(other);
19 |
20 | bool IReadOnlySet.IsSupersetOf(IEnumerable other) => set.IsSupersetOf(other);
21 |
22 | bool IReadOnlySet.Overlaps(IEnumerable other) => set.Overlaps(other);
23 |
24 | bool IReadOnlySet.SetEquals(IEnumerable other) => set.SetEquals(other);
25 |
26 |
27 | }
28 | #endif
29 |
--------------------------------------------------------------------------------
/Source/Compat/ReadOnlySetExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace System.Collections.Generic;
2 |
3 | internal static class ReadOnlySetExtensions
4 | {
5 | #pragma warning disable CA1859 // change return type
6 | public static IReadOnlySet AsReadOnly(this HashSet set)
7 | #pragma warning restore CA1859 // change return type
8 | {
9 | #if NET5_0_OR_GREATER
10 | return set;
11 | #else
12 | return new ReadOnlyHashSet(set);
13 | #endif
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Source/Compat/StringBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | #if NETFRAMEWORK
2 | namespace System.Text;
3 |
4 | internal static class StringBuilderExtensions
5 | {
6 | public static StringBuilder Append(
7 | this StringBuilder sb,
8 | IFormatProvider? provider,
9 | FormattableString formattableString)
10 | {
11 | sb.Append(formattableString.ToString(provider));
12 | return sb;
13 | }
14 |
15 | public static StringBuilder AppendLine(
16 | this StringBuilder sb,
17 | IFormatProvider? provider,
18 | FormattableString formattableString)
19 | {
20 | sb.AppendLine(formattableString.ToString(provider));
21 | return sb;
22 | }
23 | }
24 | #endif
25 |
--------------------------------------------------------------------------------
/Source/Configuration/AppConfig.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text.Json;
3 | using System.Xml;
4 | using LinqToDB.Configuration;
5 |
6 | namespace LinqToDB.LINQPad;
7 |
8 | ///
9 | /// Implements Linq To DB connection settings provider, which use data from JSON config.
10 | /// Used as settings source for static data context.
11 | ///
12 | internal sealed class AppConfig(IConnectionStringSettings[] connectionStrings) : ILinqToDBSettings
13 | {
14 | #pragma warning disable CA1859 // change return type
15 | public static ILinqToDBSettings LoadJson(string configPath)
16 | #pragma warning restore CA1859 // change return type
17 | {
18 | var config = JsonSerializer.Deserialize(File.ReadAllText(configPath));
19 |
20 | if (config?.ConnectionStrings?.Count is null or 0)
21 | return new AppConfig([]);
22 |
23 | var connections = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
24 | foreach (var cn in config.ConnectionStrings)
25 | {
26 | if (cn.Key.EndsWith("_ProviderName", StringComparison.InvariantCultureIgnoreCase))
27 | continue;
28 |
29 | connections.Add(cn.Key, new ConnectionStringSettings(cn.Key, PasswordManager.ResolvePasswordManagerFields(cn.Value)));
30 | }
31 |
32 | foreach (var cn in config.ConnectionStrings)
33 | {
34 | if (!cn.Key.EndsWith("_ProviderName", StringComparison.InvariantCultureIgnoreCase))
35 | continue;
36 |
37 | var key = cn.Key.Substring(0, cn.Key.Length - "_ProviderName".Length);
38 | if (connections.TryGetValue(key, out var cs))
39 | cs.ProviderName = cn.Value;
40 | }
41 |
42 | return new AppConfig([.. connections.Values]);
43 | }
44 |
45 | #pragma warning disable CA1859 // change return type
46 | public static ILinqToDBSettings LoadAppConfig(string configPath)
47 | #pragma warning restore CA1859 // change return type
48 | {
49 | var xml = new XmlDocument() { XmlResolver = null };
50 | using var reader = XmlReader.Create(new StringReader(File.ReadAllText(configPath)), new XmlReaderSettings() { XmlResolver = null });
51 | xml.Load(reader);
52 |
53 | var connections = xml.SelectNodes("/configuration/connectionStrings/add");
54 |
55 | if (connections?.Count is null or 0)
56 | return new AppConfig([]);
57 |
58 | var settings = new List();
59 |
60 | foreach (XmlElement node in connections)
61 | {
62 | var name = node.Attributes["name" ]?.Value;
63 | var connectionString = node.Attributes["connectionString"]?.Value;
64 | var providerName = node.Attributes["providerName" ]?.Value;
65 |
66 | if (name != null && connectionString != null)
67 | settings.Add(new ConnectionStringSettings(name, PasswordManager.ResolvePasswordManagerFields(connectionString)) { ProviderName = providerName });
68 | }
69 |
70 | return new AppConfig([.. settings]);
71 | }
72 |
73 | IEnumerable ILinqToDBSettings.DataProviders => [];
74 | string? ILinqToDBSettings.DefaultConfiguration => null;
75 | string? ILinqToDBSettings.DefaultDataProvider => null;
76 | IEnumerable ILinqToDBSettings.ConnectionStrings => connectionStrings;
77 |
78 | #pragma warning disable CA1812 // Remove unused type
79 | private sealed class JsonConfig
80 | #pragma warning restore CA1812 // Remove unused type
81 | {
82 | public IDictionary? ConnectionStrings { get; set; }
83 | }
84 |
85 | /// Connection name.
86 | /// Must be connection string without password manager tokens.
87 | private sealed class ConnectionStringSettings(string name, string connectionString) : IConnectionStringSettings
88 | {
89 | string IConnectionStringSettings.ConnectionString => connectionString;
90 | string IConnectionStringSettings.Name => name;
91 | bool IConnectionStringSettings.IsGlobal => false;
92 |
93 | public string? ProviderName { get; set; }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/Source/Configuration/CustomSerializers/IReadOnlySetConverter.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace LinqToDB.LINQPad.Json;
5 |
6 | internal sealed class IReadOnlySetConverter : JsonConverter>
7 | {
8 | private readonly JsonConverter _elementConverter;
9 | private readonly Type _elementType = typeof(T);
10 |
11 | private static IReadOnlySetConverter? _instance;
12 |
13 | public static readonly JsonConverterFactory Factory = new IReadOnlySetConverterFactory();
14 |
15 | private static JsonConverter GetInstance(JsonConverter elementConverter)
16 | {
17 | return _instance ??= new IReadOnlySetConverter(elementConverter);
18 | }
19 |
20 | private IReadOnlySetConverter(JsonConverter elementConverter)
21 | {
22 | _elementConverter = elementConverter;
23 | }
24 |
25 | public override IReadOnlySet Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
26 | {
27 | var hashSet = new HashSet();
28 |
29 | while (reader.Read())
30 | {
31 | if (reader.TokenType == JsonTokenType.EndArray)
32 | {
33 | break;
34 | }
35 |
36 | var item = _elementConverter.Read(ref reader, _elementType, options);
37 | hashSet.Add(item!);
38 | }
39 |
40 | return hashSet.AsReadOnly();
41 | }
42 |
43 | public override void Write(Utf8JsonWriter writer, IReadOnlySet value, JsonSerializerOptions options)
44 | {
45 | writer.WriteStartArray();
46 | foreach (var item in value)
47 | {
48 | _elementConverter.Write(writer, item, options);
49 | }
50 | writer.WriteEndArray();
51 | }
52 |
53 | private sealed class IReadOnlySetConverterFactory : JsonConverterFactory
54 | {
55 | public override bool CanConvert(Type typeToConvert)
56 | {
57 | if (!typeToConvert.IsGenericType)
58 | {
59 | return false;
60 | }
61 |
62 | return typeToConvert.GetGenericTypeDefinition() == typeof(IReadOnlySet<>);
63 | }
64 |
65 | public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
66 | {
67 | var elementType = typeToConvert.GetGenericArguments()[0];
68 |
69 | var converterType = typeof(IReadOnlySetConverter<>).MakeGenericType(elementType);
70 | return (JsonConverter)converterType
71 | .GetMethod(nameof(IReadOnlySetConverter.GetInstance), BindingFlags.NonPublic | BindingFlags.Static)!
72 | .Invoke(null, [options.GetConverter(elementType)])!;
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/Source/DatabaseProviders/AccessProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Data.Common;
2 | using System.Data.OleDb;
3 | using System.Data.Odbc;
4 | using System.Data;
5 | using System.Runtime.InteropServices;
6 | using LinqToDB.DataProvider;
7 |
8 | namespace LinqToDB.LINQPad;
9 |
10 | internal sealed class AccessProvider : DatabaseProviderBase
11 | {
12 | private static readonly IReadOnlyList _providers =
13 | [
14 | new (ProviderName.Access , "OLE DB"),
15 | new (ProviderName.AccessOdbc, "ODBC" ),
16 | ];
17 |
18 | public AccessProvider()
19 | : base(ProviderName.Access, "Microsoft Access", _providers)
20 | {
21 | }
22 |
23 | public override bool SupportsSecondaryConnection => true;
24 | public override bool AutomaticProviderSelection => true;
25 |
26 | public override string? GetProviderDownloadUrl(string? providerName)
27 | {
28 | return "https://www.microsoft.com/en-us/download/details.aspx?id=54920";
29 | }
30 |
31 | public override void ClearAllPools(string providerName)
32 | {
33 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && providerName == ProviderName.Access)
34 | OleDbConnection.ReleaseObjectPool();
35 |
36 | if (providerName == ProviderName.AccessOdbc)
37 | OdbcConnection.ReleaseObjectPool();
38 | }
39 |
40 | public override DateTime? GetLastSchemaUpdate(ConnectionSettings settings)
41 | {
42 | var connectionString = settings.Connection.Provider == ProviderName.Access
43 | ? settings.Connection.GetFullConnectionString()
44 | : settings.Connection.SecondaryProvider == ProviderName.Access
45 | ? settings.Connection.GetFullSecondaryConnectionString()
46 | : null;
47 |
48 | if (connectionString == null || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
49 | return null;
50 |
51 | // only OLE DB schema has required information
52 | IDataProvider provider;
53 | if (settings.Connection.Provider == ProviderName.Access)
54 | provider = DatabaseProviders.GetDataProvider(settings);
55 | else
56 | provider = DatabaseProviders.GetDataProvider(settings.Connection.SecondaryProvider, connectionString, null);
57 |
58 | using var cn = (OleDbConnection)provider.CreateConnection(connectionString);
59 | cn.Open();
60 |
61 | var dt1 = cn.GetSchema("Tables" ).Rows.Cast().Select(static r => (DateTime)r["DATE_MODIFIED"]).Concat([default]).Max();
62 | var dt2 = cn.GetSchema("Procedures").Rows.Cast().Select(static r => (DateTime)r["DATE_MODIFIED"]).Concat([default]).Max();
63 | return dt1 > dt2 ? dt1 : dt2;
64 | }
65 |
66 | public override ProviderInfo? GetProviderByConnectionString(string connectionString)
67 | {
68 | connectionString = PasswordManager.ResolvePasswordManagerFields(connectionString);
69 |
70 | var isOleDb = connectionString.IndexOf("Microsoft.Jet.OLEDB", StringComparison.OrdinalIgnoreCase) != -1
71 | || connectionString.IndexOf("Microsoft.ACE.OLEDB", StringComparison.OrdinalIgnoreCase) != -1;
72 |
73 | // we don't check for ODBC provider marker - it will fail on connection test if wrong
74 | return _providers[isOleDb ? 0 : 1];
75 | }
76 |
77 | public override DbProviderFactory GetProviderFactory(string providerName)
78 | {
79 | if (providerName == ProviderName.AccessOdbc)
80 | return OdbcFactory.Instance;
81 |
82 | return OleDbFactory.Instance;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Source/DatabaseProviders/ClickHouseProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Data.Common;
2 | using ClickHouse.Client.ADO;
3 | using LinqToDB.Data;
4 | using MySqlConnector;
5 | #if !NETFRAMEWORK
6 | using Octonica.ClickHouseClient;
7 | #endif
8 |
9 | namespace LinqToDB.LINQPad;
10 |
11 | internal sealed class ClickHouseProvider : DatabaseProviderBase
12 | {
13 | private static readonly IReadOnlyList _providers =
14 | [
15 | new (ProviderName.ClickHouseClient , "HTTP(S) Interface (ClickHouse.Client)" ),
16 | new (ProviderName.ClickHouseMySql , "MySQL Interface (MySqlConnector)" ),
17 | #if !NETFRAMEWORK
18 | // octonica provider doesn't support NETFX or NESTANDARD
19 | new (ProviderName.ClickHouseOctonica, "Binary (TCP) Interface (Octonica.ClickHouseClient)"),
20 | #endif
21 | ];
22 |
23 | public ClickHouseProvider()
24 | : base(ProviderName.ClickHouse, "ClickHouse", _providers)
25 | {
26 | }
27 |
28 | public override void ClearAllPools(string providerName)
29 | {
30 | // octonica provider doesn't implement connection pooling
31 | // client provider use http connections pooling
32 | if (providerName == ProviderName.ClickHouseMySql)
33 | MySqlConnection.ClearAllPools();
34 | }
35 |
36 | public override DateTime? GetLastSchemaUpdate(ConnectionSettings settings)
37 | {
38 | using var db = new LINQPadDataConnection(settings);
39 | return db.Query("SELECT MAX(metadata_modification_time) FROM system.tables WHERE database = database()").FirstOrDefault();
40 | }
41 |
42 | public override DbProviderFactory GetProviderFactory(string providerName)
43 | {
44 | if (providerName == ProviderName.ClickHouseClient)
45 | return new ClickHouseConnectionFactory();
46 | #if !NETFRAMEWORK
47 | if (providerName == ProviderName.ClickHouseOctonica)
48 | return new ClickHouseDbProviderFactory();
49 | #endif
50 |
51 | return MySqlConnectorFactory.Instance;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Source/DatabaseProviders/DB2Provider.cs:
--------------------------------------------------------------------------------
1 | using LinqToDB.Data;
2 | using System.Data.Common;
3 |
4 | #if WITH_ISERIES
5 | using LinqToDB.DataProvider.DB2iSeries;
6 | #endif
7 |
8 | #if NETFRAMEWORK
9 | using IBM.Data.DB2;
10 | using System.IO;
11 | #else
12 | using IBM.Data.Db2;
13 | #endif
14 |
15 | namespace LinqToDB.LINQPad;
16 |
17 | internal sealed class DB2Provider : DatabaseProviderBase
18 | {
19 | private static readonly IReadOnlyList _providers =
20 | [
21 | new (ProviderName.DB2LUW , "DB2 for Linux, UNIX and Windows (LUW)"),
22 | // zOS provider not tested at all as we don't have access to database instance
23 | new (ProviderName.DB2zOS , "DB2 for z/OS" ),
24 | #if WITH_ISERIES
25 | new (DB2iSeriesProviderName.DB2, "DB2 for i (iSeries)" ),
26 | #endif
27 | ];
28 |
29 | public DB2Provider()
30 | : base(ProviderName.DB2, "IBM DB2 (LUW, z/OS or iSeries)", _providers)
31 | {
32 | #if WITH_ISERIES
33 | DataConnection.AddProviderDetector(DB2iSeriesTools.ProviderDetector);
34 | #endif
35 | }
36 |
37 | #if NETFRAMEWORK
38 | internal static void LoadAssembly()
39 | {
40 | var assemblyPath = Path.Combine(Path.GetDirectoryName(typeof(DB2Provider).Assembly.Location), "IBM.Data.DB2.DLL_provider", IntPtr.Size == 4 ? "x86" : "x64", $"IBM.Data.DB2.dll");
41 | if (!File.Exists(assemblyPath))
42 | throw new LinqToDBLinqPadException($"Failed to locate IBM.Data.DB2 assembly at {assemblyPath}");
43 |
44 | try
45 | {
46 | _ = Assembly.LoadFrom(assemblyPath);
47 | }
48 | catch (Exception ex)
49 | {
50 | throw new LinqToDBLinqPadException($"Failed to load IBM.Data.DB2 assembly: ({ex.GetType().Name}) {ex.Message}");
51 | }
52 | }
53 | #endif
54 |
55 | public override void ClearAllPools(string providerName)
56 | {
57 | DB2Connection.ReleaseObjectPool();
58 | }
59 |
60 | public override DateTime? GetLastSchemaUpdate(ConnectionSettings settings)
61 | {
62 | var sql = settings.Connection.Provider switch
63 | {
64 | ProviderName.DB2LUW => "SELECT MAX(TIME) FROM (SELECT MAX(ALTER_TIME) AS TIME FROM SYSCAT.ROUTINES UNION SELECT MAX(ALTER_TIME) AS TIME FROM SYSCAT.TABLES)",
65 | ProviderName.DB2zOS => "SELECT MAX(TIME) FROM (SELECT MAX(ALTEREDTS) AS TIME FROM SYSIBM.SYSROUTINES UNION SELECT MAX(ALTEREDTS) AS TIME FROM SYSIBM.SYSTABLES)",
66 | #if WITH_ISERIES
67 | DB2iSeriesProviderName.DB2 => "SELECT MAX(TIME) FROM (SELECT MAX(LAST_ALTERED) AS TIME FROM QSYS2.SYSROUTINES UNION SELECT MAX(ROUTINE_CREATED) AS TIME FROM QSYS2.SYSROUTINES UNION SELECT MAX(LAST_ALTERED_TIMESTAMP) AS TIME FROM QSYS2.SYSTABLES)",
68 | #endif
69 | _ => throw new LinqToDBLinqPadException($"Unknown DB2 provider '{settings.Connection.Provider}'")
70 | };
71 |
72 | using var db = new LINQPadDataConnection(settings);
73 | return db.Query(sql).FirstOrDefault();
74 | }
75 |
76 | public override DbProviderFactory GetProviderFactory(string providerName)
77 | {
78 | return DB2Factory.Instance;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Source/DatabaseProviders/DatabaseProviderBase.cs:
--------------------------------------------------------------------------------
1 | using System.Data.Common;
2 | using LinqToDB.Data;
3 | using LinqToDB.DataProvider;
4 |
5 | namespace LinqToDB.LINQPad;
6 |
7 | internal abstract class DatabaseProviderBase(string database, string description, IReadOnlyList providers) : IDatabaseProvider
8 | {
9 | public string Database { get; } = database;
10 | public string Description { get; } = description;
11 | public IReadOnlyList Providers { get; } = providers;
12 |
13 | public virtual bool SupportsSecondaryConnection { get; }
14 | public virtual bool AutomaticProviderSelection { get; }
15 |
16 | public virtual IReadOnlyCollection GetAdditionalReferences (string providerName ) => [];
17 | public virtual string? GetProviderAssemblyName (string providerName ) => null;
18 | public virtual ProviderInfo? GetProviderByConnectionString(string connectionString ) => null;
19 | #pragma warning disable CA1055 // URI-like return values should not be strings
20 | public virtual string? GetProviderDownloadUrl (string? providerName ) => null;
21 | #pragma warning restore CA1055 // URI-like return values should not be strings
22 | public virtual bool IsProviderPathSupported (string providerName ) => false;
23 | public virtual void RegisterProviderFactory (string providerName, string providerPath) { }
24 | public virtual string? TryGetDefaultPath (string providerName ) => null;
25 | #if NETFRAMEWORK
26 | public virtual void Unload ( ) { }
27 | #endif
28 |
29 | public abstract void ClearAllPools (string providerName );
30 | public abstract DateTime? GetLastSchemaUpdate(ConnectionSettings settings);
31 | public abstract DbProviderFactory GetProviderFactory (string providerName );
32 |
33 | /// Provider name.
34 | /// Connection string must be resolved against password manager already.
35 | ///
36 | public virtual IDataProvider GetDataProvider(string providerName, string connectionString)
37 | {
38 | return DataConnection.GetDataProvider(providerName, connectionString)
39 | ?? throw new LinqToDBLinqPadException($"Can not activate provider '{providerName}'");
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Source/DatabaseProviders/DatabaseProviders.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Frozen;
2 | using System.Data.Common;
3 | using LinqToDB.DataProvider;
4 |
5 | namespace LinqToDB.LINQPad;
6 |
7 | internal static class DatabaseProviders
8 | {
9 | public static readonly FrozenDictionary Providers;
10 | public static readonly FrozenDictionary ProvidersByProviderName;
11 |
12 | #pragma warning disable CA1810 // Initialize reference type static fields inline
13 | static DatabaseProviders()
14 | #pragma warning restore CA1810 // Initialize reference type static fields inline
15 | {
16 | var providers = new Dictionary();
17 | var providersByName = new Dictionary();
18 |
19 | Register(providers, providersByName, new AccessProvider ());
20 | Register(providers, providersByName, new FirebirdProvider ());
21 | Register(providers, providersByName, new MySqlProvider ());
22 | Register(providers, providersByName, new PostgreSQLProvider());
23 | Register(providers, providersByName, new SybaseAseProvider ());
24 | Register(providers, providersByName, new SQLiteProvider ());
25 | Register(providers, providersByName, new SqlCeProvider ());
26 | #if !NETFRAMEWORK
27 | if (IntPtr.Size == 8)
28 | {
29 | Register(providers, providersByName, new DB2Provider ());
30 | Register(providers, providersByName, new InformixProvider());
31 | }
32 | #else
33 | Register(providers, providersByName, new InformixProvider ());
34 | Register(providers, providersByName, new DB2Provider ());
35 | #endif
36 | Register(providers, providersByName, new SapHanaProvider ());
37 | Register(providers, providersByName, new OracleProvider ());
38 | Register(providers, providersByName, new SqlServerProvider ());
39 | Register(providers, providersByName, new ClickHouseProvider());
40 |
41 | Providers = providers.ToFrozenDictionary();
42 | ProvidersByProviderName = providersByName.ToFrozenDictionary();
43 |
44 | static void Register(
45 | Dictionary providers,
46 | Dictionary providersByName,
47 | IDatabaseProvider provider)
48 | {
49 | providers.Add(provider.Database, provider);
50 |
51 | foreach (var info in provider.Providers)
52 | providersByName.Add(info.Name, provider);
53 | }
54 | }
55 |
56 | public static void Unload()
57 | {
58 | #if NETFRAMEWORK
59 | foreach (var provider in Providers.Values)
60 | provider.Unload();
61 | #endif
62 | }
63 |
64 | public static void Init()
65 | {
66 | // trigger .cctors
67 |
68 | #if NETFRAMEWORK
69 | // no harm in loading it here unconditionally instead of trying to detect all places where we really need it
70 | DB2Provider.LoadAssembly();
71 | #endif
72 | }
73 |
74 | public static DbConnection CreateConnection(ConnectionSettings settings)
75 | {
76 | return GetDataProvider(settings).CreateConnection(settings.Connection.GetFullConnectionString()!);
77 | }
78 |
79 | public static DbProviderFactory GetProviderFactory(ConnectionSettings settings) => GetProviderByName(settings.Connection.Provider!).GetProviderFactory(settings.Connection.Provider!);
80 |
81 | public static IDataProvider GetDataProvider(ConnectionSettings settings)
82 | {
83 | return GetDataProvider(settings.Connection.Provider, settings.Connection.GetFullConnectionString(), settings.Connection.ProviderPath);
84 | }
85 |
86 | /// Provider name.
87 | /// Connection string must be already resolved against password manager.
88 | /// Optional path to provider assembly.
89 | public static IDataProvider GetDataProvider(string? providerName, string? connectionString, string? providerPath)
90 | {
91 | if (string.IsNullOrWhiteSpace(providerName))
92 | throw new LinqToDBLinqPadException("Can not activate provider. Provider is not selected.");
93 |
94 | if (string.IsNullOrWhiteSpace(connectionString))
95 | throw new LinqToDBLinqPadException($"Can not activate provider '{providerName}'. Connection string not specified.");
96 |
97 | var databaseProvider = GetProviderByName(providerName!);
98 | if (providerPath != null)
99 | databaseProvider.RegisterProviderFactory(providerName!, providerPath);
100 |
101 | return databaseProvider.GetDataProvider(providerName!, connectionString!);
102 | }
103 |
104 | private static IDatabaseProvider GetProviderByName(string providerName)
105 | {
106 | if (ProvidersByProviderName.TryGetValue(providerName, out var provider))
107 | return provider;
108 |
109 | throw new LinqToDBLinqPadException($"Cannot find database provider '{providerName}'");
110 | }
111 |
112 | ///
113 | /// Gets database provider abstraction by database name.
114 | ///
115 | /// Database name (identifier of provider abstraction).
116 | public static IDatabaseProvider GetProvider(string? database)
117 | {
118 | if (database != null && Providers.TryGetValue(database, out var provider))
119 | return provider;
120 |
121 | throw new LinqToDBLinqPadException($"Cannot find provider for database '{database}'");
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/Source/DatabaseProviders/FirebirdProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Data.Common;
2 | using FirebirdSql.Data.FirebirdClient;
3 |
4 | namespace LinqToDB.LINQPad;
5 |
6 | internal sealed class FirebirdProvider : DatabaseProviderBase
7 | {
8 | private static readonly IReadOnlyList _providers =
9 | [
10 | new (ProviderName.Firebird , "Detect Dialect Automatically", true),
11 | new (ProviderName.Firebird25, "Firebird 2.5" ),
12 | new (ProviderName.Firebird3 , "Firebird 3" ),
13 | new (ProviderName.Firebird4 , "Firebird 4" ),
14 | new (ProviderName.Firebird5 , "Firebird 5" ),
15 | ];
16 |
17 | public FirebirdProvider()
18 | : base(ProviderName.Firebird, "Firebird", _providers)
19 | {
20 | }
21 |
22 | public override void ClearAllPools(string providerName)
23 | {
24 | FbConnection.ClearAllPools();
25 | }
26 |
27 | public override DateTime? GetLastSchemaUpdate(ConnectionSettings settings)
28 | {
29 | // no information in schema
30 | return null;
31 | }
32 |
33 | public override DbProviderFactory GetProviderFactory(string providerName)
34 | {
35 | return FirebirdClientFactory.Instance;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Source/DatabaseProviders/IDatabaseProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Data.Common;
2 | using LinqToDB.DataProvider;
3 |
4 | namespace LinqToDB.LINQPad;
5 |
6 | ///
7 | /// Provides database provider abstraction.
8 | ///
9 | internal interface IDatabaseProvider
10 | {
11 | ///
12 | /// Gets database name (generic value).
13 | ///
14 | string Database { get; }
15 |
16 | ///
17 | /// Gets database provider name for UI.
18 | ///
19 | string Description { get; }
20 |
21 | ///
22 | /// When true, database provider supports secondary connection for database schema population.
23 | ///
24 | bool SupportsSecondaryConnection { get; }
25 |
26 | ///
27 | /// Release all connections.
28 | ///
29 | void ClearAllPools(string providerName);
30 |
31 | ///
32 | /// Returns last schema update time.
33 | ///
34 | DateTime? GetLastSchemaUpdate(ConnectionSettings settings);
35 |
36 | ///
37 | /// Returns additional reference assemblies for dynamic model compilation (except main provider assembly).
38 | ///
39 | IReadOnlyCollection GetAdditionalReferences(string providerName);
40 |
41 | ///
42 | /// List of supported provider names for provider.
43 | ///
44 | IReadOnlyList Providers { get; }
45 |
46 | ///
47 | /// When true, connection settings UI doesn't allow user to select provider type.
48 | /// method will be used to infer provider automatically.
49 | /// Note that provider selection also unavailable when there is only one provider supported by database.
50 | ///
51 | bool AutomaticProviderSelection { get; }
52 |
53 | ///
54 | /// Tries to infer provider by database connection string.
55 | ///
56 | /// Connection string could contain password manager tokens.
57 | ///
58 | ProviderInfo? GetProviderByConnectionString(string connectionString);
59 |
60 | ///
61 | /// Returns true, if specified provider for current database provider supports provider assembly path configuration.
62 | ///
63 | bool IsProviderPathSupported(string providerName);
64 |
65 | ///
66 | /// If provider supports assembly path configuration, method
67 | /// returns help text for configuration UI to help user locate and/or install provider.
68 | ///
69 | string? GetProviderAssemblyName(string providerName);
70 |
71 | ///
72 | /// If provider supports assembly path configuration, method could return URL to provider download page.
73 | ///
74 | #pragma warning disable CA1055 // URI-like return values should not be strings
75 | string? GetProviderDownloadUrl(string? providerName);
76 | #pragma warning restore CA1055 // URI-like return values should not be strings
77 |
78 | ///
79 | /// If provider supports assembly path configuration (), method tries to return default path to provider assembly,
80 | /// but only if assembly exists on specified path.
81 | ///
82 | string? TryGetDefaultPath(string providerName);
83 |
84 | ///
85 | /// If provider supports assembly path configuration (), method
86 | /// performs provider factory registration to allow Linq To DB locate provider assembly.
87 | ///
88 | void RegisterProviderFactory(string providerName, string providerPath);
89 |
90 | // Technically, factory is needed for raw SQL queries only for LINQPad 5 as v6+ has code to work without factory. Still it wasn't backported to LINQPad 5 and doesn't hurt to support.
91 | // LINQPad currently calls only CreateCommand and CreateDataAdapter methods.
92 | ///
93 | /// Returns ADO.NET provider classes factory.
94 | ///
95 | DbProviderFactory GetProviderFactory(string providerName);
96 |
97 | ///
98 | /// Returns linq2db data provider.
99 | ///
100 | /// Provider name.
101 | /// Connection string must be already resolved against password manager.
102 | IDataProvider GetDataProvider(string providerName, string connectionString);
103 |
104 | #if NETFRAMEWORK
105 | ///
106 | /// Performs clanup on domain unload.
107 | ///
108 | void Unload();
109 | #endif
110 | }
111 |
--------------------------------------------------------------------------------
/Source/DatabaseProviders/InformixProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Data.Common;
2 | #if NETFRAMEWORK
3 | using IBM.Data.DB2;
4 | #else
5 | using IBM.Data.Db2;
6 | #endif
7 |
8 | namespace LinqToDB.LINQPad;
9 |
10 | internal sealed class InformixProvider : DatabaseProviderBase
11 | {
12 | private static readonly IReadOnlyList _providers =
13 | [
14 | new (ProviderName.InformixDB2, "Informix")
15 | ];
16 |
17 | public InformixProvider()
18 | : base(ProviderName.Informix, "IBM Informix", _providers)
19 | {
20 | }
21 |
22 | public override void ClearAllPools(string providerName)
23 | {
24 | DB2Connection.ReleaseObjectPool();
25 | }
26 |
27 | public override DateTime? GetLastSchemaUpdate(ConnectionSettings settings)
28 | {
29 | // Informix provides only table creation date without time, which is useless
30 | return null;
31 | }
32 |
33 | public override DbProviderFactory GetProviderFactory(string providerName)
34 | {
35 | return DB2Factory.Instance;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Source/DatabaseProviders/MySqlProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Data.Common;
2 | using LinqToDB.Data;
3 | using MySqlConnector;
4 |
5 | namespace LinqToDB.LINQPad;
6 |
7 | internal sealed class MySqlProvider : DatabaseProviderBase
8 | {
9 | private static readonly IReadOnlyList _providers =
10 | [
11 | new (ProviderName.MySql , "Detect Dialect Automatically", true),
12 | new (ProviderName.MySql57MySqlConnector , "MySql 5.7"),
13 | new (ProviderName.MySql80MySqlConnector , "MySql 8 & 9"),
14 | new (ProviderName.MariaDB10MySqlConnector, "MariaDB"),
15 | new ("MySqlConnector" , "removed since v6", IsHidden: true),
16 | ];
17 |
18 | public MySqlProvider()
19 | : base(ProviderName.MySql, "MySql/MariaDB", _providers)
20 | {
21 | }
22 |
23 | public override void ClearAllPools(string providerName)
24 | {
25 | MySqlConnection.ClearAllPools();
26 | }
27 |
28 | public override DateTime? GetLastSchemaUpdate(ConnectionSettings settings)
29 | {
30 | using var db = new LINQPadDataConnection(settings);
31 | return db.Query("SELECT MAX(u.time) FROM (SELECT MAX(UPDATE_TIME) AS time FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() UNION SELECT MAX(CREATE_TIME) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() UNION SELECT MAX(LAST_ALTERED) FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = DATABASE()) as u").FirstOrDefault();
32 | }
33 |
34 | public override DbProviderFactory GetProviderFactory(string providerName)
35 | {
36 | return MySqlConnectorFactory.Instance;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Source/DatabaseProviders/OracleProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Data.Common;
2 | using LinqToDB.Data;
3 | using Oracle.ManagedDataAccess.Client;
4 |
5 | namespace LinqToDB.LINQPad;
6 |
7 | internal sealed class OracleProvider : DatabaseProviderBase
8 | {
9 | private static readonly IReadOnlyList _providers =
10 | [
11 | new (ProviderName.Oracle , "Detect Dialect Automatically", true),
12 | new (ProviderName.Oracle11Managed, "Oracle 11g Dialect" ),
13 | new (ProviderName.OracleManaged , "Oracle 12c Dialect" ),
14 | ];
15 |
16 | public OracleProvider()
17 | : base(ProviderName.Oracle, "Oracle", _providers)
18 | {
19 | }
20 |
21 | public override void ClearAllPools(string providerName)
22 | {
23 | OracleConnection.ClearAllPools();
24 | }
25 |
26 | public override DateTime? GetLastSchemaUpdate(ConnectionSettings settings)
27 | {
28 | using var db = new LINQPadDataConnection(settings);
29 | return db.Query("SELECT MAX(LAST_DDL_TIME) FROM USER_OBJECTS WHERE OBJECT_TYPE IN ('TABLE', 'VIEW', 'INDEX', 'FUNCTION', 'PACKAGE', 'PACKAGE BODY', 'PROCEDURE', 'MATERIALIZED VIEW') AND STATUS = 'VALID'").FirstOrDefault();
30 | }
31 |
32 | public override DbProviderFactory GetProviderFactory(string providerName)
33 | {
34 | return OracleClientFactory.Instance;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Source/DatabaseProviders/PostgreSQLProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Data.Common;
2 | using Npgsql;
3 |
4 | namespace LinqToDB.LINQPad;
5 |
6 | internal sealed class PostgreSQLProvider : DatabaseProviderBase
7 | {
8 | private static readonly IReadOnlyList _providers =
9 | [
10 | new(ProviderName.PostgreSQL , "Detect Dialect Automatically", true),
11 | new(ProviderName.PostgreSQL92, "PostgreSQL 9.2 Dialect" ),
12 | new(ProviderName.PostgreSQL93, "PostgreSQL 9.3 Dialect" ),
13 | new(ProviderName.PostgreSQL95, "PostgreSQL 9.5 Dialect" ),
14 | new(ProviderName.PostgreSQL15, "PostgreSQL 15 Dialect" ),
15 | ];
16 |
17 | public PostgreSQLProvider()
18 | : base(ProviderName.PostgreSQL, "PostgreSQL", _providers)
19 | {
20 | }
21 |
22 | public override void ClearAllPools(string providerName)
23 | {
24 | NpgsqlConnection.ClearAllPools();
25 | }
26 |
27 | public override DateTime? GetLastSchemaUpdate(ConnectionSettings settings)
28 | {
29 | // no information in schema
30 | return null;
31 | }
32 |
33 | public override DbProviderFactory GetProviderFactory(string providerName)
34 | {
35 | return NpgsqlFactory.Instance;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Source/DatabaseProviders/ProviderInfo.cs:
--------------------------------------------------------------------------------
1 | namespace LinqToDB.LINQPad;
2 |
3 | ///
4 | /// Database provider descriptor for specific database.
5 | ///
6 | /// Provider identifier (e.g. value from class).
7 | /// Provider display name in settings dialog.
8 | /// When set, specified provider dialect will be selected automatically.
9 | /// When set, specified provider will not be shown in list of available dialects and used only to support old connections with provider names, existed in older releases.
10 | internal sealed record ProviderInfo(string Name, string DisplayName, bool IsDefault = false, bool IsHidden = false);
11 |
--------------------------------------------------------------------------------
/Source/DatabaseProviders/SQLiteProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Data.Common;
2 | using System.Data.SQLite;
3 |
4 | namespace LinqToDB.LINQPad;
5 |
6 | internal sealed class SQLiteProvider : DatabaseProviderBase
7 | {
8 | private static readonly IReadOnlyList _providers =
9 | [
10 | new(ProviderName.SQLiteClassic, "SQLite")
11 | ];
12 |
13 | public SQLiteProvider()
14 | : base(ProviderName.SQLite, "SQLite", _providers)
15 | {
16 | }
17 |
18 | public override void ClearAllPools(string providerName)
19 | {
20 | SQLiteConnection.ClearAllPools();
21 | }
22 |
23 | public override DateTime? GetLastSchemaUpdate(ConnectionSettings settings)
24 | {
25 | // no information in schema
26 | return null;
27 | }
28 |
29 | public override DbProviderFactory GetProviderFactory(string providerName)
30 | {
31 | return SQLiteFactory.Instance;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Source/DatabaseProviders/SapHanaProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Data.Common;
2 | using System.Data.Odbc;
3 | using LinqToDB.Data;
4 | #if !NETFRAMEWORK
5 | using System.IO;
6 | #endif
7 |
8 | namespace LinqToDB.LINQPad;
9 |
10 | internal sealed class SapHanaProvider : DatabaseProviderBase
11 | {
12 | private static readonly IReadOnlyList _providers =
13 | [
14 | new(ProviderName.SapHanaNative, "Native Provider (Sap.Data.Hana)"),
15 | new(ProviderName.SapHanaOdbc , "ODBC Provider (HANAODBC/HANAODBC32)"),
16 | ];
17 |
18 | public SapHanaProvider()
19 | : base(ProviderName.SapHana, "SAP HANA", _providers)
20 | {
21 | }
22 |
23 | public override string? GetProviderDownloadUrl(string? providerName)
24 | {
25 | return "https://tools.hana.ondemand.com/#hanatools";
26 | }
27 |
28 | #if NETFRAMEWORK
29 | private const string UnmanagedAssemblyName = "Sap.Data.Hana.v4.5";
30 | #else
31 | private const string UnmanagedAssemblyName = "Sap.Data.Hana.Core.v2.1";
32 | #endif
33 |
34 | public override void ClearAllPools(string providerName)
35 | {
36 | if (providerName == ProviderName.SapHanaOdbc)
37 | OdbcConnection.ReleaseObjectPool();
38 | else if (providerName == ProviderName.SapHanaNative)
39 | {
40 | // TODO: restore in next version
41 | //var typeName = $"{SapHanaProviderAdapter.UnmanagedClientNamespace}.HanaConnection, {SapHanaProviderAdapter.UnmanagedAssemblyName}";
42 | var typeName = $"Sap.Data.Hana.HanaConnection, {UnmanagedAssemblyName}";
43 | Type.GetType(typeName, false)?.GetMethod("ClearAllPools", BindingFlags.Public | BindingFlags.Static)?.Invoke(null, null);
44 | }
45 | }
46 |
47 | public override DateTime? GetLastSchemaUpdate(ConnectionSettings settings)
48 | {
49 | using var db = new LINQPadDataConnection(settings);
50 | return db.Query("SELECT MAX(time) FROM (SELECT MAX(CREATE_TIME) AS time FROM M_CS_TABLES UNION SELECT MAX(MODIFY_TIME) FROM M_CS_TABLES UNION SELECT MAX(CREATE_TIME) FROM M_RS_TABLES UNION SELECT MAX(CREATE_TIME) FROM PROCEDURES UNION SELECT MAX(CREATE_TIME) FROM FUNCTIONS)").FirstOrDefault();
51 | }
52 |
53 | public override DbProviderFactory GetProviderFactory(string providerName)
54 | {
55 | if (providerName == ProviderName.SapHanaOdbc)
56 | return OdbcFactory.Instance;
57 |
58 | // TODO: restore in next version
59 | //var typeName = $"{SapHanaProviderAdapter.UnmanagedClientNamespace}.HanaFactory, {SapHanaProviderAdapter.UnmanagedAssemblyName}";
60 | var typeName = $"Sap.Data.Hana.HanaFactory, {UnmanagedAssemblyName}";
61 | return (DbProviderFactory)Type.GetType(typeName, false)?.GetField("Instance", BindingFlags.Public | BindingFlags.Static)?.GetValue(null)!;
62 | }
63 |
64 | #if !NETFRAMEWORK
65 | public override bool IsProviderPathSupported(string providerName)
66 | {
67 | return providerName == ProviderName.SapHanaNative;
68 | }
69 |
70 | public override string? GetProviderAssemblyName(string providerName)
71 | {
72 | return providerName == ProviderName.SapHanaNative ? "Sap.Data.Hana.Core.v2.1.dll" : null;
73 | }
74 |
75 | public override string? TryGetDefaultPath(string providerName)
76 | {
77 | if (providerName == ProviderName.SapHanaNative)
78 | {
79 | var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
80 |
81 | if (!string.IsNullOrEmpty(programFiles))
82 | {
83 |
84 | var path = Path.Combine(programFiles, "sap\\hdbclient\\dotnetcore\\v2.1\\Sap.Data.Hana.Core.v2.1.dll");
85 |
86 | if (File.Exists(path))
87 | return path;
88 | }
89 | }
90 |
91 | return null;
92 | }
93 |
94 | private static bool _factoryRegistered;
95 | public override void RegisterProviderFactory(string providerName, string providerPath)
96 | {
97 | if (providerName == ProviderName.SapHanaNative && !_factoryRegistered)
98 | {
99 | if (!File.Exists(providerPath))
100 | throw new LinqToDBLinqPadException($"Cannot find SAP HANA provider assembly at '{providerPath}'");
101 |
102 | try
103 | {
104 | var sapHanaAssembly = Assembly.LoadFrom(providerPath);
105 | DbProviderFactories.RegisterFactory("Sap.Data.Hana", sapHanaAssembly.GetType("Sap.Data.Hana.HanaFactory")!);
106 | _factoryRegistered = true;
107 | }
108 | catch (Exception ex)
109 | {
110 | throw new LinqToDBLinqPadException($"Failed to initialize SAP HANA provider factory: ({ex.GetType().Name}) {ex.Message}");
111 | }
112 | }
113 | }
114 | #endif
115 | }
116 |
--------------------------------------------------------------------------------
/Source/DatabaseProviders/SqlCeProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Data.Common;
2 | #if !NETFRAMEWORK
3 | using System.IO;
4 | #endif
5 |
6 | namespace LinqToDB.LINQPad;
7 |
8 | internal sealed class SqlCeProvider : DatabaseProviderBase
9 | {
10 | private static readonly IReadOnlyList _providers =
11 | [
12 | new (ProviderName.SqlCe, "Microsoft SQL Server Compact Edition")
13 | ];
14 |
15 | public SqlCeProvider()
16 | : base(ProviderName.SqlCe, "Microsoft SQL Server Compact Edition (SQL CE)", _providers)
17 | {
18 | }
19 |
20 | public override DateTime? GetLastSchemaUpdate(ConnectionSettings settings)
21 | {
22 | // no information in schema
23 | return null;
24 | }
25 |
26 | public override string? GetProviderDownloadUrl(string? providerName)
27 | {
28 | return "https://www.microsoft.com/en-us/download/details.aspx?id=30709";
29 | }
30 |
31 | public override void ClearAllPools(string providerName)
32 | {
33 | // connection pooling not supported by provider
34 | }
35 |
36 | public override DbProviderFactory GetProviderFactory(string providerName)
37 | {
38 | var typeName = "System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe";
39 | return (DbProviderFactory)Type.GetType(typeName, false)?.GetField("Instance", BindingFlags.Public | BindingFlags.Static)?.GetValue(null)!;
40 | }
41 |
42 | #if !NETFRAMEWORK
43 | public override bool IsProviderPathSupported(string providerName)
44 | {
45 | return true;
46 | }
47 |
48 | public override string? GetProviderAssemblyName(string providerName)
49 | {
50 | return "System.Data.SqlServerCe.dll";
51 | }
52 |
53 | public override string? TryGetDefaultPath(string providerName)
54 | {
55 | var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
56 | if (!string.IsNullOrEmpty(programFiles))
57 | {
58 | var path = Path.Combine(programFiles, "Microsoft SQL Server Compact Edition\\v4.0\\Private\\System.Data.SqlServerCe.dll");
59 |
60 | if (File.Exists(path))
61 | return path;
62 | }
63 |
64 | return null;
65 | }
66 |
67 | private static bool _factoryRegistered;
68 | public override void RegisterProviderFactory(string providerName, string providerPath)
69 | {
70 | if (_factoryRegistered)
71 | return;
72 |
73 | if (!File.Exists(providerPath))
74 | throw new LinqToDBLinqPadException($"Cannot find SQL CE provider assembly at '{providerPath}'");
75 |
76 | try
77 | {
78 | var assembly = Assembly.LoadFrom(providerPath);
79 | DbProviderFactories.RegisterFactory("System.Data.SqlServerCe.4.0", assembly.GetType("System.Data.SqlServerCe.SqlCeProviderFactory")!);
80 | _factoryRegistered = true;
81 | }
82 | catch (Exception ex)
83 | {
84 | throw new LinqToDBLinqPadException($"Failed to initialize SQL CE provider factory: ({ex.GetType().Name}) {ex.Message}");
85 | }
86 | }
87 | #endif
88 | }
89 |
--------------------------------------------------------------------------------
/Source/DatabaseProviders/SqlServerProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Data.Common;
2 | #if NETFRAMEWORK
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Runtime.CompilerServices;
6 | using System.Runtime.InteropServices;
7 | #endif
8 | using LinqToDB.Data;
9 | using LinqToDB.DataProvider;
10 | using LinqToDB.DataProvider.SqlServer;
11 | using Microsoft.Data.SqlClient;
12 | using Microsoft.SqlServer.Types;
13 |
14 | namespace LinqToDB.LINQPad;
15 |
16 | internal sealed class SqlServerProvider : DatabaseProviderBase
17 | {
18 | #if NETFRAMEWORK
19 | static SqlServerProvider()
20 | {
21 | var oldCurrent = Directory.GetCurrentDirectory();
22 | var newCurrent = Path.GetDirectoryName(_additionalAssemblies[0].Location);
23 |
24 | if (oldCurrent != newCurrent)
25 | Directory.SetCurrentDirectory(newCurrent);
26 |
27 | // This will trigger GLNativeMethods .cctor, which loads native runtime for spatial types from relative path
28 | // We need to reset current directory before it otherwise it fails to find runtime dll as LINQPad 5 default directory is LINQPad directory, not driver's dir
29 | var type = _additionalAssemblies[0].GetType("Microsoft.SqlServer.Types.GLNativeMethods");
30 | RuntimeHelpers.RunClassConstructor(type.TypeHandle);
31 |
32 | if (oldCurrent != newCurrent)
33 | Directory.SetCurrentDirectory(oldCurrent);
34 | }
35 |
36 | [DllImport("kernel32", SetLastError = true)]
37 | [DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
38 | private static extern bool FreeLibrary(IntPtr hModule);
39 |
40 | public static void UnloadModule(string moduleName)
41 | {
42 | }
43 |
44 | public override void Unload()
45 | {
46 | // must unload unmanaged dlls to allow LINQPad 5 to delete old driver instance without error
47 | // as SQL Server types lib doesn't implement cleanup for unloadable domains...
48 | foreach (ProcessModule mod in Process.GetCurrentProcess().Modules)
49 | if (string.Equals(mod.ModuleName, "SqlServerSpatial160.dll", StringComparison.OrdinalIgnoreCase))
50 | FreeLibrary(mod.BaseAddress);
51 | }
52 | #endif
53 |
54 | private static readonly IReadOnlyList _providers =
55 | [
56 | new (ProviderName.SqlServer , "Detect Dialect Automatically", true),
57 | new (ProviderName.SqlServer2005, "SQL Server 2005 Dialect" ),
58 | new (ProviderName.SqlServer2008, "SQL Server 2008 Dialect" ),
59 | new (ProviderName.SqlServer2012, "SQL Server 2012 Dialect" ),
60 | new (ProviderName.SqlServer2014, "SQL Server 2014 Dialect" ),
61 | new (ProviderName.SqlServer2016, "SQL Server 2016 Dialect" ),
62 | new (ProviderName.SqlServer2017, "SQL Server 2017 Dialect" ),
63 | new (ProviderName.SqlServer2019, "SQL Server 2019 Dialect" ),
64 | new (ProviderName.SqlServer2022, "SQL Server 2022 Dialect" ),
65 | ];
66 |
67 | public SqlServerProvider()
68 | : base(ProviderName.SqlServer, "Microsoft SQL Server", _providers)
69 | {
70 | }
71 |
72 | private static readonly IReadOnlyList _additionalAssemblies = [typeof(SqlHierarchyId).Assembly];
73 |
74 | public override void ClearAllPools(string providerName)
75 | {
76 | SqlConnection.ClearAllPools();
77 | }
78 |
79 | public override IReadOnlyCollection GetAdditionalReferences(string providerName)
80 | {
81 | return _additionalAssemblies;
82 | }
83 |
84 | public override DateTime? GetLastSchemaUpdate(ConnectionSettings settings)
85 | {
86 | using var db = new LINQPadDataConnection(settings);
87 | return db.Query("SELECT MAX(modify_date) FROM sys.objects").FirstOrDefault();
88 | }
89 |
90 | public override DbProviderFactory GetProviderFactory(string providerName)
91 | {
92 | return SqlClientFactory.Instance;
93 | }
94 |
95 | public override IDataProvider GetDataProvider(string providerName, string connectionString)
96 | {
97 | // provider detector fails to detect Microsoft.Data.SqlClient
98 | // kinda regression in linq2db v5
99 | return providerName switch
100 | {
101 | ProviderName.SqlServer2005 => SqlServerTools.GetDataProvider(SqlServerVersion.v2005 , DataProvider.SqlServer.SqlServerProvider.MicrosoftDataSqlClient, connectionString),
102 | ProviderName.SqlServer2008 => SqlServerTools.GetDataProvider(SqlServerVersion.v2008 , DataProvider.SqlServer.SqlServerProvider.MicrosoftDataSqlClient, connectionString),
103 | ProviderName.SqlServer2012 => SqlServerTools.GetDataProvider(SqlServerVersion.v2012 , DataProvider.SqlServer.SqlServerProvider.MicrosoftDataSqlClient, connectionString),
104 | ProviderName.SqlServer2014 => SqlServerTools.GetDataProvider(SqlServerVersion.v2014 , DataProvider.SqlServer.SqlServerProvider.MicrosoftDataSqlClient, connectionString),
105 | ProviderName.SqlServer2016 => SqlServerTools.GetDataProvider(SqlServerVersion.v2016 , DataProvider.SqlServer.SqlServerProvider.MicrosoftDataSqlClient, connectionString),
106 | ProviderName.SqlServer2017 => SqlServerTools.GetDataProvider(SqlServerVersion.v2017 , DataProvider.SqlServer.SqlServerProvider.MicrosoftDataSqlClient, connectionString),
107 | ProviderName.SqlServer2019 => SqlServerTools.GetDataProvider(SqlServerVersion.v2019 , DataProvider.SqlServer.SqlServerProvider.MicrosoftDataSqlClient, connectionString),
108 | ProviderName.SqlServer2022 => SqlServerTools.GetDataProvider(SqlServerVersion.v2022 , DataProvider.SqlServer.SqlServerProvider.MicrosoftDataSqlClient, connectionString),
109 | ProviderName.SqlServer => SqlServerTools.GetDataProvider(SqlServerVersion.AutoDetect, DataProvider.SqlServer.SqlServerProvider.MicrosoftDataSqlClient, connectionString),
110 | _ => base.GetDataProvider(providerName, connectionString)
111 | };
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/Source/DatabaseProviders/SybaseAseProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Data.Common;
2 | using AdoNetCore.AseClient;
3 | using LinqToDB.Data;
4 |
5 | namespace LinqToDB.LINQPad;
6 |
7 | internal sealed class SybaseAseProvider : DatabaseProviderBase
8 | {
9 | private static readonly IReadOnlyList _providers =
10 | [
11 | new(ProviderName.SybaseManaged, "SAP/Sybase ASE")
12 | ];
13 |
14 | public SybaseAseProvider()
15 | : base(ProviderName.Sybase, "SAP/Sybase ASE", _providers)
16 | {
17 | }
18 |
19 | public override void ClearAllPools(string providerName)
20 | {
21 | AseConnection.ClearPools();
22 | }
23 |
24 | public override DateTime? GetLastSchemaUpdate(ConnectionSettings settings)
25 | {
26 | using var db = new LINQPadDataConnection(settings);
27 | return db.Query("SELECT MAX(crdate) FROM sysobjects").FirstOrDefault();
28 | }
29 |
30 | public override DbProviderFactory GetProviderFactory(string providerName)
31 | {
32 | return AseClientFactory.Instance;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Source/Drivers/DriverHelper.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using LINQPad.Extensibility.DataContext;
3 | using LinqToDB.Data;
4 | using LinqToDB.LINQPad.UI;
5 | using LinqToDB.Mapping;
6 |
7 |
8 | #if NETFRAMEWORK
9 | using System.Text.Json;
10 | using System.Numerics;
11 | using System.Buffers;
12 | using System.Runtime.CompilerServices;
13 | //using System.Collections.Immutable;
14 | using System.Diagnostics;
15 | //using System.Reflection.Metadata;
16 | using Microsoft.Extensions.Logging;
17 | #endif
18 |
19 | namespace LinqToDB.LINQPad;
20 |
21 | ///
22 | /// Contains shared driver code for dynamic (scaffolded) and static (precompiled) drivers.
23 | ///
24 | internal static class DriverHelper
25 | {
26 | public const string Name = "Linq To DB";
27 | public const string Author = "Linq To DB Team";
28 |
29 | ///
30 | /// Returned by method implementation.
31 | ///
32 | public static readonly IReadOnlyCollection DefaultImports =
33 | [
34 | "LinqToDB",
35 | "LinqToDB.Data",
36 | "LinqToDB.Mapping"
37 | ];
38 |
39 | ///
40 | /// Initialization method, called from driver's static constructor.
41 | ///
42 | public static void Init()
43 | {
44 | #if NETFRAMEWORK
45 | // Dynamically resolve assembly bindings to currently used assembly version for transitive dependencies. Used by.NET Framework build (LINQPad 5).
46 |
47 | // manage transitive dependencies dll hell
48 | // separate resolvers registered to avoid resolve errors from resolvers itself
49 |
50 | // linq2db version resolver could be needed for:
51 | // - iSeries provider
52 | // - static contexts
53 | RegisterResolver("linq2db", static () => typeof(DataContext).Assembly);
54 |
55 | RegisterResolver("System.Threading.Tasks.Extensions", static () => typeof(ValueTask).Assembly);
56 | RegisterResolver("System.Runtime.CompilerServices.Unsafe", static () => typeof(Unsafe).Assembly);
57 | RegisterResolver("System.Numerics.Vectors", static () => typeof(Vector).Assembly);
58 | RegisterResolver("System.Memory", static () => typeof(Span<>).Assembly);
59 | RegisterResolver("System.Buffers", static () => typeof(ArrayPool<>).Assembly);
60 | RegisterResolver("System.Text.Json", static () => typeof(JsonDocument).Assembly);
61 | RegisterResolver("System.Diagnostics.DiagnosticSource", static () => typeof(DiagnosticSource).Assembly);
62 | RegisterResolver("Microsoft.Bcl.AsyncInterfaces", static () => typeof(IAsyncDisposable).Assembly);
63 | RegisterResolver("Microsoft.Extensions.Logging.Abstractions", static () => typeof(ILogger).Assembly);
64 |
65 | // not needed anymore?
66 | //RegisterResolver("System.Collections.Immutable", static () => typeof(ImmutableArray).Assembly);
67 | //RegisterResolver("System.Reflection.Metadata", static () => typeof(Blob).Assembly);
68 |
69 | AppDomain.CurrentDomain.DomainUnload += static (_, _) => DatabaseProviders.Unload();
70 |
71 | static void RegisterResolver(string asemblyName, Func resolver)
72 | {
73 | AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
74 | {
75 | var requestedAssembly = new AssemblyName(args.Name!);
76 |
77 | if (requestedAssembly.Name == asemblyName)
78 | return resolver();
79 |
80 | return null;
81 | };
82 | }
83 | #endif
84 |
85 | DatabaseProviders.Init();
86 | }
87 |
88 | ///
89 | /// Implements method.
90 | ///
91 | public static MappingSchema InitializeContext(IConnectionInfo cxInfo, IDataContext context, QueryExecutionManager executionManager)
92 | {
93 | try
94 | {
95 | var settings = ConnectionSettings.Load(cxInfo);
96 |
97 | // apply context-specific Linq To DB options
98 | Common.Configuration.Linq.OptimizeJoins = settings.LinqToDB.OptimizeJoins;
99 |
100 | // TODO: add OnTraceConnection to IDataContext
101 | if (context is DataConnection dc)
102 | dc.OnTraceConnection = GetSqlLogAction(executionManager);
103 | else if (context is DataContext dctx)
104 | dctx.OnTraceConnection = GetSqlLogAction(executionManager);
105 | else
106 | {
107 | // Try to find a OnTraceConnection property in the custom context object
108 | try
109 | {
110 | context.GetType().GetProperty("OnTraceConnection")?.SetValue(context, GetSqlLogAction(executionManager));
111 | }
112 | catch
113 | {
114 | // not our problem
115 | }
116 | }
117 |
118 | DataConnection.TurnTraceSwitchOn();
119 |
120 | return context.MappingSchema;
121 |
122 | // Implements Linq To DB connection logging handler to feed SQL logs to LINQPad.
123 | static Action GetSqlLogAction(QueryExecutionManager executionManager)
124 | {
125 | return info =>
126 | {
127 | switch (info.TraceInfoStep)
128 | {
129 | case TraceInfoStep.BeforeExecute:
130 | // log SQL query
131 | executionManager.SqlTranslationWriter.WriteLine(info.SqlText);
132 | break;
133 | case TraceInfoStep.Error:
134 | // log error
135 | if (info.Exception != null)
136 | {
137 | for (var ex = info.Exception; ex != null; ex = ex.InnerException)
138 | {
139 | executionManager.SqlTranslationWriter.WriteLine();
140 | executionManager.SqlTranslationWriter.WriteLine("/*");
141 | executionManager.SqlTranslationWriter.WriteLine($"Exception: {ex.GetType()}");
142 | executionManager.SqlTranslationWriter.WriteLine($"Message : {ex.Message}");
143 | executionManager.SqlTranslationWriter.WriteLine(ex.StackTrace);
144 | executionManager.SqlTranslationWriter.WriteLine("*/");
145 | }
146 | }
147 | break;
148 | case TraceInfoStep.Completed:
149 | // log data reader execution stats
150 | executionManager.SqlTranslationWriter.WriteLine(FormattableString.Invariant($"-- Data read time: {info.ExecutionTime}. Records fetched: {info.RecordsAffected}.\r\n"));
151 | break;
152 | case TraceInfoStep.AfterExecute:
153 | // log query execution stats
154 | if (info.RecordsAffected != null)
155 | executionManager.SqlTranslationWriter.WriteLine(FormattableString.Invariant($"-- Execution time: {info.ExecutionTime}. Records affected: {info.RecordsAffected}.\r\n"));
156 | else
157 | executionManager.SqlTranslationWriter.WriteLine(FormattableString.Invariant($"-- Execution time: {info.ExecutionTime}\r\n"));
158 | break;
159 | }
160 | };
161 | }
162 | }
163 | catch (Exception ex)
164 | {
165 | HandleException(ex, nameof(InitializeContext));
166 | return MappingSchema.Default;
167 | }
168 | }
169 |
170 | ///
171 | /// Implements method.
172 | ///
173 | public static string GetConnectionDescription(IConnectionInfo cxInfo)
174 | {
175 | try
176 | {
177 | var settings = ConnectionSettings.Load(cxInfo);
178 |
179 | // this is default connection name string in connecion explorer when user doesn't specify own name
180 | return $"[Linq To DB: {settings.Connection.Provider}] {settings.Connection.Server}\\{settings.Connection.DatabaseName} (v.{settings.Connection.DbVersion})";
181 | }
182 | catch (Exception ex)
183 | {
184 | HandleException(ex, nameof(GetConnectionDescription));
185 | return "Error";
186 | }
187 | }
188 |
189 | ///
190 | /// Implements method.
191 | ///
192 | public static void ClearConnectionPools(IConnectionInfo cxInfo)
193 | {
194 | try
195 | {
196 | var settings = ConnectionSettings.Load(cxInfo);
197 | DatabaseProviders.GetProvider(settings.Connection.Database).ClearAllPools(settings.Connection.Provider!);
198 | }
199 | catch (Exception ex)
200 | {
201 | HandleException(ex, nameof(ClearConnectionPools));
202 | }
203 | }
204 |
205 | public static bool ShowConnectionDialog(IConnectionInfo cxInfo, ConnectionDialogOptions dialogOptions, bool isDynamic)
206 | {
207 | var settings = ConnectionSettings.Load(cxInfo);
208 | var model = new SettingsModel(settings, !isDynamic);
209 |
210 | if (SettingsDialog.Show(
211 | model,
212 | isDynamic ? TestDynamicConnection : TestStaticConnection,
213 | isDynamic ? "Connection to database failed." : "Invalid configuration."))
214 | {
215 | model.Save();
216 | settings.Save(cxInfo);
217 | return true;
218 | }
219 |
220 | return false;
221 |
222 | static Exception? TestStaticConnection(SettingsModel model)
223 | {
224 | try
225 | {
226 | // basic checks
227 | if (model.StaticConnection.ContextAssemblyPath == null)
228 | throw new LinqToDBLinqPadException("Data context assembly not specified");
229 |
230 | if (model.StaticConnection.ContextTypeName == null)
231 | throw new LinqToDBLinqPadException("Data context class not specified");
232 |
233 | return null;
234 | }
235 | catch (Exception ex)
236 | {
237 | return ex;
238 | }
239 | }
240 |
241 | static Exception? TestDynamicConnection(SettingsModel model)
242 | {
243 | try
244 | {
245 | // TODO: add secondary connection test
246 | if (model.DynamicConnection.Database == null)
247 | throw new LinqToDBLinqPadException("Database is not selected");
248 |
249 | if (model.DynamicConnection.Provider == null)
250 | throw new LinqToDBLinqPadException("Database provider is not selected");
251 |
252 | if (model.DynamicConnection.ConnectionString == null)
253 | throw new LinqToDBLinqPadException("Connection string is not specified");
254 |
255 | if (model.DynamicConnection.SecondaryProvider != null
256 | && model.DynamicConnection.Provider.Name == model.DynamicConnection.SecondaryProvider.Name)
257 | throw new LinqToDBLinqPadException("Secondary connection shouldn't use same provider type as primary connection");
258 |
259 | if (model.DynamicConnection.Database.IsProviderPathSupported(model.DynamicConnection.Provider.Name))
260 | {
261 | if (model.DynamicConnection.ProviderPath == null)
262 | throw new LinqToDBLinqPadException("Provider path is not specified");
263 | if (!File.Exists(model.DynamicConnection.ProviderPath))
264 | throw new LinqToDBLinqPadException($"Cannot access provider assembly at {model.DynamicConnection.ProviderPath}");
265 | }
266 |
267 | var connectionString = PasswordManager.ResolvePasswordManagerFields(model.DynamicConnection.ConnectionString);
268 | var provider = DatabaseProviders.GetDataProvider(model.DynamicConnection.Provider.Name, connectionString, model.DynamicConnection.ProviderPath);
269 | using (var con = provider.CreateConnection(connectionString))
270 | con.Open();
271 |
272 | if (model.DynamicConnection.Database.SupportsSecondaryConnection
273 | && model.DynamicConnection.SecondaryProvider != null
274 | && model.DynamicConnection.SecondaryConnectionString != null)
275 | {
276 | var secondaryConnectionString = PasswordManager.ResolvePasswordManagerFields(model.DynamicConnection.SecondaryConnectionString);
277 | var secondaryProvider = DatabaseProviders.GetDataProvider(model.DynamicConnection.SecondaryProvider.Name, secondaryConnectionString, null);
278 | using var con = secondaryProvider.CreateConnection(secondaryConnectionString);
279 | con.Open();
280 | }
281 |
282 | return null;
283 | }
284 | catch (Exception ex)
285 | {
286 | return ex;
287 | }
288 | }
289 | }
290 |
291 | // intercepts exceptions from driver to linqpad
292 | public static void HandleException(Exception ex, string method)
293 | {
294 | Notification.Error($"Unhandled error in method '{method}': {ex.Message}\r\n{ex.StackTrace}", "Linq To DB Driver Error");
295 | }
296 |
297 | public static IEnumerable GetAssembliesToAdd(IConnectionInfo cxInfo)
298 | {
299 | #if !NETFRAMEWORK
300 | yield return "*";
301 | #endif
302 | yield return typeof(DataConnection).Assembly.Location;
303 | yield return typeof(LINQPadDataConnection).Assembly.Location;
304 |
305 | var settings = ConnectionSettings.Load(cxInfo);
306 |
307 | Type cnType;
308 | IDatabaseProvider provider;
309 | try
310 | {
311 | provider = DatabaseProviders.GetProvider(settings.Connection.Database);
312 | using var cn = DatabaseProviders.CreateConnection(ConnectionSettings.Load(cxInfo));
313 | cnType = cn.GetType();
314 | }
315 | catch (Exception ex)
316 | {
317 | HandleException(ex, nameof(GetAssembliesToAdd));
318 | yield break;
319 | }
320 |
321 | foreach (var assembly in provider.GetAdditionalReferences(settings.Connection.Provider!))
322 | yield return assembly.FullName!;
323 |
324 | yield return cnType.Assembly.Location;
325 | }
326 | }
327 |
--------------------------------------------------------------------------------
/Source/Drivers/DynamicLinqToDBDriver.cs:
--------------------------------------------------------------------------------
1 | using System.Data;
2 | using System.Data.Common;
3 | using System.IO;
4 | using LINQPad.Extensibility.DataContext;
5 | using LinqToDB.Data;
6 | using LinqToDB.Mapping;
7 | using Microsoft.CodeAnalysis;
8 | using Microsoft.CodeAnalysis.CSharp;
9 | #if NETFRAMEWORK
10 | using System.Net.NetworkInformation;
11 | using System.Numerics;
12 | #else
13 | using System.Text.RegularExpressions;
14 | #endif
15 |
16 | namespace LinqToDB.LINQPad;
17 |
18 | // IMPORTANT:
19 | // 1. driver must be public or it will be missing from create connection dialog (existing connections will work)
20 | // 2. don't rename class or namespace as it is used by LINQPad as driver identifier. If renamed, old connections will disappear from UI
21 | ///
22 | /// Implements LINQPad driver for synamic (scaffolded from DB schema) model.
23 | ///
24 | public sealed partial class LinqToDBDriver : DynamicDataContextDriver
25 | {
26 | private MappingSchema? _mappingSchema;
27 |
28 | ///
29 | public override string Name => DriverHelper.Name;
30 | ///
31 | public override string Author => DriverHelper.Author;
32 |
33 | static LinqToDBDriver() => DriverHelper.Init();
34 |
35 | ///
36 | public override string GetConnectionDescription(IConnectionInfo cxInfo) => DriverHelper.GetConnectionDescription(cxInfo);
37 |
38 | ///
39 | public override DateTime? GetLastSchemaUpdate(IConnectionInfo cxInfo)
40 | {
41 | try
42 | {
43 | var settings = ConnectionSettings.Load(cxInfo);
44 | return DatabaseProviders.GetProvider(settings.Connection.Database).GetLastSchemaUpdate(settings);
45 | }
46 | catch (Exception ex)
47 | {
48 | DriverHelper.HandleException(ex, nameof(GetLastSchemaUpdate));
49 | return null;
50 | }
51 | }
52 |
53 | ///
54 | public override bool ShowConnectionDialog(IConnectionInfo cxInfo, ConnectionDialogOptions dialogOptions) => DriverHelper.ShowConnectionDialog(cxInfo, dialogOptions, true);
55 |
56 | #if !NETFRAMEWORK
57 | [GeneratedRegex(@"^.+\\(?[^\\]+)\\[^\\]+$", RegexOptions.ExplicitCapture | RegexOptions.Compiled)]
58 | private static partial Regex RuntimeTokenExtractor();
59 |
60 | private static IEnumerable GetFallbackTokens(string forToken)
61 | {
62 | switch (forToken)
63 | {
64 | case "net9.0":
65 | yield return "net9.0";
66 | goto case "net8.0";
67 | case "net8.0":
68 | yield return "net8.0";
69 | goto case "net7.0";
70 | case "net7.0":
71 | yield return "net7.0";
72 | goto case "net6.0";
73 | case "net6.0":
74 | yield return "net6.0";
75 | goto case "net5.0";
76 | case "net5.0":
77 | yield return "net5.0";
78 | goto case "netcoreapp3.1";
79 | case "netcoreapp3.1":
80 | yield return "netcoreapp3.1";
81 | goto case "netstandard2.1";
82 | case "netstandard2.1":
83 | yield return "netstandard2.1";
84 | yield return "netstandard2.0";
85 | yield break;
86 | }
87 |
88 | yield return forToken;
89 | }
90 |
91 | private PortableExecutableReference MakeReferenceByRuntime(string runtimeToken, string reference)
92 | {
93 | var token = RuntimeTokenExtractor().Match(reference).Groups["token"].Value;
94 |
95 | foreach (var fallback in GetFallbackTokens(runtimeToken))
96 | {
97 | if (token == fallback)
98 | return MetadataReference.CreateFromFile(reference);
99 |
100 | var newReference = reference.Replace($"\\{token}\\", $"\\{fallback}\\", StringComparison.Ordinal);
101 |
102 | if (File.Exists(newReference))
103 | return MetadataReference.CreateFromFile(newReference);
104 | }
105 |
106 | return MetadataReference.CreateFromFile(reference);
107 | }
108 | #endif
109 |
110 | ///
111 | public override List GetSchemaAndBuildAssembly(IConnectionInfo cxInfo, AssemblyName assemblyToBuild, ref string? nameSpace, ref string typeName)
112 | {
113 | try
114 | {
115 | var settings = ConnectionSettings.Load(cxInfo);
116 | var (items, text, providerAssemblyLocation) = DynamicSchemaGenerator.GetModel(settings, ref nameSpace, ref typeName);
117 | var syntaxTree = CSharpSyntaxTree.ParseText(text);
118 |
119 | #if !NETFRAMEWORK
120 | // TODO: find better way to do it
121 | // hack to overwrite provider assembly references that target wrong runtime
122 | // e.g. referenceAssemblies contains path to net5 MySqlConnector
123 | // but GetCoreFxReferenceAssemblies returns netcoreapp3.1 runtime references
124 | var coreAssemblies = GetCoreFxReferenceAssemblies(cxInfo);
125 | var runtimeToken = RuntimeTokenExtractor().Match(coreAssemblies[0]).Groups["token"].Value;
126 | #endif
127 | var references = new List()
128 | {
129 | #if NETFRAMEWORK
130 | MetadataReference.CreateFromFile(typeof(object). Assembly.Location),
131 | MetadataReference.CreateFromFile(typeof(Enumerable). Assembly.Location),
132 | #pragma warning disable RS0030 // Do not use banned APIs
133 | MetadataReference.CreateFromFile(typeof(IDbConnection). Assembly.Location),
134 | #pragma warning restore RS0030 // Do not use banned APIs
135 | MetadataReference.CreateFromFile(typeof(PhysicalAddress) .Assembly.Location),
136 | MetadataReference.CreateFromFile(typeof(BigInteger) .Assembly.Location),
137 | MetadataReference.CreateFromFile(typeof(IAsyncDisposable) .Assembly.Location),
138 | MetadataReference.CreateFromFile(Path.Combine(Path.GetDirectoryName(typeof(string).Assembly.Location), "netstandard.dll")),
139 | #endif
140 | #if NETFRAMEWORK
141 | MetadataReference.CreateFromFile(typeof(DataConnection). Assembly.Location),
142 | #else
143 | MakeReferenceByRuntime(runtimeToken, typeof(DataConnection).Assembly.Location),
144 | #endif
145 | MetadataReference.CreateFromFile(typeof(LINQPadDataConnection).Assembly.Location),
146 | };
147 |
148 | foreach (var assembly in DatabaseProviders.GetProvider(settings.Connection.Database).GetAdditionalReferences(settings.Connection.Provider!))
149 | references.Add(MetadataReference.CreateFromFile(assembly.Location));
150 |
151 | #if !NETFRAMEWORK
152 | references.Add(MakeReferenceByRuntime(runtimeToken, providerAssemblyLocation));
153 | references.AddRange(coreAssemblies.Select(static path => MetadataReference.CreateFromFile(path)));
154 | #else
155 | references.Add(MetadataReference.CreateFromFile(providerAssemblyLocation));
156 | #endif
157 |
158 | var compilation = CSharpCompilation.Create(
159 | assemblyToBuild.Name!,
160 | syntaxTrees : [syntaxTree],
161 | references : references,
162 | options : new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
163 |
164 | #pragma warning disable SYSLIB0044 // Type or member is obsolete
165 | using (var stream = new FileStream(assemblyToBuild.CodeBase!, FileMode.Create))
166 | #pragma warning restore SYSLIB0044 // Type or member is obsolete
167 | {
168 | var result = compilation.Emit(stream);
169 |
170 | if (!result.Success)
171 | {
172 | var failures = result.Diagnostics.Where(static diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error);
173 |
174 | foreach (var diagnostic in failures)
175 | throw new LinqToDBLinqPadException(diagnostic.ToString());
176 | }
177 | }
178 |
179 | return items;
180 | }
181 | catch (Exception ex)
182 | {
183 | Notification.Error($"{ex}\n{ex.StackTrace}", "Schema Build Error");
184 | throw;
185 | }
186 | }
187 |
188 | private static readonly ParameterDescriptor[] _contextParameters =
189 | [
190 | new ParameterDescriptor("provider", typeof(string).FullName),
191 | new ParameterDescriptor("providerPath", typeof(string).FullName),
192 | new ParameterDescriptor("connectionString", typeof(string).FullName),
193 | ];
194 |
195 | ///
196 | public override ParameterDescriptor[] GetContextConstructorParameters(IConnectionInfo cxInfo) => _contextParameters;
197 |
198 | ///
199 | public override object?[] GetContextConstructorArguments(IConnectionInfo cxInfo)
200 | {
201 | try
202 | {
203 | var settings = ConnectionSettings.Load(cxInfo);
204 |
205 | return
206 | [
207 | settings.Connection.Provider,
208 | settings.Connection.ProviderPath,
209 | settings.Connection.GetFullConnectionString()
210 | ];
211 | }
212 | catch (Exception ex)
213 | {
214 | DriverHelper.HandleException(ex, nameof(GetContextConstructorArguments));
215 | return new object[3];
216 | }
217 | }
218 |
219 | ///
220 | public override IEnumerable GetAssembliesToAdd(IConnectionInfo cxInfo) => DriverHelper.GetAssembliesToAdd(cxInfo);
221 |
222 | ///
223 | public override IEnumerable GetNamespacesToAdd(IConnectionInfo cxInfo) => DriverHelper.DefaultImports;
224 |
225 | ///
226 | public override void ClearConnectionPools(IConnectionInfo cxInfo) => DriverHelper.ClearConnectionPools(cxInfo);
227 |
228 | ///
229 | public override void InitializeContext(IConnectionInfo cxInfo, object context, QueryExecutionManager executionManager)
230 | {
231 | _mappingSchema = DriverHelper.InitializeContext(cxInfo, (DataConnection)context, executionManager);
232 | }
233 |
234 | ///
235 | public override void TearDownContext(IConnectionInfo cxInfo, object context, QueryExecutionManager executionManager, object[] constructorArguments)
236 | {
237 | try
238 | {
239 | ((DataConnection)context).Dispose();
240 | }
241 | catch (Exception ex)
242 | {
243 | DriverHelper.HandleException(ex, nameof(TearDownContext));
244 | }
245 | }
246 |
247 | ///
248 | public override IDbConnection GetIDbConnection(IConnectionInfo cxInfo)
249 | {
250 | try
251 | {
252 | return DatabaseProviders.CreateConnection(ConnectionSettings.Load(cxInfo));
253 | }
254 | catch (Exception ex)
255 | {
256 | DriverHelper.HandleException(ex, nameof(GetIDbConnection));
257 | throw;
258 | }
259 | }
260 |
261 | ///
262 | public override void PreprocessObjectToWrite(ref object objectToWrite, ObjectGraphInfo info)
263 | {
264 | try
265 | {
266 | objectToWrite = ValueFormatter.Format(objectToWrite);
267 | }
268 | catch (Exception ex)
269 | {
270 | DriverHelper.HandleException(ex, nameof(PreprocessObjectToWrite));
271 | }
272 | }
273 |
274 | ///
275 | public override DbProviderFactory GetProviderFactory(IConnectionInfo cxInfo)
276 | {
277 | try
278 | {
279 | return DatabaseProviders.GetProviderFactory(ConnectionSettings.Load(cxInfo));
280 | }
281 | catch (Exception ex)
282 | {
283 | DriverHelper.HandleException(ex, nameof(GetProviderFactory));
284 | throw;
285 | }
286 | }
287 | }
288 |
--------------------------------------------------------------------------------
/Source/Drivers/DynamicSchemaGenerator.cs:
--------------------------------------------------------------------------------
1 | using LINQPad.Extensibility.DataContext;
2 | using LinqToDB.CodeModel;
3 | using LinqToDB.Data;
4 | using LinqToDB.DataModel;
5 | using LinqToDB.Metadata;
6 | using LinqToDB.Naming;
7 | using LinqToDB.Scaffold;
8 | using LinqToDB.Schema;
9 |
10 | namespace LinqToDB.LINQPad;
11 |
12 | internal static class DynamicSchemaGenerator
13 | {
14 | private static ScaffoldOptions GetOptions(ConnectionSettings settings, string? contextNamespace, string contextName)
15 | {
16 | var options = ScaffoldOptions.Default();
17 |
18 | // set schema load options
19 | options.Schema.IncludeSchemas = settings.Schema.IncludeSchemas;
20 | foreach (var schema in (settings.Schema.Schemas ?? (IEnumerable)[]))
21 | options.Schema.Schemas.Add(schema);
22 |
23 | options.Schema.IncludeCatalogs = settings.Schema.IncludeCatalogs;
24 | foreach (var catalog in (settings.Schema.Catalogs ?? (IEnumerable)[]))
25 | options.Schema.Catalogs.Add(catalog);
26 |
27 | options.Schema.LoadedObjects = SchemaObjects.Table | SchemaObjects.View;
28 |
29 | if (settings.Schema.LoadForeignKeys ) options.Schema.LoadedObjects |= SchemaObjects.ForeignKey;
30 | if (settings.Schema.LoadProcedures ) options.Schema.LoadedObjects |= SchemaObjects.StoredProcedure;
31 | if (settings.Schema.LoadTableFunctions ) options.Schema.LoadedObjects |= SchemaObjects.TableFunction;
32 | if (settings.Schema.LoadScalarFunctions ) options.Schema.LoadedObjects |= SchemaObjects.ScalarFunction;
33 | if (settings.Schema.LoadAggregateFunctions) options.Schema.LoadedObjects |= SchemaObjects.AggregateFunction;
34 |
35 | options.Schema.PreferProviderSpecificTypes = settings.Scaffold.UseProviderTypes;
36 | options.Schema.IgnoreDuplicateForeignKeys = false;
37 | options.Schema.UseSafeSchemaLoad = false;
38 | options.Schema.LoadDatabaseName = false;
39 | options.Schema.LoadProceduresSchema = true;
40 | // TODO: disabled due to generation bug in current scaffolder
41 | options.Schema.EnableSqlServerReturnValue = false;
42 | //options.Schema.EnableSqlServerReturnValue = true;
43 |
44 | // set data model options
45 | if (settings.Scaffold.AsIsNames)
46 | {
47 | // https://github.com/linq2db/linq2db.LINQPad/issues/89
48 | // reset naming options for some objects:
49 | // - entities
50 | // - context properties
51 | // - columns
52 | // more could be added later on request
53 | options.DataModel.EntityClassNameOptions.Casing = NameCasing.None;
54 | options.DataModel.EntityClassNameOptions.Pluralization = Pluralization.None;
55 | options.DataModel.EntityClassNameOptions.Transformation = NameTransformation.None;
56 | options.DataModel.EntityClassNameOptions.DontCaseAllCaps = true;
57 | options.DataModel.EntityClassNameOptions.PluralizeOnlyIfLastWordIsText = false;
58 |
59 | options.DataModel.EntityContextPropertyNameOptions.Casing = NameCasing.None;
60 | options.DataModel.EntityContextPropertyNameOptions.Pluralization = Pluralization.None;
61 | options.DataModel.EntityContextPropertyNameOptions.Transformation = NameTransformation.None;
62 | options.DataModel.EntityContextPropertyNameOptions.DontCaseAllCaps = true;
63 | options.DataModel.EntityContextPropertyNameOptions.PluralizeOnlyIfLastWordIsText = false;
64 |
65 | options.DataModel.EntityColumnPropertyNameOptions.Casing = NameCasing.None;
66 | options.DataModel.EntityColumnPropertyNameOptions.Pluralization = Pluralization.None;
67 | options.DataModel.EntityColumnPropertyNameOptions.Transformation = NameTransformation.None;
68 | options.DataModel.EntityColumnPropertyNameOptions.DontCaseAllCaps = true;
69 | options.DataModel.EntityColumnPropertyNameOptions.PluralizeOnlyIfLastWordIsText = false;
70 | }
71 |
72 | if (!settings.Scaffold.Capitalize)
73 | options.DataModel.EntityColumnPropertyNameOptions.Casing = NameCasing.None;
74 | else
75 | options.DataModel.EntityColumnPropertyNameOptions.Casing = NameCasing.Pascal;
76 |
77 | if (!settings.Scaffold.Pluralize)
78 | {
79 | options.DataModel.EntityContextPropertyNameOptions.Pluralization = Pluralization.None;
80 | options.DataModel.TargetMultipleAssociationPropertyNameOptions.Pluralization = Pluralization.None;
81 | }
82 | else
83 | {
84 | options.DataModel.EntityContextPropertyNameOptions.Pluralization = Pluralization.PluralIfLongerThanOne;
85 | options.DataModel.TargetMultipleAssociationPropertyNameOptions.Pluralization = Pluralization.PluralIfLongerThanOne;
86 | }
87 |
88 | options.DataModel.GenerateDefaultSchema = true;
89 | options.DataModel.GenerateDataType = true;
90 | options.DataModel.GenerateDbType = true;
91 | options.DataModel.GenerateLength = true;
92 | options.DataModel.GeneratePrecision = true;
93 | options.DataModel.GenerateScale = true;
94 | options.DataModel.HasDefaultConstructor = false;
95 | options.DataModel.HasConfigurationConstructor = false;
96 | options.DataModel.HasUntypedOptionsConstructor = false;
97 | options.DataModel.HasTypedOptionsConstructor = false;
98 | options.DataModel.ContextClassName = contextName;
99 | options.DataModel.BaseContextClass = "LinqToDB.LINQPad.LINQPadDataConnection";
100 | options.DataModel.GenerateAssociations = true;
101 | options.DataModel.GenerateAssociationExtensions = false;
102 | options.DataModel.AssociationCollectionType = "System.Collections.Generic.List<>";
103 | options.DataModel.MapProcedureResultToEntity = true;
104 | options.DataModel.TableFunctionReturnsTable = true;
105 | options.DataModel.GenerateProceduresSchemaError = false;
106 | options.DataModel.SkipProceduresWithSchemaErrors = true;
107 | options.DataModel.GenerateProcedureResultAsList = false;
108 | options.DataModel.GenerateProcedureParameterDbType = true;
109 | options.DataModel.GenerateProcedureSync = true;
110 | options.DataModel.GenerateProcedureAsync = false;
111 | options.DataModel.GenerateSchemaAsType = false;
112 | options.DataModel.GenerateIEquatable = false;
113 | options.DataModel.GenerateFindExtensions = FindTypes.None;
114 | options.DataModel.OrderFindParametersByColumnOrdinal = true;
115 |
116 | // set code generation options
117 | options.CodeGeneration.EnableNullableReferenceTypes = false;
118 | options.CodeGeneration.SuppressMissingXmlDocWarnings = true;
119 | options.CodeGeneration.MarkAsAutoGenerated = false;
120 | options.CodeGeneration.ClassPerFile = false;
121 | options.CodeGeneration.Namespace = contextNamespace;
122 |
123 | return options;
124 | }
125 |
126 | public static (List items, string sourceCode, string providerAssemblyLocation) GetModel(
127 | ConnectionSettings settings,
128 | ref string? contextNamespace,
129 | ref string contextName)
130 | {
131 | var scaffoldOptions = GetOptions(settings, contextNamespace, contextName);
132 |
133 | var provider = DatabaseProviders.GetDataProvider(settings);
134 |
135 | using var db = new DataConnection(provider, settings.Connection.GetFullConnectionString()!);
136 | if (settings.Connection.CommandTimeout != null)
137 | db.CommandTimeout = settings.Connection.CommandTimeout.Value;
138 |
139 | var providerAssemblyLocation = db.Connection.GetType().Assembly.Location;
140 |
141 | var sqlBuilder = db.DataProvider.CreateSqlBuilder(db.MappingSchema, db.Options);
142 | var language = LanguageProviders.CSharp;
143 | var interceptor = new ModelProviderInterceptor(settings, sqlBuilder);
144 | var generator = new Scaffolder(language, HumanizerNameConverter.Instance, scaffoldOptions, interceptor);
145 |
146 | var legacySchemaProvider = new LegacySchemaProvider(db, scaffoldOptions.Schema, language);
147 | ISchemaProvider schemaProvider = legacySchemaProvider;
148 | ITypeMappingProvider typeMappingsProvider = legacySchemaProvider;
149 |
150 | DatabaseModel dataModel;
151 | if (settings.Connection.Database == ProviderName.Access && settings.Connection.SecondaryConnectionString != null)
152 | {
153 | var secondaryConnectionString = settings.Connection.GetFullSecondaryConnectionString()!;
154 | var secondaryProvider = DatabaseProviders.GetDataProvider(settings.Connection.SecondaryProvider, secondaryConnectionString, null);
155 | using var sdc = new DataConnection(secondaryProvider, secondaryConnectionString);
156 |
157 | if (settings.Connection.CommandTimeout != null)
158 | sdc.CommandTimeout = settings.Connection.CommandTimeout.Value;
159 |
160 | var secondLegacyProvider = new LegacySchemaProvider(sdc, scaffoldOptions.Schema, language);
161 | schemaProvider = settings.Connection.Provider == ProviderName.Access
162 | ? new MergedAccessSchemaProvider(schemaProvider, secondLegacyProvider)
163 | : new MergedAccessSchemaProvider(secondLegacyProvider, schemaProvider);
164 | typeMappingsProvider = new AggregateTypeMappingsProvider(typeMappingsProvider, secondLegacyProvider);
165 | dataModel = generator.LoadDataModel(schemaProvider, typeMappingsProvider);
166 | }
167 | else
168 | dataModel = generator.LoadDataModel(schemaProvider, typeMappingsProvider);
169 |
170 | var files = generator.GenerateCodeModel(
171 | sqlBuilder,
172 | dataModel,
173 | MetadataBuilders.GetMetadataBuilder(generator.Language, MetadataSource.Attributes),
174 | new ProviderSpecificStructsEqualityFixer(generator.Language),
175 | new DataModelAugmentor(language, language.TypeParser.Parse(), settings.Connection.CommandTimeout));
176 |
177 | // IMPORTANT:
178 | // real identifiers from generated code set to data model only after this line (GenerateSourceCode call)
179 | // so we call GetTree or read identifiers from dataModel.DataContext.Class before this line
180 | var sourceCode = generator.GenerateSourceCode(dataModel, files)[0].Code;
181 |
182 | contextNamespace = dataModel.DataContext.Class.Namespace;
183 | contextName = dataModel.DataContext.Class.Name;
184 |
185 | return (interceptor.GetTree(), sourceCode, providerAssemblyLocation);
186 | }
187 | }
188 |
189 |
--------------------------------------------------------------------------------
/Source/Drivers/PasswordManager.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using System.Text.RegularExpressions;
3 | using LINQPad;
4 |
5 | namespace LinqToDB.LINQPad
6 | {
7 | internal static partial class PasswordManager
8 | {
9 | #pragma warning disable SYSLIB1045 // Convert to 'GeneratedRegexAttribute'.
10 | private static readonly Regex _tokenReplacer = new(@"\{pm:([^\}]+)\}", RegexOptions.Compiled);
11 | #pragma warning restore SYSLIB1045 // Convert to 'GeneratedRegexAttribute'.
12 |
13 | [return: NotNullIfNotNull(nameof(value))]
14 | public static string? ResolvePasswordManagerFields(string? value)
15 | {
16 | if (value == null)
17 | return null;
18 |
19 | return _tokenReplacer.Replace(value, m => Util.GetPassword(m.Groups[1].Value));
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Source/Drivers/Scaffold/CSharpUtils.cs:
--------------------------------------------------------------------------------
1 | namespace LinqToDB.LINQPad
2 | {
3 | // TODO: move to linq2db.Tools (see CSharpCodeGenerator.KeyWords)
4 | internal static class CSharpUtils
5 | {
6 | // C# keywords and contextual words
7 | // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/
8 | private static readonly HashSet KeyWords = new ()
9 | {
10 | "abstract", "as" , "base" , "bool" , "break" , "byte" , "case" , "catch" , "char" , "checked",
11 | "class" , "const" , "continue" , "decimal" , "default" , "delegate", "do" , "double" , "else" , "enum",
12 | "event" , "explicit", "extern" , "false" , "finally" , "fixed" , "float" , "for" , "foreach" , "goto",
13 | "if" , "implicit", "in" , "int" , "interface", "internal", "is" , "lock" , "long" , "namespace",
14 | "new" , "null" , "object" , "operator" , "out" , "override", "params" , "private" , "protected", "public",
15 | "readonly", "ref" , "return" , "sbyte" , "sealed" , "short" , "sizeof" , "stackalloc", "static" , "string",
16 | "struct" , "switch" , "this" , "throw" , "true" , "try" , "typeof" , "uint" , "ulong" , "unchecked",
17 | "unsafe" , "ushort" , "using" , "virtual" , "void" , "volatile", "while",
18 | // contextual words
19 | // we don't analyze context for them and tread as keywords to avoid unnecessary complexity in codegeneration
20 | "add" , "and" , "alias" , "ascending", "async" , "await" , "by" , "descending", "dynamic" , "equals",
21 | "from" , "get" , "global" , "group" , "init" , "into" , "join" , "let" , "managed" , "nameof",
22 | "nint" , "not" , "notnull" , "nuint" , "on" , "or" , "orderby", "partial" , "record" , "remove",
23 | "select" , "set" , "unmanaged", "value" , "var" , "when" , "where" , "with" , "yield"
24 | };
25 |
26 | public static string EscapeIdentifier(string identifier)
27 | {
28 | if (KeyWords.Contains(identifier))
29 | {
30 | return $"@{identifier}";
31 | }
32 |
33 | return identifier;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Source/Drivers/Scaffold/DataModelAugmentor.cs:
--------------------------------------------------------------------------------
1 | using LinqToDB.CodeModel;
2 |
3 | namespace LinqToDB.LINQPad;
4 |
5 | ///
6 | /// AST modification visitor used to add custom scaffold code:
7 | ///
8 | /// - add custom constructor to generated data context class
9 | ///
10 | ///
11 | internal sealed class DataModelAugmentor(
12 | ILanguageProvider languageProvider,
13 | IType baseContextType,
14 | int? commandTimeout) : ConvertCodeModelVisitor
15 | {
16 | private readonly CodeBuilder _ast = languageProvider.ASTBuilder;
17 |
18 | protected override ICodeElement Visit(CodeClass @class)
19 | {
20 | // identify context class
21 | if (@class.Inherits != null && languageProvider.TypeEqualityComparerWithoutNRT.Equals(@class.Inherits.Type, baseContextType))
22 | {
23 | var members = @class.Members.ToList();
24 | var constructors = new ConstructorGroup(@class);
25 | members.Add(constructors);
26 |
27 | // context found
28 | @class = new CodeClass(
29 | @class.CustomAttributes,
30 | @class.Attributes,
31 | @class.XmlDoc,
32 | @class.Type,
33 | @class.Name,
34 | @class.Parent,
35 | @class.Inherits,
36 | @class.Implements,
37 | members,
38 | @class.TypeInitializer);
39 |
40 | constructors.Class = @class;
41 |
42 | // generate
43 | // .ctor(string provider, string? assemblyPath, string connectionString)
44 | var parametrizedCtor = constructors.New().SetModifiers(Modifiers.Public);
45 | var providerParam = _ast.Parameter(WellKnownTypes.System.String, _ast.Name("provider"), CodeParameterDirection.In);
46 | var assemblyPathParam = _ast.Parameter(WellKnownTypes.System.String.WithNullability(true), _ast.Name("assemblyPath"), CodeParameterDirection.In);
47 | var connectionStringParam = _ast.Parameter(WellKnownTypes.System.String, _ast.Name("connectionString"), CodeParameterDirection.In);
48 |
49 | parametrizedCtor
50 | .Parameter(providerParam)
51 | .Parameter(assemblyPathParam)
52 | .Parameter(connectionStringParam)
53 | .Base(providerParam.Reference, assemblyPathParam.Reference, connectionStringParam.Reference);
54 |
55 | // set default CommandTimeout from LINQPad connection settings
56 | if (commandTimeout != null)
57 | {
58 | parametrizedCtor
59 | .Body()
60 | .Append(_ast.Assign(_ast.Member(@class.This, WellKnownTypes.LinqToDB.Data.DataConnection_CommandTimeout), _ast.Constant(commandTimeout.Value, true)));
61 | }
62 |
63 | return @class;
64 | }
65 |
66 | return base.Visit(@class);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Source/Drivers/StaticLinqToDBDriver.cs:
--------------------------------------------------------------------------------
1 | using System.Data;
2 | using System.Data.Common;
3 | using System.IO;
4 | using LINQPad.Extensibility.DataContext;
5 | using LinqToDB.Data;
6 | using LinqToDB.Mapping;
7 |
8 | namespace LinqToDB.LINQPad;
9 |
10 | // IMPORTANT:
11 | // 1. driver must be public or it will be missing from create connection dialog (existing connections will work)
12 | // 2. don't rename class or namespace as it is used by LINQPad as driver identifier. If renamed, old connections will disappear from UI
13 | ///
14 | /// Implements LINQPad driver for static (pre-compiled) model.
15 | ///
16 | public sealed class LinqToDBStaticDriver : StaticDataContextDriver
17 | {
18 | private MappingSchema? _mappingSchema;
19 |
20 | ///
21 | public override string Name => DriverHelper.Name;
22 | ///
23 | public override string Author => DriverHelper.Author;
24 |
25 | static LinqToDBStaticDriver() => DriverHelper.Init();
26 |
27 | ///
28 | public override string GetConnectionDescription(IConnectionInfo cxInfo) => DriverHelper.GetConnectionDescription(cxInfo);
29 |
30 | ///
31 | public override bool ShowConnectionDialog(IConnectionInfo cxInfo, ConnectionDialogOptions dialogOptions) => DriverHelper.ShowConnectionDialog(cxInfo, dialogOptions, false);
32 |
33 | ///
34 | public override List GetSchema(IConnectionInfo cxInfo, Type? customType)
35 | {
36 | if (customType == null)
37 | return new();
38 |
39 | try
40 | {
41 | return StaticSchemaGenerator.GetSchema(customType);
42 | }
43 | catch (Exception ex)
44 | {
45 | Notification.Error($"{ex}\n{ex.StackTrace}", "Schema Load Error");
46 | throw;
47 | }
48 | }
49 |
50 | private static readonly ParameterDescriptor[] _contextParameters =
51 | [
52 | new ParameterDescriptor("configuration", typeof(string).FullName)
53 | ];
54 |
55 | ///
56 | public override ParameterDescriptor[] GetContextConstructorParameters(IConnectionInfo cxInfo)
57 | {
58 | try
59 | {
60 | var settings = ConnectionSettings.Load(cxInfo);
61 |
62 | if (settings.StaticContext.ConfigurationName != null)
63 | return _contextParameters;
64 |
65 | return base.GetContextConstructorParameters(cxInfo);
66 | }
67 | catch (Exception ex)
68 | {
69 | DriverHelper.HandleException(ex, nameof(GetContextConstructorParameters));
70 | return [];
71 | }
72 | }
73 |
74 | ///
75 | public override object[] GetContextConstructorArguments(IConnectionInfo cxInfo)
76 | {
77 | try
78 | {
79 | var settings = ConnectionSettings.Load(cxInfo);
80 |
81 | #if NETFRAMEWORK
82 | var configurationPath = settings.StaticContext.LocalConfigurationPath ?? settings.StaticContext.ConfigurationPath;
83 | #else
84 | var configurationPath = settings.StaticContext.ConfigurationPath;
85 | #endif
86 | TryLoadAppSettingsJson(configurationPath);
87 |
88 | if (settings.StaticContext.ConfigurationName != null)
89 | return [settings.StaticContext.ConfigurationName];
90 |
91 | return base.GetContextConstructorArguments(cxInfo);
92 | }
93 | catch (Exception ex)
94 | {
95 | DriverHelper.HandleException(ex, nameof(GetContextConstructorArguments));
96 | return [];
97 | }
98 | }
99 |
100 | ///
101 | public override IEnumerable GetNamespacesToAdd(IConnectionInfo cxInfo) => DriverHelper.DefaultImports;
102 |
103 | ///
104 | public override void ClearConnectionPools(IConnectionInfo cxInfo) => DriverHelper.ClearConnectionPools(cxInfo);
105 |
106 | ///
107 | public override void InitializeContext(IConnectionInfo cxInfo, object context, QueryExecutionManager executionManager)
108 | {
109 | _mappingSchema = DriverHelper.InitializeContext(cxInfo, (IDataContext)context, executionManager);
110 | }
111 |
112 | ///
113 | public override void TearDownContext(IConnectionInfo cxInfo, object context, QueryExecutionManager executionManager, object[] constructorArguments)
114 | {
115 | try
116 | {
117 | if (context is IDisposable ctx)
118 | ctx.Dispose();
119 | }
120 | catch (Exception ex)
121 | {
122 | DriverHelper.HandleException(ex, nameof(TearDownContext));
123 | }
124 | }
125 |
126 | ///
127 | public override void PreprocessObjectToWrite(ref object objectToWrite, ObjectGraphInfo info)
128 | {
129 | try
130 | {
131 | objectToWrite = ValueFormatter.Format(objectToWrite);
132 | }
133 | catch (Exception ex)
134 | {
135 | DriverHelper.HandleException(ex, nameof(PreprocessObjectToWrite));
136 | }
137 | }
138 |
139 | private void TryLoadAppSettingsJson(string? appConfigPath)
140 | {
141 | if (string.IsNullOrWhiteSpace(appConfigPath) || !File.Exists(appConfigPath))
142 | return;
143 |
144 | if (appConfigPath!.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
145 | DataConnection.DefaultSettings = AppConfig.LoadJson(appConfigPath);
146 | #if !NETFRAMEWORK
147 | if (appConfigPath.EndsWith(".config", StringComparison.OrdinalIgnoreCase))
148 | DataConnection.DefaultSettings = AppConfig.LoadAppConfig(appConfigPath);
149 | #endif
150 | }
151 |
152 | ///
153 | public override IDbConnection GetIDbConnection(IConnectionInfo cxInfo)
154 | {
155 | try
156 | {
157 | return DatabaseProviders.CreateConnection(ConnectionSettings.Load(cxInfo));
158 | }
159 | catch (Exception ex)
160 | {
161 | DriverHelper.HandleException(ex, nameof(GetIDbConnection));
162 | throw;
163 | }
164 | }
165 |
166 | ///
167 | public override DbProviderFactory GetProviderFactory(IConnectionInfo cxInfo)
168 | {
169 | try
170 | {
171 | return DatabaseProviders.GetProviderFactory(ConnectionSettings.Load(cxInfo));
172 | }
173 | catch (Exception ex)
174 | {
175 | DriverHelper.HandleException(ex, nameof(GetProviderFactory));
176 | throw;
177 | }
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/Source/Drivers/StaticSchemaGenerator.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Text;
3 | using LINQPad.Extensibility.DataContext;
4 | using LinqToDB.Extensions;
5 | using LinqToDB.Mapping;
6 | using LinqToDB.Reflection;
7 |
8 | namespace LinqToDB.LINQPad;
9 |
10 | ///
11 | /// Generates schema tree structure for static context using reflection.
12 | ///
13 | internal static class StaticSchemaGenerator
14 | {
15 | private sealed class TableInfo
16 | {
17 | public TableInfo(PropertyInfo propertyInfo)
18 | {
19 | Name = propertyInfo.Name;
20 | Type = propertyInfo.PropertyType.GetItemType()!;
21 | TypeAccessor = TypeAccessor.GetAccessor(Type);
22 |
23 | var tableAttr = Type.GetAttribute();
24 |
25 | if (tableAttr != null)
26 | {
27 | IsColumnAttributeRequired = tableAttr.IsColumnAttributeRequired;
28 | IsView = tableAttr.IsView;
29 | }
30 | }
31 |
32 | ///
33 | /// Table accessor property name.
34 | ///
35 | public readonly string Name;
36 | ///
37 | /// Table mapping class type.
38 | ///
39 | public readonly Type Type;
40 | ///
41 | /// Value of mapping property for mapping.
42 | ///
43 | public readonly bool IsColumnAttributeRequired;
44 | ///
45 | /// Table mapping instance.
46 | ///
47 | public readonly TypeAccessor TypeAccessor;
48 | ///
49 | /// value for mapping.
50 | ///
51 | public readonly bool IsView;
52 | }
53 |
54 | #pragma warning disable CA1002 // Do not expose generic lists
55 | public static List GetSchema(Type customContextType)
56 | #pragma warning restore CA1002 // Do not expose generic lists
57 | {
58 | var items = new List();
59 |
60 | List? tableItems = null;
61 | List? viewItems = null;
62 |
63 | // tables discovered using table access properties in context:
64 | // ITable Prop or // IQueryable Prop
65 | var tables = customContextType.GetProperties()
66 | .Where(static p => !p.HasAttribute() && typeof(IQueryable<>).IsSameOrParentOf(p.PropertyType))
67 | .OrderBy(static p => p.Name)
68 | .Select(static p => new TableInfo(p));
69 |
70 | var lookup = new Dictionary();
71 |
72 | foreach (var table in tables)
73 | {
74 | var list = table.IsView ? (viewItems ??= new()) : (tableItems ??= new());
75 |
76 | var item = GetTable(table.IsView ? ExplorerIcon.View : ExplorerIcon.Table, table);
77 | list.Add(item);
78 | lookup.Add(table.Type, item);
79 |
80 | // add association nodes
81 | foreach (var ma in table.TypeAccessor.Members)
82 | {
83 | if (ma.MemberInfo.HasAttribute())
84 | {
85 | var isToMany = ma.Type is IEnumerable;
86 | // TODO: try to infer this information?
87 | var backToMany = true;
88 |
89 | var otherType = isToMany ? ma.Type.GetItemType()! : ma.Type;
90 | lookup.TryGetValue(otherType, out var otherItem);
91 |
92 | item.Children.Add(
93 | new ExplorerItem(
94 | ma.Name,
95 | isToMany
96 | ? ExplorerItemKind.CollectionLink
97 | : ExplorerItemKind.ReferenceLink,
98 | isToMany
99 | ? ExplorerIcon.OneToMany
100 | : backToMany
101 | ? ExplorerIcon.ManyToOne
102 | : ExplorerIcon.OneToOne)
103 | {
104 | DragText = CSharpUtils.EscapeIdentifier(ma.Name),
105 | ToolTipText = GetTypeName(ma.Type),
106 | IsEnumerable = isToMany,
107 | HyperlinkTarget = otherItem
108 | });
109 | }
110 | }
111 | }
112 |
113 | if (tableItems != null)
114 | items.Add(new ExplorerItem("Tables", ExplorerItemKind.Category, ExplorerIcon.Table)
115 | {
116 | Children = tableItems
117 | });
118 |
119 | if (viewItems != null)
120 | items.Add(new ExplorerItem("Views", ExplorerItemKind.Category, ExplorerIcon.View)
121 | {
122 | Children = viewItems
123 | });
124 |
125 | return items;
126 | }
127 |
128 | static ExplorerItem GetTable(ExplorerIcon icon, TableInfo table)
129 | {
130 | var columns =
131 | (
132 | from ma in table.TypeAccessor.Members
133 | where !ma.MemberInfo.HasAttribute()
134 | let ca = ma.MemberInfo.GetAttribute()
135 | let id = ma.MemberInfo.GetAttribute()
136 | let pk = ma.MemberInfo.GetAttribute()
137 | orderby
138 | ca == null ? 1 : ca.Order >= 0 ? 0 : 2,
139 | ca?.Order,
140 | ma.Name
141 | where
142 | ca != null && ca.IsColumn ||
143 | pk != null ||
144 | id != null ||
145 | ca == null && !table.IsColumnAttributeRequired && MappingSchema.Default.IsScalarType(ma.Type)
146 | select new ExplorerItem(
147 | ma.Name,
148 | ExplorerItemKind.Property,
149 | pk != null || ca != null && ca.IsPrimaryKey ? ExplorerIcon.Key : ExplorerIcon.Column)
150 | {
151 | Text = $"{ma.Name} : {GetTypeName(ma.Type)}",
152 | DragText = CSharpUtils.EscapeIdentifier(ma.Name),
153 | }
154 | ).ToList();
155 |
156 | var ret = new ExplorerItem(table.Name, ExplorerItemKind.QueryableObject, icon)
157 | {
158 | DragText = CSharpUtils.EscapeIdentifier(table.Name),
159 | IsEnumerable = true,
160 | Children = columns
161 | };
162 |
163 | return ret;
164 | }
165 |
166 | static string GetTypeName(Type type)
167 | {
168 | switch (type.FullName)
169 | {
170 | case "System.Boolean" : return "bool";
171 | case "System.Byte" : return "byte";
172 | case "System.SByte" : return "sbyte";
173 | case "System.Int16" : return "short";
174 | case "System.Int32" : return "int";
175 | case "System.Int64" : return "long";
176 | case "System.UInt16" : return "ushort";
177 | case "System.UInt32" : return "uint";
178 | case "System.UInt64" : return "ulong";
179 | case "System.Decimal" : return "decimal";
180 | case "System.Single" : return "float";
181 | case "System.Double" : return "double";
182 | case "System.String" : return "string";
183 | case "System.Char" : return "char";
184 | case "System.Object" : return "object";
185 | }
186 |
187 | if (type.IsArray)
188 | return GetTypeName(type.GetElementType()!) + "[]";
189 |
190 | if (type.IsNullable())
191 | return GetTypeName(type.ToNullableUnderlying()) + '?';
192 |
193 | if (type.IsGenericType)
194 | {
195 | var typeName = new StringBuilder();
196 | typeName
197 | .Append(type.Name)
198 | .Append('<');
199 |
200 | var first = true;
201 | foreach (var param in type.GetGenericArguments())
202 | {
203 | if (first)
204 | first = false;
205 | else
206 | typeName.Append(", ");
207 |
208 | typeName.Append(GetTypeName(param));
209 | }
210 |
211 | return typeName.Append('>').ToString();
212 | }
213 |
214 | return type.Name;
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/Source/LINQPadDataConnection.cs:
--------------------------------------------------------------------------------
1 | using LinqToDB.Data;
2 |
3 | namespace LinqToDB.LINQPad;
4 |
5 | ///
6 | /// Base class for generated contexts and context for direct use.
7 | ///
8 | public class LINQPadDataConnection : DataConnection
9 | {
10 | ///
11 | /// Constructor for inherited context.
12 | ///
13 | /// Provider name.
14 | /// Optional provider assembly path.
15 | /// Connection string must have password manager tokens replaced already.
16 | protected LINQPadDataConnection(string? providerName, string? providerPath, string? connectionString)
17 | : base(
18 | DatabaseProviders.GetDataProvider(providerName, connectionString, providerPath),
19 | connectionString ?? throw new LinqToDBLinqPadException("Connection string missing"))
20 | {
21 | }
22 |
23 | ///
24 | /// Constructor for use from driver code directly.
25 | ///
26 | internal LINQPadDataConnection(ConnectionSettings settings)
27 | : this(
28 | settings.Connection.Provider,
29 | settings.Connection.ProviderPath,
30 | settings.Connection.GetFullConnectionString())
31 | {
32 | if (settings.Connection.CommandTimeout != null)
33 | CommandTimeout = settings.Connection.CommandTimeout.Value;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Source/LinqToDBLinqPadException.cs:
--------------------------------------------------------------------------------
1 | namespace LinqToDB.LINQPad;
2 |
3 | internal sealed class LinqToDBLinqPadException(string message) : Exception(message)
4 | {
5 | }
6 |
--------------------------------------------------------------------------------
/Source/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 | using System.Runtime.InteropServices;
3 |
4 | [assembly: ComVisible(false)]
5 | [module: SkipLocalsInit]
6 |
--------------------------------------------------------------------------------
/Source/UI/Model/AboutModel.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Media.Imaging;
3 | using LinqToDB.Extensions;
4 |
5 | namespace LinqToDB.LINQPad.UI;
6 |
7 | internal sealed class AboutModel
8 | {
9 | public static AboutModel Instance { get; } = new AboutModel();
10 |
11 | private AboutModel()
12 | {
13 | // avoid Uri constructor crash on pack:// scheme in runtime due to initialization order
14 | // Application constructor will register schema handler
15 | if (!UriParser.IsKnownScheme("pack"))
16 | new Application();
17 |
18 | var assembly = typeof(AboutModel).Assembly;
19 | Logo = new BitmapImage(new Uri($"pack://application:,,,/{assembly.FullName};component/resources/logo.png"));
20 | Project = $"Linq To DB LINQPad Driver v{assembly.GetName().Version!.ToString(3)}";
21 | Copyright = assembly.GetAttribute()!.Copyright;
22 | }
23 |
24 | public BitmapImage Logo { get; }
25 | public string Project { get; }
26 | public string Copyright { get; }
27 | public Uri RepositoryUri { get; } = new("https://github.com/linq2db/linq2db.LINQPad");
28 | public Uri ReportsUri { get; } = new("https://github.com/linq2db/linq2db.LINQPad/issues/new");
29 | }
30 |
--------------------------------------------------------------------------------
/Source/UI/Model/ConnectionModelBase.cs:
--------------------------------------------------------------------------------
1 | namespace LinqToDB.LINQPad.UI;
2 |
3 | internal abstract class ConnectionModelBase(ConnectionSettings settings, bool enabled) : OptionalTabModelBase(settings, enabled)
4 | {
5 | public string? Name
6 | {
7 | get
8 | {
9 | if (string.IsNullOrWhiteSpace(Settings.Connection.DisplayName))
10 | return null;
11 |
12 | return Settings.Connection.DisplayName;
13 | }
14 | set
15 | {
16 | if (string.IsNullOrWhiteSpace(value))
17 | value = null;
18 |
19 | Settings.Connection.DisplayName = value;
20 | }
21 | }
22 |
23 | public bool IsSelected { get; set; } = enabled;
24 |
25 | public bool Persistent
26 | {
27 | get => Settings.Connection.Persistent;
28 | set => Settings.Connection.Persistent = value;
29 | }
30 |
31 | public bool Production
32 | {
33 | get => Settings.Connection.IsProduction;
34 | set => Settings.Connection.IsProduction = value;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Source/UI/Model/DynamicConnectionModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.ObjectModel;
2 | using System.ComponentModel;
3 | using System.Windows;
4 |
5 | namespace LinqToDB.LINQPad.UI;
6 |
7 | internal sealed class DynamicConnectionModel : ConnectionModelBase, INotifyPropertyChanged
8 | {
9 | public DynamicConnectionModel(ConnectionSettings settings, bool enabled)
10 | : base(settings, enabled)
11 | {
12 | foreach (var db in DatabaseProviders.Providers.Values.OrderBy(static db => db.Description))
13 | Databases.Add(db);
14 |
15 | UpdateProviders();
16 | UpdateProviderPathVisibility();
17 | UpdateSecondaryConnection();
18 | UpdateProviderDownloadUrl();
19 | }
20 |
21 | private void UpdateProviders()
22 | {
23 | var db = Database;
24 |
25 | if (db != null)
26 | {
27 | Providers.Clear();
28 |
29 | foreach (var provider in db.Providers.Where(p => !p.IsHidden))
30 | Providers.Add(provider);
31 |
32 | if (Providers.Count == 1)
33 | Settings.Connection.Provider = Providers[0].Name;
34 | }
35 |
36 | var old = ProviderVisibility;
37 | ProviderVisibility = Providers.Count > 1 && db?.AutomaticProviderSelection == false ? Visibility.Visible : Visibility.Collapsed;
38 |
39 | if (ProviderVisibility != old)
40 | OnPropertyChanged(_providerVisibilityChangedEventArgs);
41 | }
42 |
43 | private void UpdateProviderPathVisibility()
44 | {
45 | var db = Database;
46 | var provider = Provider;
47 |
48 | if (db == null || provider == null || !db.IsProviderPathSupported(provider.Name))
49 | {
50 | ProviderPathVisibility = Visibility.Collapsed;
51 | ProviderPathLabel = null;
52 | ProviderPath = null;
53 |
54 | OnPropertyChanged(_providerPathVisibilityChangedEventArgs);
55 | return;
56 | }
57 |
58 | var assemblyName = db.GetProviderAssemblyName(provider.Name);
59 | ProviderPathVisibility = Visibility.Visible;
60 | ProviderPath = Settings.Connection.ProviderPath ?? db.TryGetDefaultPath(provider.Name);
61 | ProviderPathLabel = $"Specify path to {assemblyName}";
62 |
63 | OnPropertyChanged(_providerPathVisibilityChangedEventArgs);
64 | OnPropertyChanged(_providerPathLabelChangedEventArgs);
65 | OnPropertyChanged(_providerPathChangedEventArgs);
66 | }
67 |
68 | private void UpdateProviderDownloadUrl()
69 | {
70 | var db = Database;
71 | var provider = Provider;
72 |
73 | if (db == null)
74 | {
75 | ProviderDownloadUrlVisibility = Visibility.Collapsed;
76 | }
77 | else
78 | {
79 | ProviderDownloadUrl = db.GetProviderDownloadUrl(provider?.Name);
80 | ProviderDownloadUrlVisibility = ProviderDownloadUrl != null ? Visibility.Visible : Visibility.Collapsed;
81 | }
82 |
83 | OnPropertyChanged(_providerDownloadUrlChangedEventArgs);
84 | OnPropertyChanged(_providerDownloadUrlVisibilityChangedEventArgs);
85 | }
86 |
87 | private void UpdateSecondaryConnection()
88 | {
89 | SecondaryConnectionStringVisibility = Database?.SupportsSecondaryConnection == true ? Visibility.Visible : Visibility.Collapsed;
90 | OnPropertyChanged(_secondaryConnectionStringVisibilityChangedEventArgs);
91 | }
92 |
93 | public ObservableCollection Databases { get; } = new();
94 |
95 | private static readonly PropertyChangedEventArgs _databaseChangedEventArgs = new (nameof(Database));
96 | public IDatabaseProvider? Database
97 | {
98 | get
99 | {
100 | if (string.IsNullOrWhiteSpace(Settings.Connection.Database))
101 | return null;
102 |
103 | return DatabaseProviders.GetProvider(Settings.Connection.Database);
104 | }
105 | set
106 | {
107 | Settings.Connection.Database = value?.Database;
108 | UpdateProviders();
109 | UpdateSecondaryConnection();
110 | UpdateProviderDownloadUrl();
111 | OnPropertyChanged(_databaseChangedEventArgs);
112 | Provider = GetCurrentProvider();
113 | }
114 | }
115 |
116 | private static readonly PropertyChangedEventArgs _providerVisibilityChangedEventArgs = new (nameof(ProviderVisibility));
117 | public Visibility ProviderVisibility { get; set; }
118 |
119 | public ObservableCollection Providers { get; } = new();
120 |
121 | private static readonly PropertyChangedEventArgs _providerChangedEventArgs = new (nameof(Provider));
122 | public ProviderInfo? Provider
123 | {
124 | get => GetCurrentProvider();
125 | set
126 | {
127 | Settings.Connection.Provider = value?.Name;
128 | UpdateProviderPathVisibility();
129 | UpdateProviderDownloadUrl();
130 | OnPropertyChanged(_providerChangedEventArgs);
131 | }
132 | }
133 |
134 | private ProviderInfo? GetCurrentProvider()
135 | {
136 | if (Database == null)
137 | return null;
138 |
139 | if (!string.IsNullOrWhiteSpace(Settings.Connection.Provider))
140 | {
141 | foreach (var provider in Database.Providers)
142 | if (provider.Name == Settings.Connection.Provider)
143 | return provider;
144 | }
145 |
146 | foreach (var provider in Database.Providers)
147 | if (provider.IsDefault)
148 | return provider;
149 |
150 | return null;
151 | }
152 |
153 | private static readonly PropertyChangedEventArgs _providerPathVisibilityChangedEventArgs = new (nameof(ProviderPathVisibility));
154 | public Visibility ProviderPathVisibility { get; set; }
155 |
156 | private static readonly PropertyChangedEventArgs _providerPathChangedEventArgs = new (nameof(ProviderPath));
157 | public string? ProviderPath
158 | {
159 | get
160 | {
161 | if (string.IsNullOrWhiteSpace(Settings.Connection.ProviderPath))
162 | return null;
163 |
164 | return Settings.Connection.ProviderPath;
165 | }
166 | set
167 | {
168 | if (string.IsNullOrWhiteSpace(value))
169 | value = null;
170 |
171 | Settings.Connection.ProviderPath = value;
172 | }
173 | }
174 |
175 | private static readonly PropertyChangedEventArgs _providerPathLabelChangedEventArgs = new (nameof(ProviderPathLabel));
176 | public string? ProviderPathLabel { get; set; }
177 |
178 | private static readonly PropertyChangedEventArgs _providerDownloadUrlVisibilityChangedEventArgs = new (nameof(ProviderDownloadUrlVisibility));
179 | public Visibility ProviderDownloadUrlVisibility { get; set; }
180 |
181 | private static readonly PropertyChangedEventArgs _providerDownloadUrlChangedEventArgs = new (nameof(ProviderDownloadUrl));
182 | #pragma warning disable CA1056 // URI-like properties should not be strings
183 | public string? ProviderDownloadUrl { get; set; }
184 | #pragma warning restore CA1056 // URI-like properties should not be strings
185 |
186 | public string? ConnectionString
187 | {
188 | get
189 | {
190 | if (string.IsNullOrWhiteSpace(Settings.Connection.ConnectionString))
191 | return null;
192 |
193 | return Settings.Connection.ConnectionString;
194 | }
195 | set
196 | {
197 | if (string.IsNullOrWhiteSpace(value))
198 | value = null;
199 |
200 | Settings.Connection.ConnectionString = value;
201 |
202 | if (Database != null && value != null && Database.AutomaticProviderSelection)
203 | Provider = Database.GetProviderByConnectionString(value);
204 | }
205 | }
206 |
207 | private static readonly PropertyChangedEventArgs _secondaryConnectionStringVisibilityChangedEventArgs = new (nameof(SecondaryConnectionStringVisibility));
208 | public Visibility SecondaryConnectionStringVisibility { get; set; }
209 |
210 | public string? SecondaryConnectionString
211 | {
212 | get
213 | {
214 | if (string.IsNullOrWhiteSpace(Settings.Connection.SecondaryConnectionString))
215 | return null;
216 |
217 | return Settings.Connection.SecondaryConnectionString;
218 | }
219 | set
220 | {
221 | if (string.IsNullOrWhiteSpace(value))
222 | value = null;
223 |
224 | Settings.Connection.SecondaryConnectionString = value;
225 |
226 | if (Database != null && value != null && Database.AutomaticProviderSelection)
227 | SecondaryProvider = Database.GetProviderByConnectionString(value);
228 | }
229 | }
230 |
231 | public ProviderInfo? SecondaryProvider
232 | {
233 | get
234 | {
235 | if (Database == null || string.IsNullOrWhiteSpace(Settings.Connection.SecondaryProvider))
236 | return null;
237 |
238 | foreach (var provider in Database.Providers)
239 | if (provider.Name == Settings.Connection.SecondaryProvider)
240 | return provider;
241 |
242 | return null;
243 | }
244 | set => Settings.Connection.SecondaryProvider = value?.Name;
245 | }
246 |
247 | public bool EncryptConnectionString
248 | {
249 | get => Settings.Connection.EncryptConnectionString;
250 | set => Settings.Connection.EncryptConnectionString = value;
251 | }
252 |
253 | public int? CommandTimeout
254 | {
255 | get => Settings.Connection.CommandTimeout;
256 | set => Settings.Connection.CommandTimeout = value;
257 | }
258 |
259 | #region INotifyPropertyChanged
260 | public event PropertyChangedEventHandler? PropertyChanged;
261 |
262 | private void OnPropertyChanged(PropertyChangedEventArgs args) => PropertyChanged?.Invoke(this, args);
263 | #endregion
264 | }
265 |
--------------------------------------------------------------------------------
/Source/UI/Model/LinqToDBModel.cs:
--------------------------------------------------------------------------------
1 | namespace LinqToDB.LINQPad.UI;
2 |
3 | internal sealed class LinqToDBModel(ConnectionSettings settings) : TabModelBase(settings)
4 | {
5 | public bool OptimizeJoins
6 | {
7 | get => Settings.LinqToDB.OptimizeJoins;
8 | set => Settings.LinqToDB.OptimizeJoins = value;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Source/UI/Model/ModelBase.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 |
3 | namespace LinqToDB.LINQPad;
4 |
5 | internal abstract partial class ModelBase : INotifyPropertyChanged
6 | {
7 | public event PropertyChangedEventHandler? PropertyChanged;
8 |
9 | protected void OnPropertyChanged(PropertyChangedEventArgs args) => PropertyChanged?.Invoke(this, args);
10 |
11 | #region Sample Simple Property
12 |
13 | private string? _name;
14 | public string? Name
15 | {
16 | get => _name;
17 | set
18 | {
19 | if (_name != value)
20 | {
21 | _name = value;
22 | OnPropertyChanged(_nameChangedEventArgs);
23 | AfterNameChanged();
24 | }
25 | }
26 | }
27 |
28 | private static readonly PropertyChangedEventArgs _nameChangedEventArgs = new (nameof(Name));
29 |
30 | partial void AfterNameChanged();
31 | #endregion
32 | }
33 |
--------------------------------------------------------------------------------
/Source/UI/Model/OptionalTabModelBase.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace LinqToDB.LINQPad.UI;
4 |
5 | internal abstract class OptionalTabModelBase(ConnectionSettings settings, bool enabled) : TabModelBase(settings)
6 | {
7 | public Visibility Visibility { get; } = enabled ? Visibility.Visible : Visibility.Collapsed;
8 | }
9 |
--------------------------------------------------------------------------------
/Source/UI/Model/ScaffoldModel.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using System.Windows;
3 |
4 | namespace LinqToDB.LINQPad.UI;
5 |
6 | internal sealed class ScaffoldModel : OptionalTabModelBase, INotifyPropertyChanged
7 | {
8 | public ScaffoldModel(ConnectionSettings settings, bool enabled)
9 | : base(settings, enabled)
10 | {
11 | UpdateClickHouseVisibility();
12 | }
13 |
14 | public bool Capitalize
15 | {
16 | get => Settings.Scaffold.Capitalize;
17 | set => Settings.Scaffold.Capitalize = value;
18 | }
19 |
20 | public bool Pluralize
21 | {
22 | get => Settings.Scaffold.Pluralize;
23 | set => Settings.Scaffold.Pluralize = value;
24 | }
25 |
26 | public bool AsIsNames
27 | {
28 | get => Settings.Scaffold.AsIsNames;
29 | set => Settings.Scaffold.AsIsNames = value;
30 | }
31 |
32 | public bool UseProviderTypes
33 | {
34 | get => Settings.Scaffold.UseProviderTypes;
35 | set => Settings.Scaffold.UseProviderTypes = value;
36 | }
37 |
38 | public bool ClickHouseUseStrings
39 | {
40 | get => Settings.Scaffold.ClickHouseFixedStringAsString;
41 | set => Settings.Scaffold.ClickHouseFixedStringAsString = value;
42 | }
43 |
44 | private static readonly PropertyChangedEventArgs _clickHouseVisibilityChangedEventArgs = new (nameof(ClickHouseVisibility));
45 | public Visibility ClickHouseVisibility { get; set; }
46 |
47 | internal void UpdateClickHouseVisibility()
48 | {
49 | ClickHouseVisibility = Settings.Connection.Database == ProviderName.ClickHouse ? Visibility.Visible : Visibility.Collapsed;
50 | OnPropertyChanged(_clickHouseVisibilityChangedEventArgs);
51 | }
52 |
53 | #region INotifyPropertyChanged
54 | public event PropertyChangedEventHandler? PropertyChanged;
55 |
56 | private void OnPropertyChanged(PropertyChangedEventArgs args) => PropertyChanged?.Invoke(this, args);
57 | #endregion
58 | }
59 |
--------------------------------------------------------------------------------
/Source/UI/Model/SchemaModel.cs:
--------------------------------------------------------------------------------
1 | namespace LinqToDB.LINQPad.UI;
2 |
3 | internal sealed class SchemaModel : OptionalTabModelBase
4 | {
5 | public SchemaModel(ConnectionSettings settings, bool enabled)
6 | : base(settings, enabled)
7 | {
8 | Schemas = new UniqueStringListModel()
9 | {
10 | Title = "Include/Exclude Schemas (Users)",
11 | ToolTip = "Include or exclude objects from specified schemas",
12 | Include = Settings.Schema.IncludeSchemas,
13 | };
14 |
15 | if (Settings.Schema.Schemas != null)
16 | foreach (var schema in Settings.Schema.Schemas)
17 | Schemas.Items.Add(schema);
18 |
19 | Catalogs = new UniqueStringListModel()
20 | {
21 | Title = "Include/Exclude Catalogs (Databases)",
22 | ToolTip = "Include or exclude objects from specified catalogs",
23 | Include = Settings.Schema.IncludeSchemas,
24 | };
25 |
26 | if (Settings.Schema.Catalogs != null)
27 | foreach (var catalog in Settings.Schema.Catalogs)
28 | Catalogs.Items.Add(catalog);
29 | }
30 |
31 | public UniqueStringListModel Schemas { get; }
32 | public UniqueStringListModel Catalogs { get; }
33 |
34 | public bool LoadForeignKeys
35 | {
36 | get => Settings.Schema.LoadForeignKeys;
37 | set => Settings.Schema.LoadForeignKeys = value;
38 | }
39 |
40 | public bool LoadProcedures
41 | {
42 | get => Settings.Schema.LoadProcedures;
43 | set => Settings.Schema.LoadProcedures = value;
44 | }
45 |
46 | public bool LoadTableFunctions
47 | {
48 | get => Settings.Schema.LoadTableFunctions;
49 | set => Settings.Schema.LoadTableFunctions = value;
50 | }
51 |
52 | public bool LoadScalarFunctions
53 | {
54 | get => Settings.Schema.LoadScalarFunctions;
55 | set => Settings.Schema.LoadScalarFunctions = value;
56 | }
57 |
58 | public bool LoadAggregateFunctions
59 | {
60 | get => Settings.Schema.LoadAggregateFunctions;
61 | set => Settings.Schema.LoadAggregateFunctions = value;
62 | }
63 |
64 | public void Save()
65 | {
66 | Settings.Schema.IncludeSchemas = Schemas .Include;
67 | Settings.Schema.IncludeCatalogs = Catalogs.Include;
68 |
69 | Settings.Schema.Schemas = Schemas .Items.Count == 0 ? null : new HashSet(Schemas .Items).AsReadOnly();
70 | Settings.Schema.Catalogs = Catalogs.Items.Count == 0 ? null : new HashSet(Catalogs.Items).AsReadOnly();
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Source/UI/Model/SettingsModel.cs:
--------------------------------------------------------------------------------
1 | namespace LinqToDB.LINQPad.UI;
2 |
3 | internal sealed class SettingsModel(ConnectionSettings settings, bool staticConnection)
4 | {
5 | // Don't remove. Design-time .ctor
6 | public SettingsModel()
7 | : this(new ConnectionSettings(), false)
8 | {
9 | }
10 |
11 | public StaticConnectionModel StaticConnection { get; } = new StaticConnectionModel(settings, staticConnection);
12 | public DynamicConnectionModel DynamicConnection { get; } = new DynamicConnectionModel(settings, !staticConnection);
13 | public ScaffoldModel Scaffold { get; } = new ScaffoldModel(settings, !staticConnection);
14 | public SchemaModel Schema { get; } = new SchemaModel(settings, !staticConnection);
15 | public LinqToDBModel LinqToDB { get; } = new LinqToDBModel(settings);
16 | public AboutModel About => AboutModel.Instance;
17 |
18 | public void Save()
19 | {
20 | // save settings that is not saved automatically by tab models
21 | Schema.Save();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Source/UI/Model/StaticConnectionModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.ObjectModel;
2 | using System.ComponentModel;
3 |
4 | namespace LinqToDB.LINQPad.UI;
5 |
6 | internal sealed class StaticConnectionModel(ConnectionSettings settings, bool enabled) : ConnectionModelBase(settings, enabled), INotifyPropertyChanged
7 | {
8 | private static readonly PropertyChangedEventArgs _contextAssemblyPathChangedEventArgs = new (nameof(ContextAssemblyPath));
9 |
10 | public string? ContextAssemblyPath
11 | {
12 | get
13 | {
14 | if (string.IsNullOrWhiteSpace(Settings.StaticContext.ContextAssemblyPath))
15 | return null;
16 |
17 | return Settings.StaticContext.ContextAssemblyPath;
18 | }
19 | set
20 | {
21 | if (string.IsNullOrWhiteSpace(value))
22 | value = null;
23 | else
24 | value = value!.Trim();
25 |
26 | if (Settings.StaticContext.ContextAssemblyPath != value)
27 | {
28 | Settings.StaticContext.ContextAssemblyPath = value;
29 | OnPropertyChanged(_contextAssemblyPathChangedEventArgs);
30 | }
31 | }
32 | }
33 |
34 | public string? ContextTypeName
35 | {
36 | get
37 | {
38 | if (string.IsNullOrWhiteSpace(Settings.StaticContext.ContextTypeName))
39 | return null;
40 |
41 | return Settings.StaticContext.ContextTypeName;
42 | }
43 | set
44 | {
45 | if (string.IsNullOrWhiteSpace(value))
46 | value = null;
47 |
48 | Settings.StaticContext.ContextTypeName = value;
49 | }
50 | }
51 |
52 | private static readonly PropertyChangedEventArgs _configurationPathChangedEventArgs = new (nameof(ConfigurationPath));
53 | public string? ConfigurationPath
54 | {
55 | get
56 | {
57 | #if NETFRAMEWORK
58 | if (!string.IsNullOrWhiteSpace(Settings.StaticContext.LocalConfigurationPath))
59 | return Settings.StaticContext.LocalConfigurationPath;
60 | #endif
61 | if (string.IsNullOrWhiteSpace(Settings.StaticContext.ConfigurationPath))
62 | return null;
63 |
64 | return Settings.StaticContext.ConfigurationPath;
65 | }
66 | set
67 | {
68 | if (string.IsNullOrWhiteSpace(value))
69 | value = null;
70 | else
71 | value = value!.Trim();
72 |
73 | #if NETFRAMEWORK
74 | Settings.StaticContext.ConfigurationPath = null;
75 | Settings.StaticContext.LocalConfigurationPath = null;
76 |
77 | if (value != null && value.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
78 | {
79 | if (Settings.StaticContext.LocalConfigurationPath != value)
80 | {
81 | Settings.StaticContext.LocalConfigurationPath = value;
82 | OnPropertyChanged(_configurationPathChangedEventArgs);
83 | }
84 | }
85 | else
86 | #endif
87 | if (Settings.StaticContext.ConfigurationPath != value)
88 | {
89 | Settings.StaticContext.ConfigurationPath = value;
90 | OnPropertyChanged(_configurationPathChangedEventArgs);
91 | }
92 | }
93 | }
94 |
95 | public string? ConfigurationName
96 | {
97 | get
98 | {
99 | if (string.IsNullOrWhiteSpace(Settings.StaticContext.ConfigurationName))
100 | return null;
101 |
102 | return Settings.StaticContext.ConfigurationName;
103 | }
104 | set
105 | {
106 | if (string.IsNullOrWhiteSpace(value))
107 | value = null;
108 |
109 | Settings.StaticContext.ConfigurationName = value;
110 | }
111 | }
112 |
113 | public ObservableCollection ContextTypes { get; } = new();
114 |
115 | public ObservableCollection Configurations { get; } = new();
116 |
117 | #region INotifyPropertyChanged
118 | public event PropertyChangedEventHandler? PropertyChanged;
119 |
120 | private void OnPropertyChanged(PropertyChangedEventArgs args) => PropertyChanged?.Invoke(this, args);
121 | #endregion
122 | }
123 |
--------------------------------------------------------------------------------
/Source/UI/Model/TabModelBase.cs:
--------------------------------------------------------------------------------
1 | namespace LinqToDB.LINQPad.UI;
2 |
3 | internal abstract class TabModelBase(ConnectionSettings settings)
4 | {
5 | protected readonly ConnectionSettings Settings = settings;
6 | }
7 |
--------------------------------------------------------------------------------
/Source/UI/Model/UniqueStringListModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.ObjectModel;
2 |
3 | namespace LinqToDB.LINQPad.UI;
4 |
5 | internal sealed class UniqueStringListModel
6 | {
7 | public string? Title { get; set; }
8 | public string? ToolTip { get; set; }
9 | public bool Include { get; set; }
10 | public ObservableCollection Items { get; } = new();
11 | }
12 |
--------------------------------------------------------------------------------
/Source/UI/Notification.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace LinqToDB.LINQPad;
4 |
5 | internal static class Notification
6 | {
7 | public static void Error(string message, string title = "Error")
8 | {
9 | MessageBox.Show(message, title, MessageBoxButton.OK, MessageBoxImage.Error);
10 | }
11 |
12 | public static void Error(Window owner, string message, string title = "Error")
13 | {
14 | MessageBox.Show(owner, message, title, MessageBoxButton.OK, MessageBoxImage.Error);
15 | }
16 |
17 | public static void Warning(Window owner, string message, string title = "Warning")
18 | {
19 | MessageBox.Show(owner, message, title, MessageBoxButton.OK, MessageBoxImage.Warning);
20 | }
21 |
22 | public static void Info(Window owner, string message, string title = "Information")
23 | {
24 | MessageBox.Show(owner, message, title, MessageBoxButton.OK, MessageBoxImage.Information);
25 | }
26 |
27 | public static bool YesNo(Window owner, string message, string title = "Information", MessageBoxImage icon = MessageBoxImage.Question)
28 | {
29 | return MessageBox.Show(owner, message, title, MessageBoxButton.YesNo, icon) == MessageBoxResult.Yes;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Source/UI/Settings/AboutTab.xaml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/Source/UI/Settings/AboutTab.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Windows.Navigation;
3 |
4 | namespace LinqToDB.LINQPad.UI;
5 |
6 | #pragma warning disable CA1812 // Remove unused type
7 | internal sealed partial class AboutTab
8 | #pragma warning restore CA1812 // Remove unused type
9 | {
10 | public AboutTab()
11 | {
12 | InitializeComponent();
13 | }
14 |
15 | private void Url_Click(object sender, RequestNavigateEventArgs e)
16 | {
17 | Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri)
18 | {
19 | UseShellExecute = true
20 | });
21 |
22 | e.Handled = true;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Source/UI/Settings/CommandTimeoutConverter.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 | using System.Windows.Data;
3 |
4 | namespace LinqToDB.LINQPad.UI;
5 |
6 | #pragma warning disable CA1812 // Remove unused type
7 | sealed class CommandTimeoutConverter : IValueConverter
8 | #pragma warning restore CA1812 // Remove unused type
9 | {
10 | public object? Convert(object? value, Type targetType, object parameter, CultureInfo culture)
11 | {
12 | if (value is int intVal)
13 | return intVal.ToString(culture);
14 |
15 | if (value is string strValue)
16 | return strValue;
17 |
18 | return string.Empty;
19 | }
20 |
21 | public object? ConvertBack(object? value, Type targetType, object parameter, CultureInfo culture)
22 | {
23 | if (value is int intValue && intValue >= 0)
24 | return intValue;
25 |
26 | if (value is string strValue && int.TryParse(strValue, NumberStyles.Integer, culture, out var intVal) && intVal >= 0)
27 | return intVal;
28 |
29 | return null;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Source/UI/Settings/DynamicConnectionTab.xaml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | You can download provider here.
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/Source/UI/Settings/DynamicConnectionTab.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.IO;
3 | using System.Windows;
4 | using System.Windows.Navigation;
5 | using Microsoft.Win32;
6 |
7 | namespace LinqToDB.LINQPad.UI;
8 |
9 | #pragma warning disable CA1812 // Remove unused type
10 | internal sealed partial class DynamicConnectionTab
11 | #pragma warning restore CA1812 // Remove unused type
12 | {
13 | private DynamicConnectionModel Model => (DynamicConnectionModel)DataContext;
14 |
15 | public DynamicConnectionTab()
16 | {
17 | InitializeComponent();
18 | }
19 |
20 | private void Url_Click(object sender, RequestNavigateEventArgs e)
21 | {
22 | Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri)
23 | {
24 | UseShellExecute = true
25 | });
26 |
27 | e.Handled = true;
28 | }
29 |
30 | void Click_SelectProvider(object sender, RoutedEventArgs e)
31 | {
32 | if (Model == null)
33 | return;
34 |
35 | var provider = Model.Database;
36 |
37 | if (provider == null || Model.Provider == null || provider.IsProviderPathSupported(Model.Provider.Name))
38 | return;
39 |
40 | var assemblyName = provider.GetProviderAssemblyName(Model.Provider.Name);
41 | var defaultPath = provider.TryGetDefaultPath(Model.Provider.Name);
42 | var startPath = Model.ProviderPath ?? defaultPath;
43 |
44 | var dialog = new OpenFileDialog()
45 | {
46 | Title = $"Choose {assemblyName} provider assembly",
47 | DefaultExt = Path.GetExtension(assemblyName),
48 | FileName = Model.ProviderPath,
49 | CheckPathExists = true,
50 | Filter = $"{assemblyName}|{assemblyName}|All Files(*.*)|*.*",
51 | InitialDirectory = startPath == null ? null : Path.GetDirectoryName(startPath)
52 | };
53 |
54 | if (dialog.ShowDialog() == true)
55 | Model.ProviderPath = dialog.FileName;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Source/UI/Settings/LinqToDBTab.xaml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
19 | Enable JOINs optimization in LINQ queries (
22 |
23 | )
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Source/UI/Settings/LinqToDBTab.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Windows.Navigation;
3 |
4 | namespace LinqToDB.LINQPad.UI;
5 |
6 | #pragma warning disable CA1812 // Remove unused type
7 | internal sealed partial class LinqToDBTab
8 | #pragma warning restore CA1812 // Remove unused type
9 | {
10 | public LinqToDBTab()
11 | {
12 | InitializeComponent();
13 | }
14 |
15 | private void Url_Click(object sender, RequestNavigateEventArgs e)
16 | {
17 | Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri)
18 | {
19 | UseShellExecute = true
20 | });
21 |
22 | e.Handled = true;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Source/UI/Settings/ScaffoldTab.xaml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
20 |
21 |
26 |
27 |
32 |
33 |
38 |
39 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Source/UI/Settings/ScaffoldTab.xaml.cs:
--------------------------------------------------------------------------------
1 | namespace LinqToDB.LINQPad.UI;
2 |
3 | #pragma warning disable CA1812 // Remove unused type
4 | internal sealed partial class ScaffoldTab
5 | #pragma warning restore CA1812 // Remove unused type
6 | {
7 | public ScaffoldTab()
8 | {
9 | InitializeComponent();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Source/UI/Settings/SchemaTab.xaml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Source/UI/Settings/SchemaTab.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Controls;
3 |
4 | namespace LinqToDB.LINQPad.UI;
5 |
6 | #pragma warning disable CA1812 // Remove unused type
7 | internal sealed partial class SchemaTab
8 | #pragma warning restore CA1812 // Remove unused type
9 | {
10 | public SchemaTab()
11 | {
12 | InitializeComponent();
13 | }
14 |
15 | private void ProcLoad_Click(object sender, RoutedEventArgs e)
16 | {
17 | if (((CheckBox)sender).IsChecked == true)
18 | {
19 | Notification.Warning(
20 | Window.GetWindow(this),
21 | "Including Stored Procedures or Table Functions may be dangerous if they contain non-transactional logic because driver needs to execute them for returned table schema population.");
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Source/UI/Settings/SettingsDialog.xaml:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/Source/UI/Settings/SettingsDialog.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using System.Windows;
3 | using System.Windows.Controls;
4 | using System.Windows.Input;
5 |
6 | namespace LinqToDB.LINQPad.UI;
7 |
8 | internal sealed partial class SettingsDialog
9 | {
10 | private readonly Func? _connectionTester;
11 | private readonly string? _testErrorMessage;
12 |
13 | public SettingsDialog()
14 | {
15 | InitializeComponent();
16 | }
17 |
18 | private SettingsModel Model => (SettingsModel)DataContext;
19 |
20 | SettingsDialog(SettingsModel model, Func connectionTester, string testErrorMessage)
21 | : this()
22 | {
23 | DataContext = model;
24 | _connectionTester = connectionTester;
25 | _testErrorMessage = testErrorMessage;
26 |
27 | model.DynamicConnection.PropertyChanged += DynamicConnection_PropertyChanged;
28 | }
29 |
30 | private void DynamicConnection_PropertyChanged(object? sender, PropertyChangedEventArgs e)
31 | {
32 | switch (e.PropertyName)
33 | {
34 | case nameof(Model.DynamicConnection.Database):
35 | Model.Scaffold.UpdateClickHouseVisibility();
36 | break;
37 | }
38 | }
39 |
40 | public static bool Show(SettingsModel model, Func connectionTester, string testErrorMessage)
41 | {
42 | return new SettingsDialog(model, connectionTester, testErrorMessage).ShowDialog() == true;
43 | }
44 |
45 | private void Click_Save(object sender, RoutedEventArgs e)
46 | {
47 | if (_connectionTester == null)
48 | {
49 | DialogResult = true;
50 | return;
51 | }
52 |
53 | // test configured connection and ask for confirmation on error
54 | Exception? ex;
55 |
56 | try
57 | {
58 | Mouse.OverrideCursor = Cursors.Wait;
59 | ex = _connectionTester(Model);
60 | }
61 | finally
62 | {
63 | Mouse.OverrideCursor = null;
64 | }
65 |
66 | if (ex == null
67 | || Notification.YesNo(this, $"{_testErrorMessage ?? "Connection to database failed"} Save anyway?\r\n\r\n{ex.Message}", "Error", icon: MessageBoxImage.Stop))
68 | {
69 | DialogResult = true;
70 | }
71 | }
72 |
73 | void Click_Test(object sender, RoutedEventArgs e)
74 | {
75 | if (_connectionTester != null)
76 | {
77 | Exception? ex;
78 |
79 | try
80 | {
81 | Mouse.OverrideCursor = Cursors.Wait;
82 | ex = _connectionTester(Model);
83 | }
84 | finally
85 | {
86 | Mouse.OverrideCursor = null;
87 | }
88 |
89 | if (ex == null)
90 | Notification.Info(this, "Successful!", "Connection Test");
91 | else
92 | Notification.Error(this, ex.Message, "Connection Test Error");
93 | }
94 | }
95 |
96 | private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
97 | {
98 | _testButton.Visibility = _tabControl.SelectedItem is TabItem ti && ti.Content is DynamicConnectionTab or StaticConnectionTab ? Visibility.Visible : Visibility.Hidden;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/Source/UI/Settings/SharedConnectionOptions.xaml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/Source/UI/Settings/SharedConnectionOptions.xaml.cs:
--------------------------------------------------------------------------------
1 | namespace LinqToDB.LINQPad.UI;
2 |
3 | #pragma warning disable CA1812 // Remove unused type
4 | internal sealed partial class SharedConnectionOptions
5 | #pragma warning restore CA1812 // Remove unused type
6 | {
7 | public SharedConnectionOptions()
8 | {
9 | InitializeComponent();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Source/UI/Settings/StaticConnectionTab.xaml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/Source/UI/Settings/StaticConnectionTab.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using System.Configuration;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Windows;
6 | using System.Windows.Input;
7 | using System.Windows.Navigation;
8 | using LINQPad.Extensibility.DataContext;
9 | using Microsoft.Win32;
10 |
11 | namespace LinqToDB.LINQPad.UI;
12 |
13 | #pragma warning disable CA1812 // Remove unused type
14 | internal sealed partial class StaticConnectionTab
15 | #pragma warning restore CA1812 // Remove unused type
16 | {
17 | private const string IDATACONTEXT_NAME = $"{nameof(LinqToDB)}.{nameof(IDataContext)}";
18 |
19 | private StaticConnectionModel Model => (StaticConnectionModel)DataContext;
20 |
21 | public StaticConnectionTab()
22 | {
23 | InitializeComponent();
24 |
25 | DataContextChanged += StaticConnectionTab_DataContextChanged;
26 | }
27 |
28 | private void StaticConnectionTab_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
29 | {
30 | // delayed init
31 | LoadContextTypes();
32 | LoadConfigurations();
33 | Model.PropertyChanged += Model_PropertyChanged;
34 | }
35 |
36 | private void Model_PropertyChanged(object? sender, PropertyChangedEventArgs e)
37 | {
38 | switch (e.PropertyName)
39 | {
40 | case nameof(Model.ContextAssemblyPath):
41 | LoadContextTypes();
42 | LoadConfigurations();
43 | break;
44 | case nameof(Model.ConfigurationPath):
45 | LoadConfigurations();
46 | break;
47 | }
48 | }
49 |
50 | private void LoadContextTypes()
51 | {
52 | Model.ContextTypes.Clear();
53 |
54 | if (Model.ContextAssemblyPath != null)
55 | {
56 | var oldCursor = Cursor;
57 |
58 | try
59 | {
60 | Mouse.OverrideCursor = Cursors.Wait;
61 |
62 | var assembly = DataContextDriver.LoadAssemblySafely(Model.ContextAssemblyPath);
63 | // as referenced linq2db assembly from context could be different version than
64 | // linq2db assembly from current process
65 | // we cannot compare types directly and should use by-name comparison
66 | foreach (var type in assembly.GetExportedTypes())
67 | {
68 | foreach (var iface in type.GetInterfaces())
69 | {
70 | if (iface.FullName == IDATACONTEXT_NAME)
71 | Model.ContextTypes.Add(type.FullName!);
72 | }
73 | }
74 | }
75 | catch (Exception ex)
76 | {
77 | Notification.Error(Window.GetWindow(this), ex.Message, "Context assembly load error");
78 | }
79 | finally
80 | {
81 | Mouse.OverrideCursor = oldCursor;
82 | }
83 | }
84 | }
85 |
86 | void LoadConfigurations()
87 | {
88 | Model.Configurations.Clear();
89 |
90 | // try to load appsettings.json
91 | if (Model.ConfigurationPath != null
92 | && Model.ConfigurationPath.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
93 | {
94 | var oldCursor = Cursor;
95 | try
96 | {
97 | Mouse.OverrideCursor = Cursors.Wait;
98 |
99 | var config = AppConfig.LoadJson(Model.ConfigurationPath!);
100 |
101 | if (config.ConnectionStrings.Any())
102 | foreach (var cs in config.ConnectionStrings)
103 | Model.Configurations.Add(cs.Name);
104 |
105 | return;
106 | }
107 | catch (Exception ex)
108 | {
109 | Notification.Error(Window.GetWindow(this), ex.Message, "JSON configuration file read error");
110 | }
111 | finally
112 | {
113 | Mouse.OverrideCursor = oldCursor;
114 | }
115 | }
116 |
117 | // try to load custom app.config
118 | else if (Model.ConfigurationPath != null)
119 | {
120 | var oldCursor = Cursor;
121 |
122 | try
123 | {
124 | Mouse.OverrideCursor = Cursors.Wait;
125 |
126 | var configMap = new ExeConfigurationFileMap();
127 | configMap.ExeConfigFilename = Model.ConfigurationPath;
128 | var config = ConfigurationManager.OpenMappedExeConfiguration(configMap, ConfigurationUserLevel.None);
129 |
130 | foreach (var cs in config.ConnectionStrings.ConnectionStrings.Cast())
131 | Model.Configurations.Add(cs.Name);
132 |
133 | Mouse.OverrideCursor = oldCursor;
134 | }
135 | catch (Exception ex)
136 | {
137 | Notification.Error(Window.GetWindow(this), ex.Message, "Custom app.config file read error");
138 | }
139 | finally
140 | {
141 | Mouse.OverrideCursor = oldCursor;
142 | }
143 | }
144 |
145 | // try to load default app.config
146 | else if (Model.ContextAssemblyPath != null)
147 | {
148 | var oldCursor = Cursor;
149 |
150 | try
151 | {
152 | Mouse.OverrideCursor = Cursors.Wait;
153 |
154 | var config = ConfigurationManager.OpenExeConfiguration(Model.ContextAssemblyPath);
155 |
156 | foreach (var cs in config.ConnectionStrings.ConnectionStrings.Cast())
157 | Model.Configurations.Add(cs.Name);
158 |
159 | Model.ConfigurationPath = config.FilePath;
160 | Mouse.OverrideCursor = oldCursor;
161 | }
162 | catch (Exception ex)
163 | {
164 | Notification.Error(Window.GetWindow(this), ex.Message, "Default app.config file read error");
165 | }
166 | finally
167 | {
168 | Mouse.OverrideCursor = oldCursor;
169 | }
170 | }
171 | }
172 |
173 | void Click_SelectAssembly(object sender, RoutedEventArgs e)
174 | {
175 | if (Model == null)
176 | return;
177 |
178 | var dialog = new OpenFileDialog()
179 | {
180 | Title = "Choose assembly with data model",
181 | DefaultExt = ".dll",
182 | FileName = Model.ContextAssemblyPath,
183 | CheckPathExists = true,
184 | Filter = "Assembly files (*.dll, *.exe)|*.dll;*.exe|All Files(*.*)|*.*",
185 | InitialDirectory = Model.ContextAssemblyPath == null ? null : Path.GetDirectoryName(Model.ContextAssemblyPath)
186 | };
187 |
188 | if (dialog.ShowDialog() == true)
189 | Model.ContextAssemblyPath = dialog.FileName;
190 | }
191 |
192 | void Click_SelectConfig(object sender, RoutedEventArgs e)
193 | {
194 | if (Model != null)
195 | {
196 | var dialog = new OpenFileDialog()
197 | {
198 | Title = "Choose application config file",
199 | DefaultExt = ".config",
200 | FileName = Model.ConfigurationPath,
201 | CheckPathExists = true,
202 | Filter = "Configuration files (*.json, *.config)|*.json;*.config|All Files(*.*)|*.*",
203 | InitialDirectory = Model.ConfigurationPath == null ? null : Path.GetDirectoryName(Model.ConfigurationPath)
204 | };
205 |
206 | if (dialog.ShowDialog() == true)
207 | Model.ConfigurationPath = dialog.FileName;
208 | }
209 | }
210 |
211 | private void Url_Click(object sender, RequestNavigateEventArgs e)
212 | {
213 | Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri)
214 | {
215 | UseShellExecute = true
216 | });
217 |
218 | e.Handled = true;
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/Source/UI/Settings/UniqueStringListControl.xaml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Source/UI/Settings/UniqueStringListControl.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Controls;
3 | using System.Windows.Input;
4 |
5 | namespace LinqToDB.LINQPad.UI;
6 |
7 | #pragma warning disable CA1812 // Remove unused type
8 | internal sealed partial class UniqueStringListControl
9 | #pragma warning restore CA1812 // Remove unused type
10 | {
11 | private UniqueStringListModel Model => (UniqueStringListModel)DataContext;
12 |
13 | public UniqueStringListControl()
14 | {
15 | InitializeComponent();
16 | }
17 |
18 | private void GotFocus_TextBox(object sender, RoutedEventArgs e)
19 | {
20 | _button.Content = "Add";
21 | }
22 |
23 | private void Click_Button(object sender, RoutedEventArgs e)
24 | {
25 | if (_button.Content is string buttonName)
26 | {
27 | if (buttonName == "Add")
28 | AddNewItem();
29 | else
30 | DeleteSelectedItem();
31 | }
32 | }
33 |
34 | private void DeleteSelectedItem()
35 | {
36 | if (_listBox.SelectedItem is string selectedItem)
37 | {
38 | Model.Items.Remove(selectedItem);
39 | _button.Content = "Add";
40 | }
41 | }
42 |
43 | private void AddNewItem()
44 | {
45 | var item = _textBox.Text;
46 |
47 | if (!string.IsNullOrWhiteSpace(item))
48 | {
49 | item = item.Trim();
50 |
51 | if (!Model.Items.Contains(item))
52 | Model.Items.Add(item);
53 | }
54 |
55 | _textBox.Text = null;
56 | }
57 |
58 | private void KeyDown_TextBox(object sender, KeyEventArgs e)
59 | {
60 | if (e.Key == Key.Enter)
61 | {
62 | AddNewItem();
63 | e.Handled = true;
64 | }
65 | }
66 |
67 | private void KeyDown_ListBox(object sender, KeyEventArgs e)
68 | {
69 | if (e.Key == Key.Delete)
70 | {
71 | DeleteSelectedItem();
72 | e.Handled = true;
73 | }
74 | }
75 |
76 | private void SelectionChanged_ListBox(object sender, SelectionChangedEventArgs e)
77 | {
78 | _button.Content = _listBox.SelectedItem != null ? "Remove" : "Add";
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Source/linq2db.LINQPad.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48;net8.0-windows
5 | LinqToDB.LINQPad
6 | linq2db
7 | linq2db.LINQPad
8 | $(Product)
9 | 6.0.0.0
10 | $(Version)
11 | $(Version)
12 | Copyright © 2016-2025 Linq To DB Team
13 | true
14 | true
15 |
16 | latest
17 | enable
18 |
19 | 9999
20 | prompt
21 | strict
22 | true
23 | enable
24 |
25 | true
26 |
27 |
28 | true
29 |
30 |
31 | MSB3270
32 |
33 |
34 |
35 |
36 |
37 |
38 | true
39 | true
40 |
41 | preview-All
42 |
43 | true
44 | false
45 |
46 | true
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | $(PkgIBM_Data_DB_Provider)\build\net451\x64\IBM.Data.DB2.dll
90 |
91 |
92 | $(PkgIBM_Data_DB_Provider)\build\net451\x86\IBM.Data.DB2.DLL
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | $(DefaultXamlRuntime)
109 | Designer
110 |
111 |
112 | $(DefaultXamlRuntime)
113 | Designer
114 |
115 |
116 | $(DefaultXamlRuntime)
117 | Designer
118 |
119 |
120 | $(DefaultXamlRuntime)
121 | Designer
122 |
123 |
124 | $(DefaultXamlRuntime)
125 | Designer
126 |
127 |
128 | $(DefaultXamlRuntime)
129 | Designer
130 |
131 |
132 | $(DefaultXamlRuntime)
133 | Designer
134 |
135 |
136 | $(DefaultXamlRuntime)
137 | Designer
138 |
139 |
140 | $(DefaultXamlRuntime)
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
--------------------------------------------------------------------------------
/Testing/TypeRenderingTests.txt:
--------------------------------------------------------------------------------
1 | // generic
2 | new object[]
3 | {
4 | new
5 | {
6 | BigInteger = System.Numerics.BigInteger.Parse("-2352454252352345235235235235235346353523523"),
7 | IPv4 = System.Net.IPAddress.Parse("127.0.0.1"),
8 | IPv6 = System.Net.IPAddress.Parse("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
9 | }
10 | }
11 |
12 |
13 | // firebird
14 | new object[]
15 | {
16 | new
17 | {
18 | FbDecFloat1 = new FirebirdSql.Data.Types.FbDecFloat(System.Numerics.BigInteger.Parse("-1234"), 0),
19 | FbDecFloat2 = new FirebirdSql.Data.Types.FbDecFloat(System.Numerics.BigInteger.Parse("-1234"), 1),
20 | FbDecFloat3 = new FirebirdSql.Data.Types.FbDecFloat(System.Numerics.BigInteger.Parse("-1234"), -1),
21 | FbDecFloat4 = new FirebirdSql.Data.Types.FbDecFloat(System.Numerics.BigInteger.Parse("-1234"), -4),
22 | FbDecFloat5 = new FirebirdSql.Data.Types.FbDecFloat(System.Numerics.BigInteger.Parse("-1234"), -5),
23 | FbDecFloat6 = new FirebirdSql.Data.Types.FbDecFloat(System.Numerics.BigInteger.Parse("1234"), 0),
24 | FbDecFloat7 = new FirebirdSql.Data.Types.FbDecFloat(System.Numerics.BigInteger.Parse("1234"), 1),
25 | FbDecFloat8 = new FirebirdSql.Data.Types.FbDecFloat(System.Numerics.BigInteger.Parse("1234"), -1),
26 | FbDecFloat9 = new FirebirdSql.Data.Types.FbDecFloat(System.Numerics.BigInteger.Parse("1234"), -4),
27 | FbDecFloat0 = new FirebirdSql.Data.Types.FbDecFloat(System.Numerics.BigInteger.Parse("1234"), -5),
28 | FbZonedDateTime = new FirebirdSql.Data.Types.FbZonedDateTime(DateTime.UtcNow, "Europe/Amsterdam"),
29 | FbZonedTime = new FirebirdSql.Data.Types.FbZonedTime(TimeSpan.FromMinutes(-2222), "Europe/Amsterdam"),
30 | }
31 | }
32 |
33 | // sqlce
34 | new object[]
35 | {
36 | new
37 | {
38 | SqlBinary = new System.Data.SqlTypes.SqlBinary(new byte[]{0,1,2,3,4,5}),
39 | SqlBinaryNull = new System.Data.SqlTypes.SqlBinary(null),
40 | SqlBoolean = new System.Data.SqlTypes.SqlBoolean(true),
41 | SqlBooleanFalse = new System.Data.SqlTypes.SqlBoolean(false),
42 | SqlBooleanNull = System.Data.SqlTypes.SqlBoolean.Null,
43 | SqlByte = new System.Data.SqlTypes.SqlByte(255),
44 | SqlBytes = new System.Data.SqlTypes.SqlBytes(new byte[]{0,1,2,3,4,5}),
45 | SqlChars = new System.Data.SqlTypes.SqlChars(new char[]{(char)0,(char)1, 'S', 'ы'}),
46 | SqlDateTime = new System.Data.SqlTypes.SqlDateTime(DateTime.Now),
47 | SqlDecimal = new System.Data.SqlTypes.SqlDecimal(-123.456m),
48 | SqlDouble = new System.Data.SqlTypes.SqlDouble(-123.456e123),
49 | SqlGuid = new System.Data.SqlTypes.SqlGuid(Guid.NewGuid()),
50 | SqlInt16 = new System.Data.SqlTypes.SqlInt16(short.MinValue),
51 | SqlInt32 = new System.Data.SqlTypes.SqlInt32(int.MinValue),
52 | SqlInt64 = new System.Data.SqlTypes.SqlInt64(long.MinValue),
53 | SqlMoney = new System.Data.SqlTypes.SqlMoney(-123.456m),
54 | SqlSingle = new System.Data.SqlTypes.SqlSingle(-123.456e-23),
55 | SqlString = new System.Data.SqlTypes.SqlString("test\0\r\nтест"),
56 | SqlXml = new System.Data.SqlTypes.SqlXml(new MemoryStream(Encoding.UTF8.GetBytes("тест"))),
57 | }
58 | }
59 |
60 |
61 | // ase
62 | new object[]
63 | {
64 | new
65 | {
66 | AseDecimal = AdoNetCore.AseClient.AseDecimal.Parse("-99999999999999999999.999999999999999999"),
67 | }
68 | }
69 |
70 | // clickhouse client
71 | new object[]
72 | {
73 | new
74 | {
75 | ClickHouseDecimal = new ClickHouse.Client.Numerics.ClickHouseDecimal(System.Numerics.BigInteger.Parse("-2352454252352345235235235235235346353523523"), 10),
76 | }
77 | }
78 |
79 | // mysqlconnector
80 | new object[]
81 | {
82 | new
83 | {
84 | MySqlDateTime = new MySqlConnector.MySqlDateTime(DateTime.Now),
85 | MySqlDateTimeZero = new MySqlConnector.MySqlDateTime(),
86 | MySqlDecimal = Activator.CreateInstance(typeof(MySqlConnector.MySqlDecimal), BindingFlags.NonPublic | BindingFlags.Instance, null, new object[]{"-23524542523523452352352352352.35346353523523"}, null),
87 | MySqlGeometry = MySqlConnector.MySqlGeometry.FromWkb(123, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }),
88 | }
89 | }
90 |
91 | // sqlserver.types
92 | new object[]
93 | {
94 | new
95 | {
96 | SqlHierarchyId = Microsoft.SqlServer.Types.SqlHierarchyId.Parse("/1/2/3/4/5/"),
97 | SqlGeometry = Microsoft.SqlServer.Types.SqlGeometry.Point(66.56, -66.2343, 4977),
98 | SqlGeography = Microsoft.SqlServer.Types.SqlGeography.Point(66.56, -66.2343, 4977),
99 | }
100 | }
101 |
102 | // npgsql
103 | new object[]
104 | {
105 | new
106 | {
107 | NpgsqlLogSequenceNumber = new NpgsqlTypes.NpgsqlLogSequenceNumber(123),
108 | NpgsqlTid = new NpgsqlTypes.NpgsqlTid(123, 456),
109 | NpgsqlTsQueryLexeme = new NpgsqlTypes.NpgsqlTsQueryLexeme("one two", NpgsqlTypes.NpgsqlTsQueryLexeme.Weight.C, true),
110 | NpgsqlTsQueryAnd = new NpgsqlTypes.NpgsqlTsQueryAnd(new NpgsqlTypes.NpgsqlTsQueryLexeme("x ы", NpgsqlTypes.NpgsqlTsQueryLexeme.Weight.C, true), new NpgsqlTypes.NpgsqlTsQueryLexeme("one two", NpgsqlTypes.NpgsqlTsQueryLexeme.Weight.C, true)),
111 | NpgsqlTsQueryNot = new NpgsqlTypes.NpgsqlTsQueryNot(new NpgsqlTypes.NpgsqlTsQueryLexeme("one two", NpgsqlTypes.NpgsqlTsQueryLexeme.Weight.C, true)),
112 | NpgsqlTsQueryOr = new NpgsqlTypes.NpgsqlTsQueryOr(new NpgsqlTypes.NpgsqlTsQueryLexeme("x ы", NpgsqlTypes.NpgsqlTsQueryLexeme.Weight.C, true), new NpgsqlTypes.NpgsqlTsQueryLexeme("one two", NpgsqlTypes.NpgsqlTsQueryLexeme.Weight.C, true)),
113 | NpgsqlTsQueryFollowedBy = new NpgsqlTypes.NpgsqlTsQueryFollowedBy(new NpgsqlTypes.NpgsqlTsQueryLexeme("x ы", NpgsqlTypes.NpgsqlTsQueryLexeme.Weight.C, true), 3, new NpgsqlTypes.NpgsqlTsQueryLexeme("one two", NpgsqlTypes.NpgsqlTsQueryLexeme.Weight.C, true)),
114 | NpgsqlTsQueryEmpty = new NpgsqlTypes.NpgsqlTsQueryEmpty(),
115 | NpgsqlTsVector = NpgsqlTypes.NpgsqlTsVector.Parse("dg80h wi0fhweiofwe "),
116 | NpgsqlInet = new NpgsqlTypes.NpgsqlInet(System.Net.IPAddress.Parse("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), 123),
117 | NpgsqlInterval = new NpgsqlTypes.NpgsqlInterval(1, 2, 3),
118 | NpgsqlLine = new NpgsqlTypes.NpgsqlLine(-1, -2, -3),
119 | NpgsqlPoint = new NpgsqlTypes.NpgsqlPoint(1, 2),
120 | NpgsqlCircle = new NpgsqlTypes.NpgsqlCircle(1, 2, 3),
121 | NpgsqlPolygon = new NpgsqlTypes.NpgsqlPolygon(new []{new NpgsqlTypes.NpgsqlPoint(1, 2), new NpgsqlTypes.NpgsqlPoint(3, -2)}),
122 | NpgsqlPath = new NpgsqlTypes.NpgsqlPath(new []{new NpgsqlTypes.NpgsqlPoint(1, 2), new NpgsqlTypes.NpgsqlPoint(3, -2)}, true),
123 | NpgsqlBox = new NpgsqlTypes.NpgsqlBox(new NpgsqlTypes.NpgsqlPoint(1, 2), new NpgsqlTypes.NpgsqlPoint(3, -2)),
124 | NpgsqlLSeg = new NpgsqlTypes.NpgsqlLSeg(new NpgsqlTypes.NpgsqlPoint(1, 2), new NpgsqlTypes.NpgsqlPoint(3, -2)),
125 | BitArray = new BitArray(new byte[]{0x7E}),
126 | BitVector32 = new System.Collections.Specialized.BitVector32(1354467),
127 | Tuple = Tuple.Create(new NpgsqlTypes.NpgsqlBox(new NpgsqlTypes.NpgsqlPoint(1, 2), new NpgsqlTypes.NpgsqlPoint(3, -2)), 123),
128 | ValueTuple = (new NpgsqlTypes.NpgsqlBox(new NpgsqlTypes.NpgsqlPoint(1, 2), new NpgsqlTypes.NpgsqlPoint(3, -2)), 123),
129 | PhysicalAddress = System.Net.NetworkInformation.PhysicalAddress.Parse("00-11-22-33-44-55"),
130 | Array1 = new int[]{ 1, 2,3 },
131 | Array2 = new object[]{ 1, 2,3 },
132 | Array3 = new NpgsqlTypes.NpgsqlPoint[]{ new NpgsqlTypes.NpgsqlPoint(1, 2), new NpgsqlTypes.NpgsqlPoint(1, 2)},
133 | Array4 = new object[]{ new NpgsqlTypes.NpgsqlPoint(1, 2), new NpgsqlTypes.NpgsqlPoint(1, 2), 1 },
134 | Array5 = new object[][]{ new object[]{new NpgsqlTypes.NpgsqlPoint(1, 2), 2 }, new object[]{1,new NpgsqlTypes.NpgsqlPoint(1, 2) } },
135 | List = new List