diff --git a/.editorconfig b/.editorconfig
index c0eadd3..50b1333 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,5 +1,3 @@
-; What is EditorConfig? http://editorconfig.org/
-
root = true
; use tabs identation for all files
@@ -227,6 +225,9 @@ dotnet_diagnostic.IDE0079.severity = none # IDE0079: Remove unnecessary suppress
dotnet_diagnostic.IDE0080.severity = none # IDE0080: Remove unnecessary suppression operator
dotnet_diagnostic.IDE0081.severity = none # IDE0081: Remove ByVal
dotnet_diagnostic.IDE0083.severity = none # IDE0083: Use pattern matching (not operator)
+dotnet_diagnostic.IDE0130.severity = none # IDE0130: Namespace does not match folder structure
+dotnet_diagnostic.IDE0160.severity = none # IDE0160: Use block-scoped namespace
+dotnet_diagnostic.IDE0161.severity = error # IDE0161: Use file-scoped namespace
dotnet_diagnostic.IDE1006.severity = none # IDE1006: Naming rule violation
dotnet_diagnostic.CS1998.severity = error # CS1998: Async method lacks 'await' operators and will run synchronously
diff --git a/.gitignore b/.gitignore
index 5fcb5d8..eef3217 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
-#Ignore files build by Visual Studio
.vs/
bin/
obj/
+*.zip
*.lpx
*.lpx6
+*.csproj.user
diff --git a/Build/BuildNuspecs.ps1 b/Build/BuildNuspecs.ps1
index 9c1e861..0d2c6f7 100644
--- a/Build/BuildNuspecs.ps1
+++ b/Build/BuildNuspecs.ps1
@@ -37,7 +37,7 @@ if ($version) {
$xml.package.metadata.AppendChild($child)
$child = $xml.CreateElement('copyright', $nsUri)
- $child.InnerText = 'Copyright © 2016-2020 ' + $authors
+ $child.InnerText = 'Copyright © 2016-2023 ' + $authors
$xml.package.metadata.AppendChild($child)
$child = $xml.CreateElement('authors', $nsUri)
diff --git a/Source/Connection.png b/Build/Connection.png
similarity index 100%
rename from Source/Connection.png
rename to Build/Connection.png
diff --git a/Source/FailedConnection.png b/Build/FailedConnection.png
similarity index 100%
rename from Source/FailedConnection.png
rename to Build/FailedConnection.png
diff --git a/Build/Pack.bat b/Build/Pack.bat
new file mode 100644
index 0000000..820dcac
--- /dev/null
+++ b/Build/Pack.bat
@@ -0,0 +1,36 @@
+ECHO OFF
+ECHO Packing %2
+
+DEL linq2db.LINQPad.%2
+DEL linq2db.LINQPad.%2.zip
+
+REM LINQPad 5 driver archive generation
+IF %2 EQU lpx (
+ REM remove resource satellite assemblies
+ RD /S /Q %1\cs
+ RD /S /Q %1\de
+ RD /S /Q %1\es
+ RD /S /Q %1\fr
+ RD /S /Q %1\it
+ RD /S /Q %1\ja
+ RD /S /Q %1\ko
+ RD /S /Q %1\pl
+ RD /S /Q %1\pt
+ RD /S /Q %1\pt-BR
+ RD /S /Q %1\ru
+ RD /S /Q %1\tr
+ RD /S /Q %1\zh-Hans
+ RD /S /Q %1\zh-Hant
+
+ REM remove not needed files
+ DEL /Q %1\linq2db.*.xml
+ DEL /Q %1\*.pdb
+
+ "C:\Program Files\7-Zip\7z.exe" -r a linq2db.LINQPad.%2.zip %1\*.* %1\..\..\..\..\Build\Connection.png %1\..\..\..\..\Build\FailedConnection.png %1\..\..\..\..\Build\header.xml
+)
+
+REM LINQPad 7 driver archive generation
+IF %2 EQU lpx6 ("C:\Program Files\7-Zip\7z.exe" a linq2db.LINQPad.%2.zip %1\linq2db.LINQPad.dll %1\..\..\..\..\Build\Connection.png %1\..\..\..\..\Build\FailedConnection.png %1\linq2db.LINQPad.deps.json)
+
+REN linq2db.LINQPad.%2.zip linq2db.LINQPad.%2
+
diff --git a/Build/README.md b/Build/README.md
index ecc2898..b8a756f 100644
--- a/Build/README.md
+++ b/Build/README.md
@@ -1,23 +1,25 @@
-## LINQ to DB LINQPad 6 and 7 Driver
+# LINQ to DB LINQPad 7 Driver
-This nuget package is a driver for [LINQPad 6 and 7](http://www.linqpad.net).
+This nuget package is a driver for [LINQPad 7](http://www.linqpad.net). Support for LINQPad 6 is available via older 4.x drivers.
Following databases supported:
-- **DB2** (LUW, z/OS) (only 64-bit version)
+- **ClickHouse**: using Binary, HTTP and MySQL interfaces
+- **DB2** (LUW, z/OS): x64-bit version of LINQPad only
- **Firebird**
-- **Informix** (only 64-bit version)
-- **Microsoft Access** *(supports both OleDb and ODBC)*
-- **Microsoft Sql Server** 2005+ *(including **Microsoft Sql Azure**)*
-- **Microsoft Sql Server Compact (SqlCe)**
-- **MySql/MariaDB**
+- **Informix**: x64-bit version of LINQPad only
+- **Microsoft Access**: both OLE DB and ODBC drivers
+- **Microsoft SQL Server** 2005+ *(including **Microsoft SQL Azure**)*
+- **Microsoft SQL Server Compact (SQL CE)**
+- **MariaDB**
+- **MySql**
- **Oracle**
- **PostgreSQL**
-- **SQLite**
- **SAP HANA** *(client software must be installed, supports both Native and ODBC providers)*
- **SAP/Sybase ASE**
+- **SQLite**
-### Installation
+## Installation
- Click "Add connection" in LINQPad.
- In the "Choose Data Context" dialog, press the "View more drivers..." button.
@@ -26,4 +28,3 @@ Following databases supported:
- In the "Choose Data Context" dialog, select the "LINQ to DB" driver and click the "Next" button.
- In the "LINQ to DB connection" dialog, supply your connection information.
- You're done.
-
diff --git a/Build/azure-pipelines.yml b/Build/azure-pipelines.yml
index 510ecb3..7f0146d 100644
--- a/Build/azure-pipelines.yml
+++ b/Build/azure-pipelines.yml
@@ -1,10 +1,10 @@
variables:
solution: 'linq2db.LINQPad.sln'
build_configuration: 'Release'
- assemblyVersion: 4.2.0.0
- nugetVersion: 4.2.0
- nugetDevVersion: 4.2.1
- nugetPRVersion: 4.2.1
+ assemblyVersion: 5.0.0.0
+ nugetVersion: 5.0.0
+ nugetDevVersion: 5.0.1
+ nugetPRVersion: 5.0.1
artifact_lpx: 'lpx'
artifact_lpx6: 'lpx6'
artifact_nuget: 'nuget'
@@ -98,7 +98,6 @@ stages:
displayName: Publish to Azure Artifacts feed
condition: eq(variables['Build.SourceBranchName'], 'master')
-# apikey exires Oct/2020
- task: NuGetCommand@2
inputs:
command: 'push'
diff --git a/Source/header.xml b/Build/header.xml
similarity index 97%
rename from Source/header.xml
rename to Build/header.xml
index 8778755..1d210ca 100644
--- a/Source/header.xml
+++ b/Build/header.xml
@@ -1,5 +1,5 @@
-
-
- linq2db.LINQPad.dll
- https://github.com/linq2db/linq2db.LINQPad
-
+
+
+ linq2db.LINQPad.dll
+ https://github.com/linq2db/linq2db.LINQPad
+
diff --git a/Build/linq2db.LINQPad.nuspec b/Build/linq2db.LINQPad.nuspec
index 1b0c4a2..9a9d1c1 100644
--- a/Build/linq2db.LINQPad.nuspec
+++ b/Build/linq2db.LINQPad.nuspec
@@ -2,48 +2,51 @@
linq2db.LINQPad
- LINQ to DB driver for LINQPad 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.
+ LINQ to DB driver for LINQPad 7
+ 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.
- 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
+ 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
README.md
-
-
-
+
+
+
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 2165f19..58366b1 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -1,42 +1,37 @@
-
+
+
+
+
-
-
+
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/LinqToDB.Templates/NotifyPropertyChanged.ttinclude b/LinqToDB.Templates/NotifyPropertyChanged.ttinclude
deleted file mode 100644
index 32cad1c..0000000
--- a/LinqToDB.Templates/NotifyPropertyChanged.ttinclude
+++ /dev/null
@@ -1,344 +0,0 @@
-<#
- {
- var beforeGenerateModel = BeforeGenerateModel;
- BeforeGenerateModel = () =>
- {
- beforeGenerateModel();
- NotifyPropertyChangedImpl();
- };
-
- SetPropertyValueAction += (obj,prop,val) =>
- {
- if (prop == "IsNotifying")
- obj.IsNotifying = (bool)val;
- };
- }
-#><#+
-public bool ImplementNotifyPropertyChanging;
-public bool SkipNotifyPropertyChangedImplementation = false;
-
-void NotifyPropertyChangedImpl()
-{
- foreach (Property prop in GetTreeNodes(Model).OfType().Where(p => p.IsNotifying).ToList())
- {
- List parentMembers;
-
- MemberGroup gr = null;
-
- if (prop.Parent is Class)
- {
- var parent = (Class)prop.Parent;
- parentMembers = parent.Members;
- }
- else
- {
- var parent = (MemberGroup)prop.Parent;
-
- parent.IsCompact = false;
-
- parentMembers = parent.Members;
-
- if (parent.IsPropertyGroup)
- gr = parent;
- }
-
- var name = prop.Name.Trim();
- var type = prop.BuildType().Trim();
-
- if (gr == null)
- {
- gr = new MemberGroup
- {
- Region = name + " : " + type,
- Members = { prop },
- IsPropertyGroup = true,
- };
-
- var index = parentMembers.IndexOf(prop);
-
- parentMembers.RemoveAt(index);
- parentMembers.Insert (index, gr);
- }
-
- gr.Conditional = prop.Conditional;
- prop.Conditional = null;
-
- if (prop.IsAuto)
- {
- var field = new Field(() => type, "_" + ToCamelCase(name))
- {
- AccessModifier = AccessModifier.Private,
- InsertBlankLineAfter = false,
- };
-
- if (prop.InitValue != null)
- field.InitValue = prop.InitValue;
-
- gr.Members.Insert(0, field);
-
- prop.Name = " " + name;
- prop.TypeBuilder = () => " " + type;
- prop.IsAuto = false;
-
- if (prop.HasGetter) prop.GetBodyBuilders.Add(() => new [] { "return " + field.Name + ";" });
- if (prop.HasSetter) prop.SetBodyBuilders.Add(() => new [] { field.Name + " = value;" });
- }
-
- var methods = new MemberGroup
- {
- Region = "INotifyPropertyChanged support",
- Members =
- {
- new Field(() => "const string", "NameOf" + name)
- {
- InitValue = ToStringLiteral(name),
- AccessModifier = AccessModifier.Public,
- },
- new Field(() => "PropertyChangedEventArgs", "_" + ToCamelCase(name) + "ChangedEventArgs")
- {
- InitValue = "new PropertyChangedEventArgs(NameOf" + name + ")",
- AccessModifier = AccessModifier.Private,
- IsStatic = true,
- IsReadonly = true,
- },
- new Method(() => "void", "On" + name + "Changed", null,
- () => new[] { "OnPropertyChanged(_" + ToCamelCase(name) + "ChangedEventArgs);" })
- {
- AccessModifier = AccessModifier.Private
- }
- }
- };
-
- gr.Members.Add(methods);
-
- if (prop.Dependents.Count == 0)
- prop.Dependents.Add(name);
-
- if (ImplementNotifyPropertyChanging)
- {
- gr.Members.Add(new MemberGroup
- {
- Region = "INotifyPropertyChanging support",
- Members =
- {
- new Field(() => "PropertyChangingEventArgs", "_" + ToCamelCase(name) + "ChangingEventArgs")
- {
- InitValue = "new PropertyChangingEventArgs(NameOf" + name + ")",
- AccessModifier = AccessModifier.Private,
- IsStatic = true,
- IsReadonly = true,
- },
- new Method(() => "void", "On" + name + "Changing", null,
- () => new[] { "OnPropertyChanging(_" + ToCamelCase(name) + "ChangingEventArgs);" })
- {
- AccessModifier = AccessModifier.Private
- }
- }
- });
- }
-
- if (prop.HasSetter)
- {
- var setBody = prop.BuildSetBody().Select(s => "\t" + s).ToArray();
- prop.SetBodyBuilders.Clear();
- prop.SetBodyBuilders.Add(() => setBody);
-
- string getValue;
-
- var getBody = prop.BuildGetBody().ToArray();
- if (getBody.Length == 1 && getBody[0].StartsWith("return"))
- {
- getValue = getBody[0].Substring("return".Length).Trim(' ', '\t', ';');
- }
- else
- {
- getValue = name;
- }
-
- var insSpaces = setBody.Length > 1;
- var n = 0;
-
- prop.SetBodyBuilders.Insert(n++, () => new [] {"if (" + getValue + " != value)", "{" });
-
- if (ImplementNotifyPropertyChanging)
- {
- foreach (var dp in prop.Dependents)
- prop.SetBodyBuilders.Insert(n++, () => new [] { "\tOn" + dp + "Changing();" });
- prop.SetBodyBuilders.Insert(n++, () => new [] { "" });
- }
-
- prop.SetBodyBuilders.Insert(n++, () => new [] { "\tBefore" + name + "Changed(value);" });
-
- if (insSpaces)
- {
- prop.SetBodyBuilders.Insert(3, () => new [] { "" });
- prop.SetBodyBuilders.Add(() => new [] { "" });
- }
-
- prop.SetBodyBuilders.Add(() => new [] { "\tAfter" + name + "Changed();" });
- prop.SetBodyBuilders.Add(() => new [] { "" });
-
- foreach (var dp in prop.Dependents)
- prop.SetBodyBuilders.Add(() => new [] { "\tOn" + dp + "Changed();" });
-
- prop.SetBodyBuilders.Add(() => new [] { "}" });
-
- methods.Members.Insert(0, new MemberGroup
- {
- IsCompact = true,
- Members =
- {
- new Method(() => "void", "Before" + name + "Changed", new Func[] { () => type + " newValue" }) { AccessModifier = AccessModifier.Partial },
- new Method(() => "void", "After" + name + "Changed") { AccessModifier = AccessModifier.Partial },
- }
- });
- }
-
- prop.Parent.SetTree();
-
- ITree p = prop.Parent;
-
- while (!(p is Class) && p != null)
- p = p.Parent;
-
- if (p != null)
- {
- var cl = (Class)p;
-
- if (!SkipNotifyPropertyChangedImplementation && !cl.Interfaces.Contains("INotifyPropertyChanged"))
- {
- if (!Model.Usings.Contains("System.ComponentModel"))
- Model.Usings.Add("System.ComponentModel");
-
- cl.Interfaces.Add("INotifyPropertyChanged");
-
- cl.Members.Add(new MemberGroup
- {
- Region = "INotifyPropertyChanged support",
- Members =
- {
- new Event("PropertyChangedEventHandler", "PropertyChanged", true)
- {
- IsVirtual = true,
- Attributes = { new Attribute("field : NonSerialized") }
- },
- new Method(() => "void", "OnPropertyChanged", new Func[] { () => "string propertyName" }, () => OnPropertyChangedBody)
- {
- AccessModifier = AccessModifier.Protected
- },
- new Method(() => "void", "OnPropertyChanged", new Func[] { () => "PropertyChangedEventArgs arg" }, () => OnPropertyChangedArgBody)
- {
- AccessModifier = AccessModifier.Protected
- },
- }
- });
- }
-
- if (ImplementNotifyPropertyChanging && !cl.Interfaces.Contains("INotifyPropertyChanging"))
- {
- if (!Model.Usings.Contains("System.ComponentModel"))
- Model.Usings.Add("System.ComponentModel");
-
- cl.Interfaces.Add("INotifyPropertyChanging");
-
- cl.Members.Add(new MemberGroup
- {
- Region = "INotifyPropertyChanging support",
- Members =
- {
- new Event("PropertyChangingEventHandler", "PropertyChanging", true)
- {
- IsVirtual = true,
- Attributes = { new Attribute("field : NonSerialized") }
- },
- new Method(() => "void", "OnPropertyChanging", new Func[] { () => "string propertyName" }, () => OnPropertyChangingBody)
- {
- AccessModifier = AccessModifier.Protected
- },
- new Method(() => "void", "OnPropertyChanging", new Func[] { () => "PropertyChangingEventArgs arg" }, () => OnPropertyChangingArgBody)
- {
- AccessModifier = AccessModifier.Protected
- },
- }
- });
- }
- }
- }
-}
-
-public string[] OnPropertyChangedBody = new[]
-{
- "var propertyChanged = PropertyChanged;",
- "",
- "if (propertyChanged != null)",
- "{",
- "\tpropertyChanged(this, new PropertyChangedEventArgs(propertyName));",
- "}",
-};
-
-public string[] OnPropertyChangedArgBody = new[]
-{
- "var propertyChanged = PropertyChanged;",
- "",
- "if (propertyChanged != null)",
- "{",
- "\tpropertyChanged(this, arg);",
- "}",
-};
-
-public string[] OnPropertyChangingBody = new[]
-{
- "var propertyChanging = PropertyChanging;",
- "",
- "if (propertyChanging != null)",
- "{",
- "\tpropertyChanging(this, new PropertyChangingEventArgs(propertyName));",
- "}",
-};
-
-public string[] OnPropertyChangingArgBody = new[]
-{
- "var propertyChanging = PropertyChanging;",
- "",
- "if (propertyChanging != null)",
- "{",
- "\tpropertyChanging(this, arg);",
- "}",
-};
-
-partial class Property
-{
- public bool IsNotifying;
- public List Dependents = new List();
-}
-
-class NotifyingProperty : Property
-{
- public NotifyingProperty()
- {
- IsNotifying = true;
- }
-
- public NotifyingProperty(ModelType type, string name, params string[] dependents)
- : base(() => type.ToTypeName(), name, null, null)
- {
- IsNotifying = true;
-
- if (dependents.Length == 0)
- Dependents.Add(name);
- else
- Dependents.AddRange(dependents);
- }
-
- public NotifyingProperty(string type, string name, params string[] dependents)
- : base(() => type, name, null, null)
- {
- IsNotifying = true;
-
- if (dependents.Length == 0)
- Dependents.Add(name);
- else
- Dependents.AddRange(dependents);
- }
-}
-#>
diff --git a/LinqToDB.Templates/T4Model.ttinclude b/LinqToDB.Templates/T4Model.ttinclude
deleted file mode 100644
index 3d1a953..0000000
--- a/LinqToDB.Templates/T4Model.ttinclude
+++ /dev/null
@@ -1,1812 +0,0 @@
-<#@ assembly name="System.Core" #>
-<#@ import namespace="System" #>
-<#@ import namespace="System.Collections.Generic" #>
-<#@ import namespace="System.Linq" #>
-<#@ import namespace="System.Text" #>
-<#+
-static Action WriteComment = (tt,s) => tt.WriteLine("//{0}", s);
-
-Action BeforeGenerateModel = () => {};
-
-bool GenerateProcedureErrors = true;
-
-static bool EnableNullableReferenceTypes = false;
-static Func IsValueType = IsValueTypeDefault;
-
-void GenerateModel()
-{
- Model.SetTree();
-
- if (GenerationEnvironment.Length > 0 && GenerationEnvironment.ToString().Trim().Length == 0)
- GenerationEnvironment.Length = 0;
-
- WriteComment(this, "---------------------------------------------------------------------------------------------------");
- WriteComment(this, " ");
- WriteComment(this, " This code was generated by T4Model template for T4 (https://github.com/linq2db/linq2db).");
- WriteComment(this, " Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.");
- WriteComment(this, " ");
- WriteComment(this, "---------------------------------------------------------------------------------------------------");
-
- WriteLine("");
- WriteLine("#pragma warning disable 1591");
- if (EnableNullableReferenceTypes)
- WriteLine($"#nullable enable");
- WriteLine("");
-
- BeforeGenerateModel();
-
- Model.Render(this);
-
- WriteLine("");
- WriteLine("#pragma warning restore 1591");
-}
-
-void Trim()
-{
- var arr = new[] { '\r', '\n', ' ' };
- while (GenerationEnvironment.Length > 0 && arr.Contains(GenerationEnvironment[GenerationEnvironment.Length - 1]))
- GenerationEnvironment.Length--;
-
- WriteLine("");
-}
-
-static Action WriteUsing = (tt,s) => tt.WriteLine("using {0};", s);
-
-void RenderUsings(List usings)
-{
- var q =
- from ns in usings.Distinct()
- group ns by ns.Split('.')[0];
-
- var groups =
- (from ns in q where ns.Key == "System" select ns).Concat
- (from ns in q where ns.Key != "System" orderby ns.Key select ns);
-
- foreach (var gr in groups)
- {
- foreach (var ns in from s in gr orderby s select s)
- WriteUsing(this, ns);
-
- WriteLine("");
- }
-
- Trim();
-}
-
-// Base data types.
-//
-public interface ITree
-{
- ITree Parent { get; set; }
- IEnumerable GetNodes();
- void SetTree();
-}
-
-ModelSource Model = new ModelSource();
-
-public partial class ModelSource : ITree
-{
- public int CurrentNamespace = 0;
-
- public List Usings = new List { "System" };
- public List Namespaces = new List { new Namespace() };
-
- public Namespace Namespace { get { return Namespaces[CurrentNamespace]; } }
- public List Types { get { return Namespaces[CurrentNamespace].Types; } }
-
- public virtual void Render(GeneratedTextTransformation tt)
- {
- tt.RenderUsings(Usings);
- tt.WriteLine("");
-
- foreach (var nm in Namespaces)
- {
- nm.Render(tt);
- tt.WriteLine("");
- }
-
- tt.Trim();
- }
-
- public ITree Parent { get; set; }
- public IEnumerable GetNodes() { return Namespaces; }
-
- public void SetTree()
- {
- foreach (var ch in GetNodes())
- {
- ch.Parent = this;
- ch.SetTree();
- }
- }
-}
-
-static Action WriteBeginNamespace = (tt,s) => { tt.WriteLine("namespace {0}", s); tt.WriteLine("{"); };
-static Action WriteEndNamespace = tt => tt.WriteLine("}");
-
-public partial class Namespace : ITree
-{
- public string Name;
- public List Types = new List();
- public List Usings = new List();
-
- public virtual void Render(GeneratedTextTransformation tt)
- {
- if (!string.IsNullOrEmpty(Name))
- {
- WriteBeginNamespace(tt, Name);
- tt.PushIndent("\t");
- }
-
- tt.RenderUsings(Usings);
-
- foreach (var t in Types)
- {
- t.Render(tt);
- tt.WriteLine("");
- }
-
- tt.Trim();
-
- if (!string.IsNullOrEmpty(Name))
- {
- tt.PopIndent();
- WriteEndNamespace(tt);
- }
- }
-
- public ITree Parent { get; set; }
- public IEnumerable GetNodes() { return Types; }
-
- public void SetTree()
- {
- foreach (var ch in GetNodes())
- {
- ch.Parent = this;
- ch.SetTree();
- }
- }
-}
-
-public interface IClassMember : ITree
-{
-}
-
-public enum AccessModifier
-{
- Public,
- Protected,
- Internal,
- Private,
- Partial,
- None
-}
-
-public abstract partial class TypeBase : IClassMember
-{
- public AccessModifier AccessModifier = AccessModifier.Public;
- public string Name;
- public bool IsPartial = true;
- public List Comment = new List();
- public List Attributes = new List();
- public string Conditional;
- public string ClassKeyword = "class";
-
- public abstract void Render(GeneratedTextTransformation tt);
-
- protected virtual void BeginConditional(GeneratedTextTransformation tt)
- {
- if (Conditional != null)
- {
- tt.RemoveSpace();
- tt.WriteLine("#if " + Conditional);
- tt.WriteLine("");
- }
- }
-
- protected virtual void EndConditional(GeneratedTextTransformation tt)
- {
- if (Conditional != null)
- {
- tt.RemoveSpace();
- tt.WriteLine("");
- tt.RemoveSpace();
- tt.WriteLine("#endif");
- }
- }
-
- public ITree Parent { get; set; }
- public abstract IEnumerable GetNodes();
- public abstract void SetTree ();
-}
-
-static Action WriteBeginClass = (tt,cl) =>
-{
- tt.Write(cl.AccessModifier.ToString().ToLower() + " ");
- if (cl.IsStatic) tt.Write("static ");
- if (cl.IsPartial) tt.Write("partial ", cl.Name);
- tt.Write("{0} {1}{2}", cl.ClassKeyword, cl.Name, cl.GenericArguments.Count > 0 ? $"<{string.Join(", ", cl.GenericArguments)}>" : string.Empty);
-
- if (!string.IsNullOrEmpty(cl.BaseClass) || cl.Interfaces.Count > 0)
- {
- var arr = new[] { cl.BaseClass }.Concat(cl.Interfaces)
- .Where(n => n != null)
- .ToArray();
-
- tt.Write(" : ");
- tt.Write(string.Join(", ", arr));
- }
-
- tt.WriteLine("");
- tt.WriteLine("{");
-};
-
-static Action WriteEndClass = tt => tt.WriteLine("}");
-
-public partial class Class : TypeBase
-{
- public string BaseClass;
- public List GenericArguments = new List();
- public bool IsStatic = false;
- public List Interfaces = new List();
- public List Members = new List();
-
- public Class()
- {
- }
-
- public Class(string name, params IClassMember[] members)
- {
- Name = name;
- Members.AddRange(members);
- }
-
- public override void Render(GeneratedTextTransformation tt)
- {
- BeginConditional(tt);
-
- foreach (var c in Comment)
- tt.WriteLine("//" + c);
-
- if (Attributes.Count > 0)
- {
- var aa = Attributes.Where(a => !a.IsSeparated).ToList();
-
- if (aa.Count > 0)
- {
- tt.Write("[");
-
- for (var i = 0; i < aa.Count; i++)
- {
- if (i > 0) SkipSpacesAndInsert(tt, ", ");
- aa[i].Render(tt);
- }
-
- tt.WriteLine("]");
- }
-
- aa = Attributes.Where(a => a.IsSeparated).ToList();
-
- foreach (var a in aa)
- {
- tt.Write("[");
- a.Render(tt);
- tt.WriteLine("]");
- }
- }
-
- WriteBeginClass(tt, this);
- tt.PushIndent("\t");
-
- foreach (var cm in Members)
- {
- if (cm is MemberBase)
- {
- var m = (MemberBase)cm;
-
- if (!(m is MemberGroup))
- m.BeginConditional(tt, false);
-
- foreach (var c in m.Comment)
- WriteComment(tt, c);
-
- if (m.Attributes.Count > 0)
- {
- var q =
- from a in m.Attributes
- group a by a.Conditional ?? "";
-
- foreach (var g in q)
- {
- if (g.Key.Length > 0)
- {
- tt.RemoveSpace();
- tt.WriteLine("#if " + g.Key);
- }
-
- var attrs = g.ToList();
-
- tt.Write("[");
-
- for (var i = 0; i < attrs.Count; i++)
- {
- if (i > 0) SkipSpacesAndInsert(tt, ", ");
- attrs[i].Render(tt);
- }
-
- tt.WriteLine("]");
-
- if (g.Key.Length > 0)
- {
- tt.RemoveSpace();
- tt.WriteLine("#endif");
- }
- }
- }
-
- m.Render(tt, false);
- if (m.InsertBlankLineAfter)
- tt.WriteLine("");
-
- if (!(m is MemberGroup))
- m.EndConditional(tt, false);
- }
- else if (cm is TypeBase)
- {
- var t = (TypeBase)cm;
-
- t.Render(tt);
- tt.WriteLine("");
- }
- }
-
- tt.Trim();
-
- tt.PopIndent();
- WriteEndClass(tt);
-
- EndConditional(tt);
- }
-
- public override IEnumerable GetNodes()
- {
- return Members;
- }
-
- public override void SetTree()
- {
- foreach (var ch in GetNodes())
- {
- ch.Parent = this;
- ch.SetTree();
- }
- }
-}
-
-public abstract partial class MemberBase : IClassMember
-{
- public string ID;
- public AccessModifier AccessModifier = AccessModifier.Public;
- public string Name;
- public Func TypeBuilder;
- public List Comment = new List();
- public string EndLineComment;
- public List Attributes = new List();
- public bool InsertBlankLineAfter = true;
- public string Conditional;
-
- public int AccessModifierLen;
- public int ModifierLen;
- public int TypeLen;
- public int NameLen;
- public int ParamLen;
- public int BodyLen;
-
- public string Type
- {
- get { return TypeBuilder?.Invoke(); }
- set { TypeBuilder = () => value; }
- }
-
- public string BuildType() { return TypeBuilder?.Invoke(); }
-
- public virtual int CalcModifierLen() { return 0; }
- public abstract int CalcBodyLen ();
- public virtual int CalcParamLen () { return 0; }
- public abstract void Render (GeneratedTextTransformation tt, bool isCompact);
-
- public virtual void BeginConditional(GeneratedTextTransformation tt, bool isCompact)
- {
- if (Conditional != null)
- {
- tt.RemoveSpace();
- tt.WriteLine("#if " + Conditional);
- if (!isCompact)
- tt.WriteLine("");
- }
- }
-
- public virtual void EndConditional(GeneratedTextTransformation tt, bool isCompact)
- {
- if (Conditional != null)
- {
- tt.RemoveSpace();
- tt.WriteLine("#endif");
- if (!isCompact)
- tt.WriteLine("");
- }
- }
-
- public ITree Parent { get; set; }
- public virtual IEnumerable GetNodes() { return Enumerable.Empty(); }
- public virtual void SetTree () {}
-}
-
-static Action BeginRegion = (tt,s) => { tt.WriteLine("#region {0}", s); };
-static Action EndRegion = (tt) => { tt.WriteLine("#endregion"); };
-
-public partial class MemberGroup : MemberBase
-{
- public string Region;
- public bool IsCompact;
- public bool IsPropertyGroup;
- public List Members = new List();
- public List Errors = new List();
-
- public override int CalcBodyLen() { return 0; }
-
- public override void Render(GeneratedTextTransformation tt, bool isCompact)
- {
- if (!string.IsNullOrEmpty(Region))
- {
- BeginRegion(tt, Region);
- tt.WriteLine("");
- }
-
- BeginConditional(tt, isCompact);
-
- if (Errors.Count > 0 && tt.GenerateProcedureErrors)
- {
- tt.RemoveSpace();
- WriteComment(tt, " Use 'GenerateProcedureErrors=false' to disable errors.");
- foreach (var error in Errors)
- {
- tt.Error(error);
-
- foreach (var e in error.Split('\n'))
- {
- tt.RemoveSpace();
- tt.WriteLine("#error " + e.Trim('\r'));
- }
- }
-
- tt.WriteLine("");
- }
-
- if (IsCompact)
- {
- var allMembers = GetTreeNodes(this).OfType().Where(m => !(m is MemberGroup)).ToList();
-
- if (allMembers.Count > 0)
- {
- int max = allMembers.Max(m => m.AccessModifier == AccessModifier.None ? 0 : m.AccessModifier.ToString().Length);
- foreach (var m in allMembers)
- m.AccessModifierLen = max;
-
- max = allMembers.Max(m => m.CalcModifierLen());
- foreach (var m in allMembers)
- m.ModifierLen = max;
-
- max = allMembers.Max(m => (m.BuildType() ?? "").Length);
- foreach (var m in allMembers)
- m.TypeLen = max;
-
- var notHasGetter = allMembers.OfType().Any(m => m.IsAuto && !m.HasGetter);
- var notHasSetter = allMembers.OfType().Any(m => m.IsAuto && !m.HasSetter);
-
- foreach (var p in allMembers.OfType())
- {
- if (notHasGetter) p.GetterLen = 13;
- if (notHasSetter) p.SetterLen = 13;
- }
-
- max = allMembers.Max(m => m.Name.Length);
- foreach (var m in allMembers)
- m.NameLen = max;
-
- max = allMembers.Max(m => m.CalcParamLen());
- foreach (var m in allMembers)
- m.ParamLen = max;
-
- max = allMembers.Max(m => m.CalcBodyLen());
- foreach (var m in allMembers)
- m.BodyLen = max;
-
- var members =
- (
- from m in allMembers
- select new
- {
- m,
- attrs =
- (
- from a in m.Attributes
- group a by a.Name into gr
- select gr.Select((a,i) => new { a, name = a.Name + "." + i }).ToList() into s
- from a in s
- select a
- ).ToList()
- }
- ).ToList();
-
- var attrWeight =
- (
- from m in members
- from a in m.attrs
- group a by a.name into gr
- select new { gr.Key, Count = gr.Count() }
- ).ToDictionary(a => a.Key, a => a.Count);
-
- var q =
- from m in members
- where m.attrs.Count > 0
- select new { m, w = m.attrs.Sum(aa => attrWeight[aa.name]) } into m
- orderby m.w descending
- select m.m;
-
- var attrs = new List();
-
- foreach (var m in q)
- {
- var list = m.attrs.Select(a => a.name).ToList();
-
- if (attrs.Count == 0)
- attrs.AddRange(list);
- else
- {
- for (var i = 0; i < list.Count; i++)
- {
- var nm = list[i];
-
- if (!attrs.Contains(nm))
- {
- for (var j = i + 1; j < list.Count; j++)
- {
- var idx = attrs.IndexOf(list[j]);
-
- if (idx >= 0)
- {
- attrs.Insert(idx, nm);
- break;
- }
- }
- }
-
- if (!attrs.Contains(nm))
- attrs.Add(nm);
- }
- }
- }
-
- var mms = members.Select(m =>
- {
- var arr = new Attribute[attrs.Count];
-
- foreach (var a in m.attrs)
- arr[attrs.IndexOf(a.name)] = a.a;
-
- return new { m.m, attrs = arr.ToList() };
- }).ToList();
-
- var idxs = Enumerable.Range(0, attrs.Count).Select(_ => new List()).ToList();
-
- for (var i = 0; i < mms.Count; i++)
- for (var j = 0; j < mms[i].attrs.Count; j++)
- if (mms[i].attrs[j] != null)
- idxs[j].Add(i);
-
- var toRemove = new List();
-
- for (int i = 1; i < idxs.Count; i++)
- {
- for (int j = 0; j < i; j++)
- {
- if (idxs[j] == null)
- continue;
-
- if (idxs[i].Intersect(idxs[j]).Count() == 0)
- {
- foreach (var m in mms)
- {
- if (m.attrs[i] != null)
- {
- m.attrs[j] = m.attrs[i];
- m.attrs[i] = null;
- }
- }
-
- idxs[j].AddRange(idxs[i]);
- idxs[i] = null;
- toRemove.Add(i);
- break;
- }
- }
-
- }
-
- foreach (var n in toRemove.OrderByDescending(i => i))
- foreach (var m in mms)
- m.attrs.RemoveAt(n);
-
- var lens = new int[attrs.Count - toRemove.Count];
-
- foreach (var m in mms)
- {
- for (var i = 0; i < m.attrs.Count; i++)
- {
- var a = m.attrs[i];
-
- if (a != null)
- {
- var len = a.Name.Length;
-
- if (a.Parameters.Count >= 0)
- len += a.Parameters.Sum(p => 2 + p.Length);
-
- lens[i] = Math.Max(lens[i], len);
- }
- }
- }
-
- foreach (var m in allMembers)
- {
- if (!(m is MemberGroup))
- m.BeginConditional(tt, IsCompact);
-
- foreach (var c in m.Comment)
- WriteComment(tt, c);
-
- if (attrs.Count > 0)
- {
- var ma = mms.First(mr => mr.m == m);
-
- if (m.Attributes.Count > 0)
- {
- tt.Write("[");
-
- for (var i = 0; i < ma.attrs.Count; i++)
- {
- var a = ma.attrs[i];
-
- if (a == null)
- {
- tt.WriteSpaces(lens[i]);
- if (i + 1 < ma.attrs.Count)
- tt.Write(" ");
- }
- else
- {
- var len = tt.GenerationEnvironment.Length;
- a.Render(tt);
- len = (tt.GenerationEnvironment.Length - len);
-
- var commaAdded = false;
-
- for (var j = i + 1; j < ma.attrs.Count; j++)
- {
- if (ma.attrs[j] != null)
- {
- SkipSpacesAndInsert(tt, ", ");
- commaAdded = true;
- break;
- }
- }
-
- if (i + 1 < ma.attrs.Count && !commaAdded)
- tt.Write(" ");
-
- tt.WriteSpaces(lens[i] - len);
- }
- }
-
- tt.Write("] ");
- }
- else
- {
- tt.WriteSpaces(lens.Sum() + ma.attrs.Count * 2 + 1);
- }
- }
-
- m.Render(tt, true);
-
- if (!IsCompact)
- tt.WriteLine("");
-
- if (!(m is MemberGroup))
- m.EndConditional(tt, IsCompact);
- }
- }
- }
- else
- {
- foreach (var cm in Members)
- {
- if (cm is MemberBase)
- {
- var m = (MemberBase)cm;
-
- if (!(m is MemberGroup))
- m.BeginConditional(tt, IsCompact);
-
- foreach (var c in m.Comment)
- WriteComment(tt, c);
-
- if (m.Attributes.Count > 0)
- {
- var q =
- from a in m.Attributes
- group a by a.Conditional ?? "";
-
- foreach (var g in q)
- {
- if (g.Key.Length > 0)
- {
- tt.RemoveSpace();
- tt.WriteLine("#if " + g.Key);
- }
-
- var attrs = g.ToList();
-
- var aa = attrs.Where(a => !a.IsSeparated).ToList();
-
- if (aa.Count > 0)
- {
- tt.Write("[");
-
- for (var i = 0; i < aa.Count; i++)
- {
- if (i > 0) tt.Write(", ");
- aa[i].Render(tt);
- }
-
- tt.WriteLine("]");
- }
-
- aa = attrs.Where(a => a.IsSeparated).ToList();
-
- foreach (var a in aa)
- {
- tt.Write("[");
- a.Render(tt);
- tt.WriteLine("]");
- }
-
- if (g.Key.Length > 0)
- {
- tt.RemoveSpace();
- tt.WriteLine("#endif");
- }
- }
- }
-
- m.Render(tt, false);
-
- if (m.InsertBlankLineAfter)
- tt.WriteLine("");
-
- if (!(m is MemberGroup))
- m.EndConditional(tt, IsCompact);
- }
- else if (cm is TypeBase)
- {
- var t = (TypeBase)cm;
-
- t.Render(tt);
- tt.WriteLine("");
- }
- }
- }
-
- tt.Trim();
-
- EndConditional(tt, isCompact);
-
- if (!string.IsNullOrEmpty(Region))
- {
- tt.WriteLine("");
- EndRegion(tt);
- }
- }
-
- public override IEnumerable GetNodes() { return Members; }
-
- public override void SetTree()
- {
- foreach (var ch in GetNodes())
- {
- ch.Parent = this;
- ch.SetTree();
- }
- }
-}
-
-static Action WriteField = (tt,f) =>
-{
- var am = f.AccessModifier.ToString().ToLower();
- var mdf =
- (f.IsStatic ? " static" : "") +
- (f.IsReadonly ? " readonly" : "") ;
-
- tt.Write("{0}{1}{2}{3} {4}{5} {6}",
- am, LenDiff(f.AccessModifierLen, am),
- mdf, LenDiff(f.ModifierLen, mdf),
- f.BuildType(), LenDiff(f.TypeLen, f.BuildType()),
- f.Name);
-
- if (f.InitValue != null)
- {
- tt.Write(" = {0}", f.InitValue);
- }
-
- tt.Write(";");
-
- if (!string.IsNullOrEmpty(f.EndLineComment))
- {
- tt.WriteSpaces(f.NameLen - f.Name.Length + f.BodyLen + f.ParamLen - 1);
- tt.Write(" ");
- WriteComment(tt, " " + f.EndLineComment);
- }
- else
- tt.WriteLine("");
-};
-
-public partial class Field : MemberBase
-{
- public bool IsStatic;
- public bool IsReadonly;
- public string InitValue;
-
- public Field()
- {
- }
-
- public Field(ModelType type, string name)
- {
- TypeBuilder = () => type.ToTypeName();
- Name = name;
- }
-
- public Field(Func typeBuilder, string name)
- {
- TypeBuilder = typeBuilder;
- Name = name;
- }
-
- public override int CalcModifierLen()
- {
- return
- (IsStatic ? " static". Length : 0) +
- (IsReadonly ? " readonly".Length : 0) ;
- }
-
- public override int CalcBodyLen() { return InitValue == null ? 1 : 4 + InitValue.Length; }
-
- public override void Render(GeneratedTextTransformation tt, bool isCompact)
- {
- WriteField(tt, this);
- }
-}
-
-static Action WriteEvent = (tt,m) =>
-{
- var am = m.AccessModifier.ToString().ToLower();
- var mdf =
- (m.IsStatic ? " static" : "") +
- (m.IsVirtual ? " virtual" : "") +
- " event";
-
- tt.Write("{0}{1}{2}{3} {4}{5} {6};",
- am, LenDiff(m.AccessModifierLen, am),
- mdf, LenDiff(m.ModifierLen, mdf),
- m.BuildType(), LenDiff(m.TypeLen, m.BuildType()),
- m.Name);
-
- if (!string.IsNullOrEmpty(m.EndLineComment))
- {
- tt.WriteSpaces(m.NameLen - m.Name.Length + m.BodyLen + m.ParamLen - 1);
- tt.Write(" ");
- WriteComment(tt, " " + m.EndLineComment);
- }
- else
- tt.WriteLine("");
-};
-
-public partial class Event : MemberBase
-{
- public bool IsStatic;
- public bool IsVirtual;
-
- public Event()
- {
- }
-
- public Event(Type eventType, string name, bool nullable)
- {
- TypeBuilder = () => new ModelType(eventType, nullable).ToTypeName();
- Name = name;
- }
-
- public Event(string eventType, string name, bool nullable)
- {
- TypeBuilder = () => new ModelType(eventType, true, nullable).ToTypeName();
- Name = name;
- }
-
- public Event(Func typeBuilder, string name)
- {
- TypeBuilder = typeBuilder;
- Name = name;
- }
-
- public override int CalcModifierLen()
- {
- return
- (IsStatic ? " static". Length : 0) +
- (IsVirtual ? " virtual".Length : 0) +
- " event".Length;
- }
-
- public override int CalcBodyLen() { return 1; }
-
- public override void Render(GeneratedTextTransformation tt, bool isCompact)
- {
- WriteEvent(tt, this);
- }
-}
-
-static Action WriteProperty = (tt,p,compact) =>
-{
- var am = p.AccessModifier == AccessModifier.None ? "" : p.AccessModifier.ToString().ToLower() + " ";
- var mdf = p.IsAbstract ? "abstract " : p.IsVirtual ? "virtual " : p.IsOverride ? "override " : p.IsStatic ? "static " : "";
-
- tt.Write("{0}{1}{2}{3}{4}{5} {6}",
- am, LenDiff(p.AccessModifierLen, am),
- mdf, LenDiff(p.ModifierLen, mdf),
- p.BuildType(), LenDiff(p.TypeLen, p.BuildType()),
- p.Name);
-
- Action writeComment = () =>
- {
- if (!string.IsNullOrEmpty(p.EndLineComment))
- {
- tt.Write(" ");
- WriteComment(tt, " " + p.EndLineComment);
- }
- else
- tt.WriteLine("");
- };
-
- if (p.IsAuto)
- {
- tt.Write(LenDiff(p.NameLen + p.ParamLen, p.Name));
-
- var len = tt.GenerationEnvironment.Length;
-
- tt.Write(" { ");
-
- if (!p.HasGetter)
- tt.Write("private ");
- else if (p.GetterLen == 13)
- tt.Write(" ");
- tt.Write("get; ");
-
- if (!p.HasSetter)
- tt.Write("private ");
- else if (p.SetterLen == 13)
- tt.Write(" ");
- tt.Write("set; ");
-
- tt.Write("}");
-
- if (p.EnforceNotNullable)
- tt.Write(" = null!;");
-
- if (!string.IsNullOrEmpty(p.EndLineComment))
- tt.WriteSpaces(p.BodyLen - (tt.GenerationEnvironment.Length - len));
- writeComment();
- }
- else
- {
- if (compact)
- {
- tt.Write(LenDiff(p.NameLen + p.ParamLen, p.Name));
-
- var len = tt.GenerationEnvironment.Length;
-
- tt.Write(" { ");
-
- if (p.HasGetter)
- {
- tt.Write("get { ");
- foreach (var t in p.BuildGetBody())
- tt.Write("{0} ", t);
- tt.Write("} ");
- }
-
- if (p.HasSetter)
- {
- tt.Write("set { ");
- foreach (var t in p.BuildSetBody())
- tt.Write("{0} ", t);
- tt.Write("} ");
- }
-
- tt.Write("}");
-
- if (!string.IsNullOrEmpty(p.EndLineComment))
- tt.WriteSpaces(p.BodyLen - (tt.GenerationEnvironment.Length - len));
- writeComment();
- }
- else
- {
- writeComment();
-
- tt.WriteLine("{");
- tt.PushIndent("\t");
-
- if (p.HasGetter)
- {
- var getBody = p.BuildGetBody().ToArray();
- if (getBody.Length == 1)
- {
- tt.WriteLine("get {{ {0} }}", getBody[0]);
- }
- else
- {
- tt.WriteLine("get");
- tt.WriteLine("{");
- tt.PushIndent("\t");
-
- foreach (var t in getBody)
- tt.WriteLine(t);
-
- tt.PopIndent();
- tt.WriteLine("}");
- }
- }
-
- if (p.HasSetter)
- {
- var setBody = p.BuildSetBody().ToArray();
- if (setBody.Length == 1)
- {
- tt.WriteLine("set {{ {0} }}", setBody[0]);
- }
- else
- {
- tt.WriteLine("set");
- tt.WriteLine("{");
- tt.PushIndent("\t");
-
- foreach (var t in setBody)
- tt.WriteLine(t);
-
- tt.PopIndent();
- tt.WriteLine("}");
- }
- }
-
- tt.PopIndent();
- tt.WriteLine("}");
- }
- }
-};
-
-public partial class Property : MemberBase
-{
- public bool IsAuto = true;
- public string InitValue;
- public bool IsVirtual;
- public bool IsOverride;
- public bool IsAbstract;
- public bool IsStatic;
- public bool HasGetter = true;
- public bool HasSetter = true;
- public List>> GetBodyBuilders = new List>>();
- public List>> SetBodyBuilders = new List>>();
-
- public int GetterLen = 5;
- public int SetterLen = 5;
-
- public Property()
- {
- }
-
- public Property(ModelType type, string name, Func> getBodyBuilder = null, Func> setBodyBuilder = null)
- {
- TypeBuilder = () => type.ToTypeName();
- Name = name;
-
- InitBody(getBodyBuilder, setBodyBuilder);
- }
-
- public Property(bool enforceNotNullable, Func typeBuilder, string name, Func> getBodyBuilder = null, Func> setBodyBuilder = null)
- : this(typeBuilder, name, getBodyBuilder, setBodyBuilder)
- {
- EnforceNotNullable = enforceNotNullable;
- }
-
- public Property(Func typeBuilder, string name, Func> getBodyBuilder = null, Func> setBodyBuilder = null)
- {
- TypeBuilder = typeBuilder;
- Name = name;
-
- InitBody(getBodyBuilder, setBodyBuilder);
- }
-
- public override int CalcModifierLen()
- {
- return IsVirtual ? " virtual".Length : 0;
- }
-
- public override int CalcBodyLen()
- {
- if (IsAuto)
- return 4 + GetterLen + SetterLen; // ' { get; set; }'
-
- var len = " {".Length;
-
- if (HasGetter)
- {
- len += " get {".Length;
- foreach (var t in BuildGetBody())
- len += 1 + t.Length;
- len += " }".Length;
- }
-
- if (HasSetter)
- {
- len += " set {".Length;
- foreach (var t in BuildSetBody())
- len += 1 + t.Length;
- len += " }".Length;
- }
-
- len += " }".Length;
-
- return len;
- }
-
- public override void Render(GeneratedTextTransformation tt, bool isCompact)
- {
- if (!IsAuto && HasGetter)
- {
- var getBody = BuildGetBody().ToArray();
- if (getBody.Length == 1)
- {
- var t = getBody[0];
-
- if (!t.StartsWith("return"))
- {
- t = "return " + t;
-
- if (!t.EndsWith(";"))
- t += ";";
-
- GetBodyBuilders.Clear();
- GetBodyBuilders.Add(() => new [] { t });
- }
- }
- }
-
- WriteProperty(tt, this, isCompact);
- }
-
- public Property InitBody(Func> getBodyBuilder = null, Func> setBodyBuilder = null)
- {
- IsAuto = getBodyBuilder == null && setBodyBuilder == null;
-
- if (getBodyBuilder != null) GetBodyBuilders.Add(getBodyBuilder);
- if (setBodyBuilder != null) SetBodyBuilders.Add(setBodyBuilder);
-
- if (!IsAuto)
- {
- HasGetter = getBodyBuilder != null;
- HasSetter = setBodyBuilder != null;
- }
-
- return this;
- }
-
- public Property InitGetter(Func> getBodyBuilder)
- {
- return InitBody(getBodyBuilder, null);
- }
-
- public IEnumerable BuildGetBody()
- {
- return GetBodyBuilders.SelectMany(builder => builder?.Invoke() ?? Array.Empty());
- }
-
- public IEnumerable BuildSetBody()
- {
- return SetBodyBuilders.SelectMany(builder => builder?.Invoke() ?? Array.Empty());
- }
-
- protected internal virtual bool EnforceNotNullable { get; }
-}
-
-static Action WriteMethod = (tt,m,compact) =>
-{
- var am1 = m.AccessModifier.ToString().ToLower();
- var len1 = m.AccessModifierLen;
- var am2 = "";
- var len2 = 0;
- var mdf = m.IsAbstract ? " abstract" : m.IsVirtual ? " virtual" : m.IsOverride ? " override" : m.IsStatic ? " static" : "";
- var mlen = m.ModifierLen;
-
- if (am1 == "partial" && mdf.Length > 0)
- {
- am2 = " " + am1; len2 = len1 + 1;
- am1 = ""; len1 = 0;
- mdf = mdf.Trim();
- mlen--;
- }
-
- tt.Write("{0}{1}{2}{3}{4}{5}{6}{7}{8} {9}{10}",
- am1, LenDiff(len1, am1),
- mdf, LenDiff(mlen, mdf),
- am2, LenDiff(len2, am2),
- m.BuildType() == null ? "" : " ",
- m.BuildType(), LenDiff(m.TypeLen, m.BuildType() ?? ""),
- m.Name,
- m.GenericArguments.Count > 0 ? $"<{string.Join(", ", m.GenericArguments)}>" : string.Empty);
-
- Action writeComment = () =>
- {
- if (!string.IsNullOrEmpty(m.EndLineComment))
- {
- tt.Write(" ");
- WriteComment(tt, " " + m.EndLineComment);
- }
- else
- tt.WriteLine("");
- };
-
- Action writeParams = () =>
- {
- tt.Write("(");
-
- for (int i = 0; i < m.ParameterBuilders.Count; i++)
- {
- if (i > 0)
- tt.Write(", ");
- tt.Write(m.ParameterBuilders[i]());
- }
-
- tt.Write(")");
- };
-
- if (compact)
- {
- tt.Write(LenDiff(m.NameLen, m.Name));
-
- var len = tt.GenerationEnvironment.Length;
-
- writeParams();
-
- foreach (var s in m.AfterSignature)
- {
- tt.Write(" ");
- tt.Write(s);
- }
-
- len = tt.GenerationEnvironment.Length - len;
-
- if (m.IsAbstract || m.AccessModifier == AccessModifier.Partial)
- {
- tt.Write(";");
- len = 0;
- }
- else
- {
- tt.WriteSpaces(m.ParamLen - len);
-
- len = tt.GenerationEnvironment.Length;
-
- tt.Write(" {");
-
- foreach (var t in m.BuildBody())
- tt.Write(" {0}", t);
-
- tt.Write(" }");
- }
-
- if (!string.IsNullOrEmpty(m.EndLineComment))
- tt.WriteSpaces(m.BodyLen - (tt.GenerationEnvironment.Length - len));
- writeComment();
- }
- else
- {
- writeParams ();
- writeComment();
-
- tt.PushIndent("\t");
- foreach (var s in m.AfterSignature)
- tt.WriteLine(s);
- tt.PopIndent();
-
- tt.WriteLine("{");
- tt.PushIndent("\t");
-
- foreach (var t in m.BuildBody())
- {
- if (t.Length > 1 && t[0] == '#')
- {
- tt.RemoveSpace();
- }
-
- tt.WriteLine(t);
- }
-
- tt.PopIndent();
- tt.WriteLine("}");
- }
-};
-
-public partial class Method : MemberBase
-{
- public bool IsAbstract;
- public bool IsVirtual;
- public bool IsOverride;
- public bool IsStatic;
- public List GenericArguments = new List();
- public List AfterSignature = new List();
- public List> ParameterBuilders = new List>();
- public List>> BodyBuilders = new List>>();
-
- public Method()
- {
- }
-
- public Method(Func typeBuilder, string name, IEnumerable> parameterBuilders = null, params Func>[] bodyBuilders)
- {
- TypeBuilder = typeBuilder;
- Name = name;
-
- if (parameterBuilders != null) ParameterBuilders.AddRange(parameterBuilders);
- if (bodyBuilders != null) BodyBuilders.AddRange(bodyBuilders);
- }
-
- public static Method Create(string type, string name, IEnumerable parameters = null, IEnumerable body = null)
- {
- return new Method(
- () => type,
- name,
- parameters?.Select>((string p) => () => p),
- body?.Select>>(p => () => new[] { p }).ToArray());
- }
-
- public IEnumerable BuildBody()
- {
- return BodyBuilders.SelectMany(builder => builder?.Invoke() ?? Array.Empty());
- }
-
- public override int CalcModifierLen()
- {
- return
- IsAbstract ? " abstract".Length :
- IsVirtual ? " virtual".Length :
- IsStatic ? " static".Length : 0;
- }
-
- public override int CalcBodyLen()
- {
- if (IsAbstract || AccessModifier == AccessModifier.Partial)
- return 1;
-
- var len = " {".Length;
-
- foreach (var t in BuildBody())
- len += 1 + t.Length;
-
- len += " }".Length;
-
- return len;
- }
-
- public override int CalcParamLen()
- {
- return ParameterBuilders.Sum(p => p().Length + 2);
- }
-
- public override void Render(GeneratedTextTransformation tt, bool isCompact)
- {
- WriteMethod(tt, this, isCompact);
- }
-}
-
-static Action WriteAttribute = (tt,a) =>
-{
- tt.Write(a.Name);
-
- if (a.Parameters.Count > 0)
- {
- tt.Write("(");
-
- for (var i = 0; i < a.Parameters.Count; i++)
- {
- if (i > 0)
- if (a.Parameters[i - 1].All(c => c == ' '))
- tt.Write(" ");
- else
- SkipSpacesAndInsert(tt, ", ");
- tt.Write(a.Parameters[i]);
- }
-
- SkipSpacesAndInsert(tt, ")");
- }
-};
-
-public partial class Attribute
-{
- public string Name;
- public List Parameters = new List();
- public string Conditional;
- public bool IsSeparated;
-
- public Attribute()
- {
- }
-
- public Attribute(string name, params string[] ps)
- {
- Name = name;
- Parameters.AddRange(ps);
- }
-
- public virtual void Render(GeneratedTextTransformation tt)
- {
- WriteAttribute(tt, this);
- }
-}
-
-// Helpers.
-//
-
-Func ToPlural = s => s + "s";
-Func ToSingular = s => s;
-
-static string LenDiff(int max, string str)
-{
- var s = "";
-
- while (max-- > str.Length)
- s += " ";
-
- return s;
-}
-
-public void WriteSpaces(int len)
-{
- while (len-- > 0)
- Write(" ");
-}
-
-void RemoveSpace()
-{
- Write(" ");
-
- while (GenerationEnvironment.Length > 0 &&
- (GenerationEnvironment[GenerationEnvironment.Length - 1] == ' ' ||
- GenerationEnvironment[GenerationEnvironment.Length - 1] == '\t'))
- GenerationEnvironment.Length--;
-}
-
-public static IEnumerable GetTreeNodes(ITree parent)
-{
- foreach (var node in parent.GetNodes())
- {
- yield return node;
-
- foreach (var grandNode in GetTreeNodes(node))
- yield return grandNode;
- }
-}
-
-public static ITree FindNode(ITree parent, Func func)
-{
- foreach (var node in parent.GetNodes())
- {
- if (func(node))
- return node;
-
- var n = FindNode(node, func);
-
- if (n != null)
- return n;
- }
-
- return null;
-}
-
-static void SkipSpacesAndInsert(GeneratedTextTransformation tt, string value)
-{
- var l = tt.GenerationEnvironment.Length;
-
- for (; l > 0 && tt.GenerationEnvironment[l - 1] == ' '; l--)
- {
- }
-
- tt.GenerationEnvironment.Insert(l, value);
-}
-
-
-string ToCamelCase(string name)
-{
- var n = 0;
-
- foreach (var c in name)
- {
- if (char.IsUpper(c))
- n++;
- else
- break;
- }
-
- if (n == 0)
- return name;
-
- if (n == name.Length)
- return name.ToLower();
-
- n = Math.Max(1, n - 1);
-
- return name.Substring(0, n).ToLower() + name.Substring(n);
-}
-
-event Action SetPropertyValueAction;
-
-void SetPropertyValue(Property propertyObject, string propertyName, object value)
-{
- if (SetPropertyValueAction != null)
- SetPropertyValueAction(propertyObject, propertyName, value);
-}
-
-static string ToStringLiteral(string value)
-{
- if (value == null)
- return "null";
-
- var sb = new StringBuilder("\"");
-
- foreach (var chr in value)
- {
- switch (chr)
- {
- case '\t': sb.Append("\\t"); break;
- case '\n': sb.Append("\\n"); break;
- case '\r': sb.Append("\\r"); break;
- case '\\': sb.Append("\\\\"); break;
- case '"' : sb.Append("\\\""); break;
- case '\0': sb.Append("\\0"); break;
- case '\u0085':
- case '\u2028':
- case '\u2029':
- sb.Append($"\\u{(ushort)chr:X4}"); break;
- default: sb.Append(chr); break;
- }
- }
-
- sb.Append('"');
-
- return sb.ToString();
-}
-
-public class ModelType
-{
- private readonly IList _arguments = new List();
-
- public static ModelType Create(bool referenceNullable)
- {
- return Create(typeof(TType), referenceNullable);
- }
-
- public static ModelType Create(Type type, bool referenceNullable)
- {
- if (type.IsArray)
- return Array(Create(type.GetElementType(), false), referenceNullable);
-
- return new ModelType(type, referenceNullable);
- }
-
- public static ModelType Array(ModelType elementType, bool referenceNullable)
- {
- return new ModelType(elementType, referenceNullable);
- }
-
- public ModelType(Type type, bool nullable, params ModelType[] typeArguments)
- {
- if (type.IsConstructedGenericType)
- {
- if (typeArguments != null && typeArguments.Length > 0)
- throw new ArgumentException($"{type} must be open generic type or {typeArguments} should be empty");
-
- if (!_aliasedTypes.ContainsKey(type))
- _arguments = new List(type.GetGenericArguments().Select(a => new ModelType(a, /* we don't have type info here */ false)));
- }
-
- Type = type;
- IsReference = !type.IsValueType;
- IsNullable = nullable || (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));
- if (typeArguments != null && typeArguments.Length > 0)
- _arguments = new List(typeArguments);
- }
-
- public ModelType(string type, bool referenceType, bool isNullable, params ModelType[] typeArguments)
- {
- TypeName = type;
- IsReference = referenceType;
- IsNullable = isNullable;
-
- if (typeArguments != null && typeArguments.Length > 0)
- _arguments = new List(typeArguments);
- }
-
- // array constructor
- public ModelType(ModelType elementType, bool isNullable)
- {
- ElementType = elementType;
- IsReference = true;
- IsNullable = isNullable;
- IsArray = true;
- }
-
- public Type Type { get; }
- public string TypeName { get; }
- public ModelType ElementType { get; }
- public bool IsReference { get; }
- public bool IsNullable { get; }
- public bool IsArray { get; }
-
- public IEnumerable Arguments => _arguments ?? System.Array.Empty();
-
- private static readonly IDictionary _aliasedTypes = new Dictionary()
- {
- { typeof(bool), "bool" },
- { typeof(byte), "byte" },
- { typeof(sbyte), "sbyte" },
- { typeof(char), "char" },
- { typeof(decimal), "decimal" },
- { typeof(double), "double" },
- { typeof(float), "float" },
- { typeof(int), "int" },
- { typeof(uint), "uint" },
- { typeof(long), "long" },
- { typeof(ulong), "ulong" },
- { typeof(object), "object" },
- { typeof(short), "short" },
- { typeof(ushort), "ushort" },
- { typeof(string), "string" },
- { typeof(bool?), "bool?" },
- { typeof(byte?), "byte?" },
- { typeof(sbyte?), "sbyte?" },
- { typeof(char?), "char?" },
- { typeof(decimal?), "decimal?" },
- { typeof(double?), "double?" },
- { typeof(float?), "float?" },
- { typeof(int?), "int?" },
- { typeof(uint?), "uint?" },
- { typeof(long?), "long?" },
- { typeof(ulong?), "ulong?" },
- { typeof(short?), "short?" },
- { typeof(ushort?), "ushort?" }
- };
-
- public string ToTypeName()
- {
- var sb = new StringBuilder();
-
- if (TypeName != null)
- sb.Append(TypeName);
- else if (Type != null)
- sb.Append(_aliasedTypes.ContainsKey(Type) ? _aliasedTypes[Type] : (Type.Name.Substring(0, Type.Name.IndexOf('`') < 0 ? Type.Name.Length : Type.Name.IndexOf('`'))));
- else
- sb.Append(ElementType.ToTypeName());
-
- if (_arguments != null && _arguments.Count > 0)
- {
- sb.Append("<");
- sb.Append(string.Join(", ", _arguments.Select(a => a.ToTypeName())));
- sb.Append(">");
- }
-
- if (IsArray)
- sb.Append("[]");
-
- if (EnableNullableReferenceTypes && IsReference && IsNullable)
- sb.Append("?");
-
- return sb.ToString();
- }
-}
-
-static bool IsValueTypeDefault(string typeName)
-{
- switch (typeName)
- {
- case "bool":
- case "bool?":
- case "char":
- case "char?":
- case "decimal":
- case "decimal?":
- case "int":
- case "int?":
- case "uint":
- case "uint?":
- case "byte":
- case "byte?":
- case "sbyte":
- case "sbyte?":
- case "long":
- case "long?":
- case "ulong":
- case "ulong?":
- case "short":
- case "short?":
- case "ushort":
- case "ushort?":
- case "float":
- case "float?":
- case "double":
- case "double?":
- case "DateTime":
- case "DateTime?":
- case "DateTimeOffset":
- case "DateTimeOffset?":
- case "TimeSpan":
- case "TimeSpan?":
- case "Guid":
- case "Guid?":
- case "SqlHierarchyId":
- case "SqlHierarchyId?":
- case "NpgsqlDate":
- case "NpgsqlDate?":
- case "NpgsqlTimeSpan":
- case "NpgsqlTimeSpan?":
- case "NpgsqlPoint":
- case "NpgsqlPoint?":
- case "NpgsqlLSeg":
- case "NpgsqlLSeg?":
- case "NpgsqlBox":
- case "NpgsqlBox?":
- case "NpgsqlPath":
- case "NpgsqlPath?":
- case "NpgsqlPolygon":
- case "NpgsqlPolygon?":
- case "NpgsqlCircle":
- case "NpgsqlCircle?":
- case "NpgsqlLine":
- case "NpgsqlLine?":
- case "NpgsqlInet":
- case "NpgsqlInet?":
- case "NpgsqlDateTime":
- case "NpgsqlDateTime?":
- return true;
- case "object":
- case "string":
- case "byte[]":
- case "BitArray":
- case "SqlGeography":
- case "SqlGeometry":
- case "PhysicalAddress":
- case "Array":
- case "DataTable":
- return false;
- }
-
- return typeName.EndsWith("?");
-}
-
-#>
diff --git a/MIT-LICENSE.txt b/MIT-LICENSE.txt
index 6223674..120733c 100644
--- a/MIT-LICENSE.txt
+++ b/MIT-LICENSE.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2016-2022 Linq To DB Team
+Copyright (c) 2016-2023 Linq To DB Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 860e4b4..0e59b15 100644
--- a/README.md
+++ b/README.md
@@ -1,37 +1,37 @@
-## LINQ to DB LINQPad Driver
+# LINQ to DB LINQPad Driver
[](https://www.nuget.org/packages/linq2db.LINQPad) [](MIT-LICENSE.txt)
[)](https://dev.azure.com/linq2db/linq2db/_build?definitionId=8&_a=summary) [)](https://dev.azure.com/linq2db/linq2db/_build?definitionId=8&_a=summary)
-linq2db.LINQPad is a driver for [LINQPad 5 (.NET Framework)](http://www.linqpad.net) and [LINQPad 6-7 (.NET Core)](http://www.linqpad.net).
+linq2db.LINQPad is a driver for [LINQPad 5 (.NET Framework 4.8)](http://www.linqpad.net) and [LINQPad 7 (.NET 6+)](http://www.linqpad.net).
-Following databases supported (by all LINQPad versions if other not noted):
+Following databases supported (by all LINQPad versions if other not specified):
-- **DB2** (LUW, z/OS) (LINQPad 6+ supports only 64-bit version)
-- **DB2 iSeries** (using [3rd-party provider](https://github.com/LinqToDB4iSeries/Linq2DB4iSeries)) *(iAccess 7.1+ software must be installed)*. **IMPORTANT:** currently available only for LINQPad 5 using linq2db.LINQPad version 2.9.3 or earlier
+- **ClickHouse**: using Binary (LINQPad 7), HTTP and MySQL interfaces
+- **DB2** (LUW, z/OS): LINQPad 7 x64 and LINQPad 5 x86
- **Firebird**
-- **Informix** (LINQPad 6+ supports only 64-bit version)
-- **Microsoft Access** *(supports both OleDb and ODBC)*
-- **Microsoft Sql Server** 2000+ *(including **Microsoft Sql Azure**. LINQPad 6+ [doesn't support](https://stackoverflow.com/a/45418196) **Sql Server 2000**)*
-- **Microsoft Sql Server Compact (SqlCe)**
-- **MySql/MariaDB**
+- **Informix**: LINQPad 7 x64 and LINQPad 5 x86
+- **Microsoft Access**: both OLE DB and ODBC drivers
+- **Microsoft SQL Server** 2005+ *(including **Microsoft SQL Azure**)*
+- **Microsoft SQL Server Compact (SQL CE)**
+- **MariaDB**
+- **MySql**
- **Oracle**
- **PostgreSQL**
-- **SQLite**
- **SAP HANA** *(client software must be installed, supports both Native and ODBC providers)*
- **SAP/Sybase ASE**
+- **SQLite**
-### Download
+## Download
-Releases are hosted on [Github](https://github.com/linq2db/linq2db.LINQPad/releases) and on [Nuget](https://www.nuget.org/packages/linq2db.LINQPad) for LINQPad 6+ driver.
+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 7 driver.
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)).
+## Installation
-### Installation
-
-#### LINQPad 6+ (NuGet)
+### LINQPad 7 (NuGet)
- Click "Add connection" in LINQPad.
- In the "Choose Data Context" dialog, press the "View more drivers..." button.
@@ -41,7 +41,7 @@ Latest build is hosted on [Azure Artifacts](https://dev.azure.com/linq2db/linq2d
- In the "LINQ to DB connection" dialog, supply your connection information.
- You're done.
-#### LINQPad 6+ (Manual)
+### LINQPad 7 (Manual)
- Download latest **.lpx6** file from the link provided above.
- Click "Add connection" in LINQPad.
@@ -52,7 +52,7 @@ Latest build is hosted on [Azure Artifacts](https://dev.azure.com/linq2db/linq2d
- In the "LINQ to DB connection" dialog, supply your connection information.
- You're done.
-#### LINQPad 5 (Choose a driver)
+### LINQPad 5 (Choose a driver)
- Click "Add connection" in LINQPad.
- In the "Choose Data Context" dialog, press the "View more drivers..." button.
@@ -62,7 +62,7 @@ Latest build is hosted on [Azure Artifacts](https://dev.azure.com/linq2db/linq2d
- In the "LINQ to DB connection" dialog, supply your connection information.
- You're done.
-#### LINQPad 5 (Manual)
+### LINQPad 5 (Manual)
- Download latest **.lpx** file from the link provided above.
- Click "Add connection" in LINQPad.
diff --git a/Source/AppJsonConfig.cs b/Source/AppJsonConfig.cs
deleted file mode 100644
index fd17baa..0000000
--- a/Source/AppJsonConfig.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-#if NETCORE
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text.Json;
-using LinqToDB.Configuration;
-
-namespace LinqToDB.LINQPad
-{
- internal class AppJsonConfig : ILinqToDBSettings
- {
- public static ILinqToDBSettings Load(string configPath)
- {
- var config = JsonSerializer.Deserialize(File.ReadAllText(configPath));
-
- return new AppJsonConfig(config?.ConnectionStrings?.Select(entry => (IConnectionStringSettings)new ConnectionStringSettings(entry.Key, entry.Value)).ToArray()
- ?? Array.Empty());
- }
-
- private readonly IConnectionStringSettings[] _connectionStrings;
-
- public AppJsonConfig(IConnectionStringSettings[] connectionStrings)
- {
- _connectionStrings = connectionStrings;
- }
-
- IEnumerable ILinqToDBSettings.DataProviders => Array.Empty();
- string? ILinqToDBSettings.DefaultConfiguration => null;
- string? ILinqToDBSettings.DefaultDataProvider => null;
- IEnumerable ILinqToDBSettings.ConnectionStrings => _connectionStrings;
-
- private class JsonConfig
- {
- public IDictionary? ConnectionStrings { get; set; }
- }
-
- private class ConnectionStringSettings : IConnectionStringSettings
- {
- private readonly string _name;
- private readonly string _connectionString;
-
- public ConnectionStringSettings(string name, string connectionString)
- {
- _name = name;
- _connectionString = connectionString;
- }
-
- string IConnectionStringSettings.ConnectionString => _connectionString;
- string IConnectionStringSettings.Name => _name;
- string? IConnectionStringSettings.ProviderName => null;
- bool IConnectionStringSettings.IsGlobal => false;
- }
- }
-}
-#endif
diff --git a/Source/CSharpTools.cs b/Source/CSharpTools.cs
deleted file mode 100644
index 5b36319..0000000
--- a/Source/CSharpTools.cs
+++ /dev/null
@@ -1,166 +0,0 @@
-using System.Collections.Generic;
-using System.Globalization;
-using System.Text;
-
-namespace LinqToDB.LINQPad
-{
- // added temporary from https://github.com/linq2db/linq2db/pull/1393
- internal static class CSharpTools
- {
- ///
- /// Reserved words (keywords) taken from
- /// .
- /// List actual for C# 8.0.
- ///
- private static readonly ISet _reservedWords
- = new HashSet()
- {
- "abstract", "as", "base", "bool", "break", "byte", "case", "catch",
- "char", "checked", "class", "const", "continue", "decimal", "default", "delegate",
- "do", "double", "else", "enum", "event", "explicit", "extern", "false",
- "finally", "fixed", "float", "for", "foreach", "goto", "if", "implicit",
- "in", "int", "interface", "internal", "is", "lock", "long", "namespace",
- "new", "null", "object", "operator", "out", "override", "params", "private",
- "protected", "public", "readonly", "ref", "return", "sbyte", "sealed", "short",
- "sizeof", "stackalloc", "static", "string", "struct", "switch", "this", "throw",
- "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort",
- "using", "virtual", "void", "volatile", "while"
- };
-
- ///
- /// Contextual words taken from
- /// .
- /// List actual for C# 7.3.
- ///
- private static readonly ISet _contextualWords
- = new HashSet()
- {
- "add", "alias", "ascending", "async", "await", "by", "descending", "dynamic",
- "equals", "from", "get", "global", "group", "into", "join", "let",
- "nameof", "on", "orderby", "partial", "remove", "select", "set", "unmanaged",
- "value", "var", "when", "where", "yield"
- };
-
- private static readonly ISet _otherCharsCategories;
-
- private static readonly ISet _startCharCategories;
-
- static CSharpTools()
- {
- _startCharCategories = new HashSet()
- {
- // Lu letter
- UnicodeCategory.UppercaseLetter,
- // Ll letter
- UnicodeCategory.LowercaseLetter,
- // Lt letter
- UnicodeCategory.TitlecaseLetter,
- // Lm letter
- UnicodeCategory.ModifierLetter,
- // Lo letter
- UnicodeCategory.OtherLetter,
- // Nl letter
- UnicodeCategory.LetterNumber
- };
-
- _otherCharsCategories = new HashSet(_startCharCategories)
- {
- // Mn
- UnicodeCategory.NonSpacingMark,
- // Mc
- UnicodeCategory.SpacingCombiningMark,
- // Nd
- UnicodeCategory.DecimalDigitNumber,
- // Pc
- UnicodeCategory.ConnectorPunctuation,
- // Cf
- UnicodeCategory.Format
- };
- }
-
- ///
- /// Converts to valid C# identifier.
- ///
- public static string ToValidIdentifier(string? name)
- {
- if (name == null || name == string.Empty || name == "@")
- {
- return "_";
- }
-
- if (_reservedWords.Contains(name) || _contextualWords.Contains(name))
- {
- return "@" + name;
- }
-
- if (name.StartsWith("@"))
- {
- if (_reservedWords.Contains(name.Substring(1)) || _contextualWords.Contains(name.Substring(1)))
- {
- return name;
- }
- else
- {
- name = name.Substring(1);
- }
- }
-
- var sb = new StringBuilder();
-
- foreach (var chr in name)
- {
- var cat = CharUnicodeInfo.GetUnicodeCategory(chr);
- if (sb.Length == 0 && !_startCharCategories.Contains(cat) && chr != '_')
- {
- sb.Append('_');
- }
-
- if (sb.Length != 0 && !_otherCharsCategories.Contains(cat))
- {
- sb.Append('_');
- }
- else
- {
- sb.Append(chr);
- }
- }
-
- if (sb.Length >= 2 && sb[0] == '_' && sb[1] == '_' && (sb.Length == 2 || sb[2] != '_'))
- {
- sb.Insert(0, '_');
- }
-
- return sb.ToString();
- }
-
- public static string ToStringLiteral(string? value)
- {
- if (value == null)
- return "null";
-
- var sb = new StringBuilder("\"");
-
- foreach (var chr in value)
- {
- switch (chr)
- {
- case '\t': sb.Append("\\t"); break;
- case '\n': sb.Append("\\n"); break;
- case '\r': sb.Append("\\r"); break;
- case '\\': sb.Append("\\\\"); break;
- case '"': sb.Append("\\\""); break;
- case '\0': sb.Append("\\0"); break;
- case '\u0085':
- case '\u2028':
- case '\u2029':
- sb.Append($"\\u{(ushort)chr:X4}"); break;
- default: sb.Append(chr); break;
- }
- }
-
- sb.Append('"');
-
- return sb.ToString();
- }
- }
-}
diff --git a/Source/CX.cs b/Source/CX.cs
deleted file mode 100644
index 8c09dea..0000000
--- a/Source/CX.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-namespace LinqToDB.LINQPad
-{
- // context properties names
- static class CX
- {
- public const string ProviderName = "providerName";
- public const string ProviderPath = "providerPath";
- public const string ConnectionString = "connectionString";
- public const string ExcludeRoutines = "excludeRoutines";
- public const string ExcludeFKs = "excludeFKs";
- public const string IncludeSchemas = "includeSchemas";
- public const string ExcludeSchemas = "excludeSchemas";
- public const string IncludeCatalogs = "includeCatalogs";
- public const string ExcludeCatalogs = "excludeCatalogs";
- public const string OptimizeJoins = "optimizeJoins";
- public const string UseProviderSpecificTypes = "useProviderSpecificTypes";
- public const string UseCustomFormatter = "useCustomFormatter";
- public const string CommandTimeout = "commandTimeout";
- public const string NormalizeNames = "normalizeNames";
- public const string CustomConfiguration = "customConfiguration";
- }
-}
diff --git a/Source/Compat/IReadOnlySet.cs b/Source/Compat/IReadOnlySet.cs
new file mode 100644
index 0000000..88665ee
--- /dev/null
+++ b/Source/Compat/IReadOnlySet.cs
@@ -0,0 +1,62 @@
+#if !NET5_0_OR_GREATER
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Collections.Generic;
+
+///
+/// Provides a readonly abstraction of a set.
+///
+/// The type of elements in the set.
+internal interface IReadOnlySet : IReadOnlyCollection
+{
+ ///
+ /// Determines if the set contains a specific item
+ ///
+ /// The item to check if the set contains.
+ /// if found; otherwise .
+ bool Contains(T item);
+ ///
+ /// Determines whether the current set is a proper (strict) subset of a specified collection.
+ ///
+ /// The collection to compare to the current set.
+ /// if the current set is a proper subset of other; otherwise .
+ /// other is .
+ bool IsProperSubsetOf(IEnumerable other);
+ ///
+ /// Determines whether the current set is a proper (strict) superset of a specified collection.
+ ///
+ /// The collection to compare to the current set.
+ /// if the collection is a proper superset of other; otherwise .
+ /// other is .
+ bool IsProperSupersetOf(IEnumerable other);
+ ///
+ /// Determine whether the current set is a subset of a specified collection.
+ ///
+ /// The collection to compare to the current set.
+ /// if the current set is a subset of other; otherwise .
+ /// other is .
+ bool IsSubsetOf(IEnumerable other);
+ ///
+ /// Determine whether the current set is a super set of a specified collection.
+ ///
+ /// The collection to compare to the current set
+ /// if the current set is a subset of other; otherwise .
+ /// other is .
+ bool IsSupersetOf(IEnumerable other);
+ ///
+ /// Determines whether the current set overlaps with the specified collection.
+ ///
+ /// The collection to compare to the current set.
+ /// if the current set and other share at least one common element; otherwise, .
+ /// other is .
+ bool Overlaps(IEnumerable other);
+ ///
+ /// Determines whether the current set and the specified collection contain the same elements.
+ ///
+ /// The collection to compare to the current set.
+ /// if the current set is equal to other; otherwise, .
+ /// other is .
+ bool SetEquals(IEnumerable other);
+}
+#endif
diff --git a/Source/Compat/ReadOnlyHashSet.cs b/Source/Compat/ReadOnlyHashSet.cs
new file mode 100644
index 0000000..fb5a0e2
--- /dev/null
+++ b/Source/Compat/ReadOnlyHashSet.cs
@@ -0,0 +1,35 @@
+#if !NET5_0_OR_GREATER
+namespace System.Collections.Generic;
+
+internal sealed class ReadOnlyHashSet : IReadOnlySet
+{
+ private readonly ISet _set;
+
+ public ReadOnlyHashSet(ISet set)
+ {
+ _set = set;
+ }
+
+ int IReadOnlyCollection.Count => _set.Count;
+
+ bool IReadOnlySet.Contains(T item) => _set.Contains(item);
+
+ IEnumerator IEnumerable.GetEnumerator() => _set.GetEnumerator();
+
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_set).GetEnumerator();
+
+ bool IReadOnlySet.IsProperSubsetOf(IEnumerable other) => _set.IsProperSubsetOf(other);
+
+ bool IReadOnlySet.IsProperSupersetOf(IEnumerable other) => _set.IsProperSupersetOf(other);
+
+ bool IReadOnlySet.IsSubsetOf(IEnumerable other) => _set.IsSubsetOf(other);
+
+ bool IReadOnlySet.IsSupersetOf(IEnumerable other) => _set.IsSupersetOf(other);
+
+ bool IReadOnlySet.Overlaps(IEnumerable other) => _set.Overlaps(other);
+
+ bool IReadOnlySet.SetEquals(IEnumerable other) => _set.SetEquals(other);
+
+
+}
+#endif
diff --git a/Source/Compat/ReadOnlySetExtensions.cs b/Source/Compat/ReadOnlySetExtensions.cs
new file mode 100644
index 0000000..02b61b0
--- /dev/null
+++ b/Source/Compat/ReadOnlySetExtensions.cs
@@ -0,0 +1,13 @@
+namespace System.Collections.Generic;
+
+internal static class ReadOnlySetExtensions
+{
+ public static IReadOnlySet AsReadOnly(this HashSet set)
+ {
+#if NET5_0_OR_GREATER
+ return set;
+#else
+ return new ReadOnlyHashSet(set);
+#endif
+ }
+}
diff --git a/Source/Configuration/AppConfig.cs b/Source/Configuration/AppConfig.cs
new file mode 100644
index 0000000..efa9bf6
--- /dev/null
+++ b/Source/Configuration/AppConfig.cs
@@ -0,0 +1,102 @@
+using System.IO;
+using System.Text.Json;
+using System.Xml;
+using LinqToDB.Configuration;
+
+namespace LinqToDB.LINQPad;
+
+///
+/// Implements Linq To DB connection settings provider, which use data from JSON config.
+/// Used as settings source for static data context.
+///
+internal sealed class AppConfig : ILinqToDBSettings
+{
+ public static ILinqToDBSettings LoadJson(string configPath)
+ {
+ var config = JsonSerializer.Deserialize(File.ReadAllText(configPath));
+
+ if (config?.ConnectionStrings?.Count is null or 0)
+ return new AppConfig(Array.Empty());
+
+ var connections = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
+ foreach (var cn in config.ConnectionStrings)
+ {
+ if (cn.Key.EndsWith("_ProviderName", StringComparison.InvariantCultureIgnoreCase))
+ continue;
+
+ connections.Add(cn.Key, new ConnectionStringSettings(cn.Key, cn.Value));
+ }
+
+ foreach (var cn in config.ConnectionStrings)
+ {
+ if (!cn.Key.EndsWith("_ProviderName", StringComparison.InvariantCultureIgnoreCase))
+ continue;
+
+ var key = cn.Key.Substring(0, cn.Key.Length - "_ProviderName".Length);
+ if (connections.TryGetValue(key, out var cs))
+ cs.ProviderName = cn.Value;
+ }
+
+ return new AppConfig(connections.Values.ToArray());
+ }
+
+ public static ILinqToDBSettings LoadAppConfig(string configPath)
+ {
+ var xml = new XmlDocument() { XmlResolver = null };
+ xml.Load(XmlReader.Create(new StringReader(File.ReadAllText(configPath)), new XmlReaderSettings() { XmlResolver = null }));
+
+ var connections = xml.SelectNodes("/configuration/connectionStrings/add");
+
+ if (connections?.Count is null or 0)
+ return new AppConfig(Array.Empty());
+
+ var settings = new List();
+
+ foreach (XmlElement node in connections)
+ {
+ var name = node.Attributes["name" ]?.Value;
+ var connectionString = node.Attributes["connectionString"]?.Value;
+ var providerName = node.Attributes["providerName" ]?.Value;
+
+ if (name != null && connectionString != null)
+ settings.Add(new ConnectionStringSettings(name, connectionString) { ProviderName = providerName });
+ }
+
+ return new AppConfig(settings.ToArray());
+ }
+
+ private readonly IConnectionStringSettings[] _connectionStrings;
+
+ public AppConfig(IConnectionStringSettings[] connectionStrings)
+ {
+ _connectionStrings = connectionStrings;
+ }
+
+ IEnumerable ILinqToDBSettings.DataProviders => Array.Empty();
+ string? ILinqToDBSettings.DefaultConfiguration => null;
+ string? ILinqToDBSettings.DefaultDataProvider => null;
+ IEnumerable ILinqToDBSettings.ConnectionStrings => _connectionStrings;
+
+ private sealed class JsonConfig
+ {
+ public IDictionary? ConnectionStrings { get; set; }
+ }
+
+ private sealed class ConnectionStringSettings : IConnectionStringSettings
+ {
+ private readonly string _name;
+ private readonly string _connectionString;
+
+ public ConnectionStringSettings(string name, string connectionString)
+ {
+ _name = name;
+ _connectionString = connectionString;
+ }
+
+ string IConnectionStringSettings.ConnectionString => _connectionString;
+ string IConnectionStringSettings.Name => _name;
+ bool IConnectionStringSettings.IsGlobal => false;
+
+ public string? ProviderName { get; set; }
+ }
+}
diff --git a/Source/Configuration/ConnectionSettings.cs b/Source/Configuration/ConnectionSettings.cs
new file mode 100644
index 0000000..806f08d
--- /dev/null
+++ b/Source/Configuration/ConnectionSettings.cs
@@ -0,0 +1,541 @@
+using System.Data.Common;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Xml.Linq;
+using LINQPad.Extensibility.DataContext;
+using LinqToDB.LINQPad.Json;
+using PN = LinqToDB.ProviderName;
+
+namespace LinqToDB.LINQPad;
+
+// IMPORTANT:
+// settings, marked by [JsonIgnore] stored in default LINQPad connection option properties and must be copied manually on settings save/load
+internal sealed class ConnectionSettings
+{
+ #region Save/Load/Migrate
+ ///
+ /// Starting from v5 release we store json string in settings instead of multiple XML nodes to simplify settings management.
+ ///
+ private const string SETTINGS_NODE = "SettingsV5";
+
+ private static readonly JsonSerializerOptions _jsonOptions;
+
+ static ConnectionSettings()
+ {
+ _jsonOptions = new()
+ {
+ // deserialization options: use permissive options
+ AllowTrailingCommas = true,
+ ReadCommentHandling = JsonCommentHandling.Skip,
+ PropertyNameCaseInsensitive = true,
+ // serialization options
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ WriteIndented = false,
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
+ };
+
+ // register IReadOnlySet converter factory
+ _jsonOptions.Converters.Add(IReadOnlySetConverter.Factory);
+ }
+
+ ///
+ /// Load connection settings from LINQPad connection object.
+ ///
+ public static ConnectionSettings Load(IConnectionInfo cxInfo)
+ {
+ ConnectionSettings? settings = null;
+
+ var json = GetString(cxInfo, SETTINGS_NODE);
+
+ if (json != null)
+ {
+ settings = JsonSerializer.Deserialize(json, _jsonOptions);
+
+ if (settings != null)
+ {
+ settings.Connection ??= new();
+ settings.Schema ??= new();
+ settings.Scaffold ??= new();
+ settings.LinqToDB ??= new();
+ settings.StaticContext ??= new();
+ }
+ }
+
+ settings ??= Legacy.Load(cxInfo);
+
+ // load data from predefined IConnectionInfo properties
+ // Main reason we use predefined properties is to provide connection configuration options to LINQPad so it could use it
+ // for raw database access functionality
+ settings.Connection.ConnectionString = cxInfo.DatabaseInfo.CustomCxString;
+ settings.Connection.Server = cxInfo.DatabaseInfo.Server;
+ settings.Connection.DatabaseName = cxInfo.DatabaseInfo.Database;
+ settings.Connection.DbVersion = cxInfo.DatabaseInfo.DbVersion;
+ settings.Connection.EncryptConnectionString = cxInfo.DatabaseInfo.EncryptCustomCxString;
+ settings.Connection.ProviderFactory = cxInfo.DatabaseInfo.Provider;
+ settings.Connection.DisplayName = cxInfo.DisplayName;
+ settings.Connection.IsProduction = cxInfo.IsProduction;
+ settings.Connection.Persistent = cxInfo.Persist;
+
+ settings.Scaffold.Pluralize = !cxInfo.DynamicSchemaOptions.NoPluralization;
+ settings.Scaffold.Capitalize = !cxInfo.DynamicSchemaOptions.NoCapitalization;
+
+ settings.StaticContext.ConfigurationPath = cxInfo.AppConfigPath;
+ settings.StaticContext.ContextTypeName = cxInfo.CustomTypeInfo.CustomTypeName;
+ settings.StaticContext.ContextAssemblyPath = cxInfo.CustomTypeInfo.CustomAssemblyPath;
+
+ // manually decrypt secondary connection
+ if (settings.Connection.EncryptConnectionString && settings.Connection.SecondaryConnectionString != null)
+ settings.Connection.SecondaryConnectionString = cxInfo.Decrypt(settings.Connection.SecondaryConnectionString);
+
+ return settings;
+
+ // TODO: debug method to reset modifications
+ //return LoadLegacySettings(cxInfo);
+ }
+
+ ///
+ /// Save connection settings to LINQPad connection object.
+ /// This method should be called from method only.
+ ///
+ public void Save(IConnectionInfo cxInfo)
+ {
+ // encrypt sencondary connection string manually
+ if (Connection.EncryptConnectionString && Connection.SecondaryConnectionString != null)
+ Connection.SecondaryConnectionString = cxInfo.Encrypt(Connection.SecondaryConnectionString);
+
+ // save data, stored in predefined IConnectionInfo properties to them
+ cxInfo.DatabaseInfo.CustomCxString = Connection.ConnectionString;
+ cxInfo.DatabaseInfo.Provider = Connection.ProviderFactory;
+ cxInfo.DatabaseInfo.EncryptCustomCxString = Connection.EncryptConnectionString;
+ cxInfo.DatabaseInfo.DbVersion = Connection.DbVersion;
+ cxInfo.DatabaseInfo.Database = Connection.DatabaseName;
+ cxInfo.DatabaseInfo.Server = Connection.Server;
+
+ cxInfo.DynamicSchemaOptions.NoPluralization = !Scaffold.Pluralize;
+ cxInfo.DynamicSchemaOptions.NoCapitalization = !Scaffold.Capitalize;
+
+ cxInfo.DisplayName = Connection.DisplayName;
+ cxInfo.IsProduction = Connection.IsProduction;
+ cxInfo.Persist = Connection.Persistent;
+ cxInfo.AppConfigPath = StaticContext.ConfigurationPath;
+ cxInfo.CustomTypeInfo.CustomTypeName = StaticContext.ContextTypeName;
+ cxInfo.CustomTypeInfo.CustomAssemblyPath = StaticContext.ContextAssemblyPath;
+
+ var json = JsonSerializer.Serialize(this, _jsonOptions);
+ SetString(cxInfo, SETTINGS_NODE, json);
+ }
+
+ ///
+ /// Legacy options migration support.
+ ///
+ private static class Legacy
+ {
+ // list item separators for legacy options
+ private static readonly char[] _listSeparators = new[]{ ',', ';' };
+
+ // legacy options
+ private const string ProviderName = "providerName";
+ private const string ProviderPath = "providerPath";
+ private const string ConnectionString = "connectionString";
+ private const string ExcludeRoutines = "excludeRoutines";
+ private const string ExcludeFKs = "excludeFKs";
+ private const string IncludeSchemas = "includeSchemas";
+ private const string ExcludeSchemas = "excludeSchemas";
+ private const string IncludeCatalogs = "includeCatalogs";
+ private const string ExcludeCatalogs = "excludeCatalogs";
+ private const string OptimizeJoins = "optimizeJoins";
+ private const string UseProviderSpecificTypes = "useProviderSpecificTypes";
+ private const string UseCustomFormatter = "useCustomFormatter";
+ private const string CommandTimeout = "commandTimeout";
+ private const string NormalizeNames = "normalizeNames";
+ private const string CustomConfiguration = "customConfiguration";
+
+ public static ConnectionSettings Load(IConnectionInfo cxInfo)
+ {
+ var settings = new ConnectionSettings();
+ settings.Connection = new();
+ settings.Schema = new();
+ settings.Scaffold = new();
+ settings.LinqToDB = new();
+ settings.StaticContext = new();
+
+ // 1. ProviderName migration
+
+ // old provider name option replaced with two options: database and database provider
+ settings.Connection.Provider = GetString(cxInfo, ProviderName);
+
+ // this native oracle provider was removed long time ago and not supported in v5 too
+ if (settings.Connection.Provider == PN.OracleNative)
+ settings.Connection.Provider = PN.OracleManaged;
+
+ // switch contains only provider names, used by pre-v5 driver
+ settings.Connection.Database = settings.Connection.Provider switch
+ {
+
+ PN.AccessOdbc => PN.Access,
+ PN.MySqlConnector => PN.MySql,
+ PN.SybaseManaged => PN.Sybase,
+ PN.SQLiteClassic => PN.SQLite,
+ PN.InformixDB2 => PN.Informix,
+ PN.SapHanaNative
+ or PN.SapHanaOdbc => PN.SapHana,
+ PN.OracleManaged => PN.Oracle,
+ // preserve same name
+ PN.Firebird
+ or PN.Access
+ or PN.PostgreSQL
+ or PN.DB2LUW
+ or PN.DB2zOS
+ or PN.SqlServer
+ //or DB2iSeriesProviderName.DB2
+ or PN.SqlCe => settings.Connection.Provider,
+ _ => null
+ };
+
+ // 2. IncludeSchemas, ExcludeSchemas, IncludeCatalogs and ExcludeCatalogs migration
+
+ // 2. convert comma/semicolon-separated strings with schemas/catalogs to list + flag
+ var strValue = GetString(cxInfo, ExcludeSchemas);
+ var schemas = strValue == null ? null : new HashSet(strValue.Split(_listSeparators, StringSplitOptions.RemoveEmptyEntries));
+ if (schemas != null && schemas.Count > 0)
+ settings.Schema.IncludeSchemas = false;
+ else
+ {
+ strValue = GetString(cxInfo, IncludeSchemas);
+ schemas = strValue == null ? null : new HashSet(strValue.Split(_listSeparators, StringSplitOptions.RemoveEmptyEntries));
+ if (schemas != null && schemas.Count > 0)
+ settings.Schema.IncludeSchemas = true;
+ }
+
+ settings.Schema.Schemas = schemas?.AsReadOnly();
+
+ strValue = GetString(cxInfo, ExcludeCatalogs);
+ var catalogs = strValue == null ? null : new HashSet(strValue.Split(_listSeparators, StringSplitOptions.RemoveEmptyEntries));
+ if (catalogs != null && catalogs.Count > 0)
+ settings.Schema.IncludeCatalogs = false;
+ else
+ {
+ strValue = GetString(cxInfo, IncludeCatalogs);
+ catalogs = strValue == null ? null : new HashSet(strValue.Split(_listSeparators, StringSplitOptions.RemoveEmptyEntries));
+ if (catalogs != null && catalogs.Count > 0)
+ settings.Schema.IncludeCatalogs = true;
+ }
+ settings.Schema.Catalogs = catalogs?.AsReadOnly();
+
+ // 3. ExcludeRoutines migration
+
+ settings.Schema.LoadAggregateFunctions
+ = settings.Schema.LoadScalarFunctions
+ = settings.Schema.LoadTableFunctions
+ = settings.Schema.LoadProcedures
+ = !GetBoolean(cxInfo, ExcludeRoutines, true).Value;
+
+ // 4. ExcludeFKs migration
+ settings.Schema.LoadForeignKeys = !GetBoolean(cxInfo, ExcludeFKs, false).Value;
+
+ // 5. ProviderPath migration
+ settings.Connection.ProviderPath = GetString(cxInfo, ProviderPath);
+
+ // 6. CommandTimeout migration
+ // note that in pre-v5 it was non-nullable option so it wasn't possible to use default db/provider timeout
+ settings.Connection.CommandTimeout = GetInt32(cxInfo, CommandTimeout);
+
+ // 7. ConnectionString migration
+ // note that in practice pre-v4 never stored connection in custom field and used CustomCxString as storage
+ settings.Connection.ConnectionString = GetString(cxInfo, ConnectionString);
+ if (!string.IsNullOrWhiteSpace(settings.Connection.ConnectionString))
+ cxInfo.DatabaseInfo.CustomCxString = settings.Connection.ConnectionString;
+
+ // 8. OptimizeJoins migration
+ settings.LinqToDB.OptimizeJoins = GetBoolean(cxInfo, OptimizeJoins, true).Value;
+
+ // 9. UseProviderSpecificTypes migration
+ settings.Scaffold.UseProviderTypes = GetBoolean(cxInfo, UseProviderSpecificTypes, false).Value;
+
+ // 10. CustomConfiguration migration
+ settings.StaticContext.ConfigurationName = GetString(cxInfo, CustomConfiguration);
+
+ // ignored options:
+ // UseCustomFormatter - removed in v5
+ // NormalizeNames - not used in pre-v5 and v5 (never used?)
+
+ return settings;
+ }
+ }
+
+ [return: NotNullIfNotNull(nameof(defaultValue))]
+ private static int? GetInt32(IConnectionInfo cxInfo, XName name, int? defaultValue = null)
+ {
+ var strValue = GetString(cxInfo, name);
+
+ if (strValue != null && int.TryParse(strValue, NumberStyles.None, CultureInfo.InvariantCulture, out var intValue))
+ return intValue;
+
+ return defaultValue;
+ }
+
+ [return: NotNullIfNotNull(nameof(defaultValue))]
+ private static bool? GetBoolean(IConnectionInfo cxInfo, XName name, bool? defaultValue = null)
+ {
+ var strValue = GetString(cxInfo, name);
+ return strValue == "true" ? true : strValue == "false" ? false : defaultValue;
+ }
+
+ [return: NotNullIfNotNull(nameof(defaultValue))]
+ private static string? GetString(IConnectionInfo cxInfo, XName name, string? defaultValue = null) => cxInfo.DriverData.Element(name)?.Value ?? defaultValue;
+
+ private static void SetString(IConnectionInfo cxInfo, XName name, string? value)
+ {
+ if (value != null)
+ cxInfo.DriverData.SetElementValue(name, value);
+ else
+ cxInfo.DriverData.Element(name)?.Remove();
+ }
+ #endregion
+
+ public ConnectionOptions Connection { get; set; } = null!;
+ public SchemaOptions Schema { get; set; } = null!;
+ public ScaffoldOptions Scaffold { get; set; } = null!;
+ public LinqToDbOptions LinqToDB { get; set; } = null!;
+ public StaticContextOptions StaticContext { get; set; } = null!;
+
+ public sealed class ConnectionOptions
+ {
+ ///
+ /// Database identifier. Usually generic name from .
+ ///
+ public string? Database { get; set; }
+
+ ///
+ /// Database provider identifier. Specific name from .
+ ///
+ public string? Provider { get; set; }
+
+ ///
+ /// Database provider assembly path.
+ ///
+ [JsonIgnore]
+ public string? ProviderPath
+ {
+ get => IntPtr.Size == 4 ? ProviderPathx86 ?? ProviderPathx64 : ProviderPathx64 ?? ProviderPathx86;
+ set
+ {
+ if (IntPtr.Size == 4)
+ ProviderPathx86 = value;
+ else
+ ProviderPathx64 = value;
+ }
+ }
+
+ ///
+ /// Database provider assembly path.
+ ///
+ public string? ProviderPathx86 { get; set; }
+
+ ///
+ /// Database provider assembly path.
+ ///
+ public string? ProviderPathx64 { get; set; }
+
+ ///
+ /// Command timeout. null for provider/database default timeout.
+ ///
+ public int? CommandTimeout { get; set; }
+
+ ///
+ /// Database provider name for secondary schema connection.
+ ///
+ public string? SecondaryProvider { get; set; }
+
+ ///
+ /// Secondary schema connection string.
+ ///
+ public string? SecondaryConnectionString { get; set; }
+
+ ///
+ /// User-defined connection name.
+ /// Stored in .
+ ///
+ [JsonIgnore]
+ public string? DisplayName { get; set; }
+
+ ///
+ /// Marks connected database as containing production data.
+ /// Stored in .
+ ///
+ [JsonIgnore]
+ public bool IsProduction { get; set; }
+
+ ///
+ /// Marks connection as persistent (saved before restarts).
+ /// Stored in .
+ ///
+ [JsonIgnore]
+ public bool Persistent { get; set; }
+
+ ///
+ /// Connection string. Stored in .
+ ///
+ [JsonIgnore]
+ public string? ConnectionString { get; set; }
+
+ ///
+ /// Stored in .
+ ///
+ [JsonIgnore]
+ public string? ProviderFactory { get; set; }
+
+ ///
+ /// Database information ().
+ /// Stored in .
+ ///
+ [JsonIgnore]
+ public string? Server { get; set; }
+
+ ///
+ /// Database information ().
+ /// Stored in .
+ ///
+ [JsonIgnore]
+ public string? DatabaseName { get; set; }
+
+ ///
+ /// Database information ().
+ /// Stored in .
+ ///
+ [JsonIgnore]
+ public string? DbVersion { get; set; }
+
+ ///
+ /// Instructs LINQPad to encrypt value.
+ /// Also instruct us to encrypt value.
+ /// Stored in .
+ ///
+ [JsonIgnore]
+ public bool EncryptConnectionString { get; set; }
+ }
+
+ public sealed class SchemaOptions
+ {
+ ///
+ /// Include/exclude schemas, specified by option.
+ ///
+ public bool IncludeSchemas { get; set; }
+
+ ///
+ /// List of schemas to include/exclude (defined by option).
+ ///
+ public IReadOnlySet? Schemas { get; set; }
+
+ ///
+ /// Include/exclude catalogs, specified by option.
+ ///
+ public bool IncludeCatalogs { get; set; }
+
+ ///
+ /// List of catalogs to include/exclude (defined by option).
+ ///
+ public IReadOnlySet? Catalogs { get; set; }
+
+ ///
+ /// Populate stored procedures.
+ ///
+ public bool LoadProcedures { get; set; }
+
+ ///
+ /// Populate table functions.
+ ///
+ public bool LoadTableFunctions { get; set; }
+
+ ///
+ /// Populate scalar functions.
+ ///
+ public bool LoadScalarFunctions { get; set; }
+
+ ///
+ /// Populate aggregate functions.
+ ///
+ public bool LoadAggregateFunctions { get; set; }
+
+ ///
+ /// Populate foreign keys.
+ ///
+ public bool LoadForeignKeys { get; set; }
+ }
+
+ public sealed class ScaffoldOptions
+ {
+ ///
+ /// Use provider data types.
+ ///
+ public bool UseProviderTypes { get; set; }
+
+ ///
+ /// Map FixedString(X) to for ClickHouse.
+ ///
+ public bool ClickHouseFixedStringAsString { get; set; }
+
+ ///
+ /// Enables pluralization context table property name and collection-type association property name.
+ /// Stored in .
+ ///
+ [JsonIgnore]
+ public bool Pluralize { get; set; }
+
+ ///
+ /// Enables capitalization of table column properties.
+ /// Stored in .
+ ///
+ [JsonIgnore]
+ public bool Capitalize { get; set; }
+ }
+
+ public sealed class LinqToDbOptions
+ {
+ ///
+ /// Value for Linq To DB setting.
+ ///
+ public bool OptimizeJoins { get; set; }
+ }
+
+ public sealed class StaticContextOptions
+ {
+ ///
+ /// Name of custom configuration (connection string name), passed to context constructor.
+ ///
+ public string? ConfigurationName { get; set; }
+
+ ///
+ /// Path to custom configuration file.
+ /// For LINQPad 5 it should be in app.config format, for .NET Core versions - in appsettings.json format.
+ /// Stored in .
+ ///
+ [JsonIgnore] // strored in linqpad storage
+ public string? ConfigurationPath { get; set; }
+
+#if NETFRAMEWORK
+ ///
+ /// Path to appsettings.json configuration file for LINQPad 5.
+ /// We cannot store it in as LINQPad will try to use
+ /// it as app.config file and fail.
+ ///
+ public string? LocalConfigurationPath { get; set; }
+#endif
+
+ ///
+ /// Full name of data context class (namespace + class name) in custom context assembly.
+ /// Stored in .
+ ///
+ [JsonIgnore]
+ public string? ContextTypeName { get; set; }
+
+ ///
+ /// Full path to custom context assembly.
+ /// Stored in .
+ ///
+ [JsonIgnore]
+ public string? ContextAssemblyPath { get; set; }
+ }
+}
diff --git a/Source/Configuration/CustomSerializers/IReadOnlySetConverter.cs b/Source/Configuration/CustomSerializers/IReadOnlySetConverter.cs
new file mode 100644
index 0000000..0aa590f
--- /dev/null
+++ b/Source/Configuration/CustomSerializers/IReadOnlySetConverter.cs
@@ -0,0 +1,75 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace LinqToDB.LINQPad.Json;
+
+internal sealed class IReadOnlySetConverter : JsonConverter>
+{
+ private readonly JsonConverter _elementConverter;
+ private readonly Type _elementType = typeof(T);
+
+ private static IReadOnlySetConverter? _instance;
+
+ public static readonly JsonConverterFactory Factory = new IReadOnlySetConverterFactory();
+
+ private static JsonConverter GetInstance(JsonConverter elementConverter)
+ {
+ return _instance ??= new IReadOnlySetConverter(elementConverter);
+ }
+
+ private IReadOnlySetConverter(JsonConverter elementConverter)
+ {
+ _elementConverter = elementConverter;
+ }
+
+ public override IReadOnlySet Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ var hashSet = new HashSet();
+
+ while (reader.Read())
+ {
+ if (reader.TokenType == JsonTokenType.EndArray)
+ {
+ break;
+ }
+
+ var item = _elementConverter.Read(ref reader, _elementType, options);
+ hashSet.Add(item!);
+ }
+
+ return hashSet.AsReadOnly();
+ }
+
+ public override void Write(Utf8JsonWriter writer, IReadOnlySet value, JsonSerializerOptions options)
+ {
+ writer.WriteStartArray();
+ foreach (var item in value)
+ {
+ _elementConverter.Write(writer, item, options);
+ }
+ writer.WriteEndArray();
+ }
+
+ private sealed class IReadOnlySetConverterFactory : JsonConverterFactory
+ {
+ public override bool CanConvert(Type typeToConvert)
+ {
+ if (!typeToConvert.IsGenericType)
+ {
+ return false;
+ }
+
+ return typeToConvert.GetGenericTypeDefinition() == typeof(IReadOnlySet<>);
+ }
+
+ public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
+ {
+ var elementType = typeToConvert.GetGenericArguments()[0];
+
+ var converterType = typeof(IReadOnlySetConverter<>).MakeGenericType(elementType);
+ return (JsonConverter)converterType
+ .GetMethod(nameof(IReadOnlySetConverter.GetInstance), BindingFlags.NonPublic | BindingFlags.Static)!
+ .Invoke(null, new[] { options.GetConverter(elementType) })!;
+ }
+ }
+}
diff --git a/Source/ConnectionDialog.xaml b/Source/ConnectionDialog.xaml
deleted file mode 100644
index ce2850b..0000000
--- a/Source/ConnectionDialog.xaml
+++ /dev/null
@@ -1,206 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Source/ConnectionDialog.xaml.cs b/Source/ConnectionDialog.xaml.cs
deleted file mode 100644
index cdbad8e..0000000
--- a/Source/ConnectionDialog.xaml.cs
+++ /dev/null
@@ -1,295 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Configuration;
-using System.IO;
-using System.Linq;
-using System.Windows;
-using System.Windows.Input;
-using LINQPad.Extensibility.DataContext;
-using LINQPad.Extensibility.DataContext.UI;
-using LinqToDB.Data;
-
-namespace LinqToDB.LINQPad
-{
- public partial class ConnectionDialog
- {
- public ConnectionDialog()
- {
- InitializeComponent();
- }
-
- private ConnectionViewModel? Model => DataContext as ConnectionViewModel;
-
- ConnectionDialog(ConnectionViewModel model)
- : this()
- {
- DataContext = model;
-
- ((INotifyPropertyChanged)model).PropertyChanged += (sender, args) =>
- {
- if (args.PropertyName == nameof(ConnectionViewModel.IncludeRoutines) && model.IncludeRoutines)
- {
- MessageBox.Show(this,
- "Including Stored Procedures may be dangerous in production if the selected database driver does not support CommandBehavior.SchemaOnly option or procedure is not safe for CommandBehavior.SchemaOnly execution mode.",
- "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
- }
-
- if (Model != null && args.PropertyName == nameof(ConnectionViewModel.ProviderPathLabel))
- Model.ProviderPath = GetDefaultProviderPath();
- };
- }
-
- Func? _connectionTester;
-
- public static bool Show(ConnectionViewModel model, Func? connectionTester)
- {
- return new ConnectionDialog(model) { _connectionTester = connectionTester }.ShowDialog() == true;
- }
-
- void TestClick(object sender, RoutedEventArgs e)
- {
- if (_connectionTester != null)
- {
- Exception? ex;
-
- try
- {
- Mouse.OverrideCursor = Cursors.Wait;
- ex = _connectionTester(Model);
- }
- finally
- {
- Mouse.OverrideCursor = null;
- }
-
-
- if (ex == null)
- {
- MessageBox.Show(this, "Successful!", "Connection Test", MessageBoxButton.OK, MessageBoxImage.Information);
- }
- else
- {
- MessageBox.Show(this, ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
- }
- }
- }
-
- void OKClick(object sender, RoutedEventArgs e)
- {
- Exception? ex;
-
- try
- {
- Mouse.OverrideCursor = Cursors.Wait;
- ex = _connectionTester?.Invoke(Model);
- }
- finally
- {
- Mouse.OverrideCursor = null;
- }
-
- if (ex == null)
- {
- DialogResult = true;
- }
- else
- {
- if (MessageBox.Show(
- this,
- $"{ex.Message}\r\n\r\nDo you want to continue?",
- "Error",
- MessageBoxButton.YesNo,
- MessageBoxImage.Stop) == MessageBoxResult.Yes)
- {
- DialogResult = true;
- }
- }
- }
-
- void BrowseAssembly(object sender, RoutedEventArgs e)
- {
- if (Model == null)
- return;
-
- var dialog = new Microsoft.Win32.OpenFileDialog
- {
- Title = "Choose custom assembly",
- DefaultExt = ".dll",
- FileName = Model.CustomAssemblyPath,
- };
-
- if (dialog.ShowDialog() == true)
- Model.CustomAssemblyPath = dialog.FileName;
- }
-
- string? GetDefaultProviderPath()
- {
- if (Model == null)
- return null;
-
- return Model.SelectedProvider?.Name switch
- {
- ProviderName.SqlCe => IntPtr.Size == 4
- ? @"c:\Program Files (x86)\Microsoft SQL Server Compact Edition\v4.0\Private\System.Data.SqlServerCe.dll"
- : @"c:\Program Files\Microsoft SQL Server Compact Edition\v4.0\Private\System.Data.SqlServerCe.dll",
- ProviderName.SapHanaNative => @"c:\Program Files (x86)\sap\hdbclient\dotnetcore\v2.1\Sap.Data.Hana.Core.v2.1.dll",
- _ => null
- };
- }
-
- void BrowseProvider(object sender, RoutedEventArgs e)
- {
- if (Model == null)
- return;
-
- var defaultPath = GetDefaultProviderPath();
- if (defaultPath == null)
- return;
-
- var fileName = Path.GetFileName(defaultPath);
-
- var dialog = new Microsoft.Win32.OpenFileDialog
- {
- Title = $"Select {fileName}",
- DefaultExt = ".dll",
- FileName = Model.ProviderPath,
- InitialDirectory = Path.GetDirectoryName(Model.ProviderPath ?? defaultPath),
- Filter = $"{fileName}|{fileName}",
- };
-
- if (dialog.ShowDialog() == true)
- Model.ProviderPath = dialog.FileName;
- }
-
- void ChooseType(object sender, RoutedEventArgs e)
- {
- if (Model != null)
- {
- var oldCursor = Cursor;
-
- try
- {
- Mouse.OverrideCursor = Cursors.Wait;
-
- Model.CustomAssemblyPath = Model.CustomAssemblyPath!.Trim();
-
- var assembly = DataContextDriver.LoadAssemblySafely(Model.CustomAssemblyPath);
- var customTypes = assembly.GetExportedTypes().Where(IsDataConnection).Select(t => t.FullName).Cast