├── .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 | [![NuGet Version](https://img.shields.io/nuget/vpre/linq2db.linqpad)](https://www.nuget.org/profiles/LinqToDB.LINQPad) [![License](https://img.shields.io/github/license/linq2db/linq2db.LINQPad)](MIT-LICENSE.txt) 4 | 5 | [![Master branch build](https://img.shields.io/azure-devops/build/linq2db/linq2db/8/master?label=build%20(master))](https://dev.azure.com/linq2db/linq2db/_build?definitionId=8&_a=summary) [![Latest build](https://img.shields.io/azure-devops/build/linq2db/linq2db/8?label=build%20(latest))](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 | 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 |