diff --git a/FirstSteps/00-Index.ipynb b/FirstSteps/00-Index.ipynb
index b1edc17..505dcf7 100644
--- a/FirstSteps/00-Index.ipynb
+++ b/FirstSteps/00-Index.ipynb
@@ -23,6 +23,12 @@
"| [Practice Lists ]( 11-Practice_Lists.ipynb ) |\n",
"| [Functions and Procedures ]( 12-Functions_and_Procedures.ipynb ) |\n",
"| [Objects and Classes ]( 13-Objects_and_Classes.ipynb ) |\n",
+ "| [Methods and Members ]( 14-Methods_and_Members.ipynb ) |\n",
+ "| [Methods and Exceptions ]( 15-Methods_and_Exceptions.ipynb ) |\n",
+ "| [Files ]( 16-Files.dib ) |\n",
+ "| [ByteStream ]( 17-ByteStream.dib ) |\n",
+ "| [TextStream ]( 18-TextStream.dib ) |\n",
+ "| [BankAccount and CSV File ]( 19-BankAccount_and_CSVFile.dib ) |\n",
"\n",
"\n",
" \n"
diff --git a/FirstSteps/17-ByteStream.dib b/FirstSteps/17-ByteStream.dib
index cdaadac..6567459 100644
--- a/FirstSteps/17-ByteStream.dib
+++ b/FirstSteps/17-ByteStream.dib
@@ -104,11 +104,11 @@ The first time you run it, you won't see any change, but if you try a second tim
#!markdown
-## Variable lifetime: `using` statement
+## Variable lifetime: `BEGIN USING` instruction
-The `using` instruction provides a convenient syntax that ensures the correct use of objects by specifying their lifetime.
+The `BEGIN USING` instruction provides a convenient syntax that ensures the correct use of objects by specifying their lifetime.
-> The code below performs the same function as the previous one, but specifies when to _create_ and _delete_ the flow variable.
+> The code below performs the same function as the previous one, but specifies when to _create_ and _delete_ the stream variable.
> The _deletion_ of the stream causes the stream to be closed, as the `FileStream` class is written that way.
#!xsharp
diff --git a/FirstSteps/19-BankAccount_and_CSVFile.dib b/FirstSteps/19-BankAccount_and_CSVFile.dib
index 079c305..b7e3749 100644
--- a/FirstSteps/19-BankAccount_and_CSVFile.dib
+++ b/FirstSteps/19-BankAccount_and_CSVFile.dib
@@ -157,3 +157,10 @@ FOR VAR i:= 1 TO list:Length
var am := string.Format("{0,15:N2}", list[i]:Amount)
? dt + am + " " + list[i]:Notes
NEXT
+
+#!markdown
+
+Great !!
+
+You can now read and write files.
+Now, have a look at the [Simple SQL Notebooks](../Simple%20SQL/01-SQLite%20Library.ipynb) to use X# to read and write data on a SQL Server.
diff --git a/README.md b/README.md
index 998933d..731f3c1 100644
--- a/README.md
+++ b/README.md
@@ -42,7 +42,7 @@ I hope you will enjoy your journey with **XSharp Interactive** !
| Notebooks | Description| X# Dialect | Level |
| -------- |--- | ------- |---|
| [First Steps](./FirstSteps/00-Index.ipynb) | Start learning X# language | Core | Beginner
-| [Simple SQL](./WorkInProgress.ipynb) | Using SQLite in your X# Application, and change for MariaDB, PostgreSQL, ... | Core | Intermediate
+| [Simple SQL](./SimpleSQL/00-Index.dib) | Using SQLite in your X# Application, and change for MariaDB, PostgreSQL, ... | Core | Intermediate
| [First Steps VFP](./WorkInProgress.ipynb) | Start X# with your VFP background | VFP | Beginner
diff --git a/SimpleSQL/00-Index.dib b/SimpleSQL/00-Index.dib
new file mode 100644
index 0000000..03f8773
--- /dev/null
+++ b/SimpleSQL/00-Index.dib
@@ -0,0 +1,22 @@
+#!meta
+
+{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"languageName":"csharp","name":"csharp"}]}}
+
+#!markdown
+
+# Index
+
+| Lesson |
+|-----|
+| [Index ]( 00-Index.dib ) |
+| [SQLite Library ]( 01-SQLite_Library.ipynb ) |
+| [Connectionv vvvv ]( 02-Connection.ipynb ) |
+| [Command NonQuery ]( 03-Command_NonQuery.dib ) |
+| [Command Reader ]( 04-Command_Reader.dib ) |
+| [Command Scalar ]( 05-Command_Scalar.dib ) |
+| [DataTable ADO.NET ]( 06-DataTable_ADO.Net.dib ) |
+| [PostgreSQL ]( 07-PostgreSQL.dib ) |
+
+
+
+
diff --git a/Simple SQL/01-SQLite Library.ipynb b/SimpleSQL/01-SQLite_Library.ipynb
similarity index 99%
rename from Simple SQL/01-SQLite Library.ipynb
rename to SimpleSQL/01-SQLite_Library.ipynb
index 90ec23d..6c81abe 100644
--- a/Simple SQL/01-SQLite Library.ipynb
+++ b/SimpleSQL/01-SQLite_Library.ipynb
@@ -40,7 +40,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 1,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
diff --git a/Simple SQL/02-Connection.ipynb b/SimpleSQL/02-Connection.ipynb
similarity index 61%
rename from Simple SQL/02-Connection.ipynb
rename to SimpleSQL/02-Connection.ipynb
index 97f3597..a6e3d66 100644
--- a/Simple SQL/02-Connection.ipynb
+++ b/SimpleSQL/02-Connection.ipynb
@@ -21,7 +21,7 @@
"outputs": [],
"source": [
"// <-= Press on the Arrow to run Me\n",
- "#r \"nuget: Microsoft.Data.Sqlite, 8.0.5\"\n",
+ "#r \"nuget:Microsoft.Data.Sqlite, 8.0.5\"\n",
"#r \"nuget:XSharpInteractive\""
]
},
@@ -31,7 +31,7 @@
"source": [
"\n",
"Warning !! Dirty Hack !!
\n",
- "As XSharp scripting engine doesn't support Nuget package load for now, we will have to force the load of the .DLL itself.
\n",
+ "As XSharp scripting engine doesn't support Nuget package load for now, we will have to force the load of the .DLL itself in the XSharp Interactive memory.
\n",
"When you load a package, it is placed in your profile folder in the path :
\n",
"\n",
"\"C:\\Users\\your login name\\.nuget\\packages\\microsoft.data.sqlite.core\\8.0.5\\lib\\net8.0\\Microsoft.Data.Sqlite.dll\"\n",
@@ -130,12 +130,15 @@
"The `System.IO` namespace contains a lot of tools in file management, and `Delete` is one of them.\n",
"\n",
"> Try to delete the file. \n",
- "> On the first run of this Notebook, you shouldn't have a DB file, but if you try it several times you might need it."
+ "> On the first run of this Notebook, you shouldn't have a DB file, but if you try it several times you might need it.\n",
+ "\n",
+ "*Be aware that, even if you close it, the Notebook might keep a handle on it, and deletion may not be possible \n",
+ "If so, the only solution is to close and reopen the current Notebook.*"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 14,
"metadata": {
"dotnet_interactive": {
"language": "xsharp"
@@ -146,38 +149,28 @@
},
"outputs": [],
"source": [
- "//\n",
+ "// Check if the File Exist\n",
"IF (File.Exists(file))\n",
+ " // If so, remove the .db file\n",
+ "\n",
" File.Delete(file)\n",
" ? i\"File {file} deleted.\"\n",
"ENDIF\n",
"//"
]
},
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "dotnet_interactive": {
- "language": "xsharp"
- },
- "polyglot_notebook": {
- "kernelName": "xsharp"
- }
- },
- "outputs": [],
- "source": []
- },
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "###"
+ "## Open and Close\n",
+ "\n",
+ "In order to interact with our SQL engine, we will have to Open the connection and Close it when no more needed. "
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 13,
"metadata": {
"dotnet_interactive": {
"language": "xsharp"
@@ -189,81 +182,21 @@
"outputs": [],
"source": [
"var connection := SqliteConnection{connectionString:ToString()}\n",
- " ? \"Opening...\"\n",
- " connection:Open()\n",
- " ? connection:State\n",
+ "? \"Opening...\"\n",
+ "connection:Open()\n",
+ "? connection:State\n",
"connection:Close()\n",
"? connection:State"
]
},
{
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "dotnet_interactive": {
- "language": "xsharp"
- },
- "polyglot_notebook": {
- "kernelName": "xsharp"
- }
- },
- "outputs": [],
- "source": [
- "BEGIN USING var shortConnection := SqliteConnection{connectionString:ToString()}\n",
- " ? \"Opening...\"\n",
- " shortConnection:Open()\n",
- " ? shortConnection:State\n",
- "END USING\n",
- "? shortConnection"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "dotnet_interactive": {
- "language": "xsharp"
- },
- "polyglot_notebook": {
- "kernelName": "xsharp"
- }
- },
- "outputs": [],
- "source": [
- "? connection:State"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "dotnet_interactive": {
- "language": "xsharp"
- },
- "polyglot_notebook": {
- "kernelName": "xsharp"
- }
- },
- "outputs": [],
+ "cell_type": "markdown",
+ "metadata": {},
"source": [
- "var name := \"Fabrice\" \n",
- "? i\"Welcome {name}.\""
+ "But it's also a good practice to release the Connection object at the end : To do so, we will use the `BEGIN USING` instruction. \n",
+ "And what is nice here, is that at the `END USING`, not only the object will be released, but the connection will be Closed first !\n"
]
},
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "dotnet_interactive": {
- "language": "xsharp"
- },
- "polyglot_notebook": {
- "kernelName": "xsharp"
- }
- },
- "outputs": [],
- "source": []
- },
{
"cell_type": "code",
"execution_count": null,
@@ -277,64 +210,13 @@
},
"outputs": [],
"source": [
- "BEGIN USING var connection := SqliteConnection{connectionString:ToString()}\n",
+ "BEGIN USING var shortconnection := SqliteConnection{connectionString:ToString()}\n",
" ? \"Opening...\"\n",
- " connection:Open()\n",
- " ? \"Create DataBase...\"\n",
- " BEGIN USING var command := connection:CreateCommand()\n",
- " command:CommandText = ;\n",
- " \" CREATE TABLE user (\" +;\n",
- " \" id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\"+;\n",
- " \" name TEXT NOT NULL\"+;\n",
- " \" );\"+;\n",
- " \" INSERT INTO user\"+;\n",
- " \" VALUES (1, 'Diana'),\"+;\n",
- " \" (2, 'Bruce'),\"+;\n",
- " \" (3, 'Peter'),\"+;\n",
- " \" (4, 'Natasha');\"\n",
- " command:ExecuteNonQuery()\n",
- " END USING\n",
+ " shortconnection:Open()\n",
+ " ? shortconnection:State\n",
"END USING\n",
- "\n",
- "/*\n",
- "This is a Work-in-progress\n",
- "I leave it here to not forget\n",
- "\n",
- "\n",
- "#include \"Xsharpdefs.xh\"\n",
- "\n",
- "LOCAL myText AS STRING\n",
- "TEXT TO myText\n",
- "hello\n",
- "ENDTEXT\n",
- "? myText\n",
- "*/"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "dotnet_interactive": {
- "language": "xsharp"
- },
- "polyglot_notebook": {
- "kernelName": "xsharp"
- }
- },
- "outputs": [],
- "source": [
- "BEGIN USING var connection := SqliteConnection{connectionString:ToString()}\n",
- " BEGIN USING var command := connection:CreateCommand()\n",
- " // Add yourself to the Table\n",
- " ? \"Adding user...\"\n",
- " command:CommandText = ;\n",
- " \" INSERT INTO user (name)\"+;\n",
- " \" VALUES (@name)\"\n",
- " command:Parameters:AddWithValue(\"@name\", name)\n",
- " command:ExecuteNonQuery()\n",
- " END USING\n",
- "END USING"
+ "// Try to remove the comment here : The connection var doesn't exist\n",
+ "//? shortconnection"
]
}
],
diff --git a/SimpleSQL/03-Command_NonQuery.dib b/SimpleSQL/03-Command_NonQuery.dib
new file mode 100644
index 0000000..3d6bf23
--- /dev/null
+++ b/SimpleSQL/03-Command_NonQuery.dib
@@ -0,0 +1,177 @@
+#!meta
+
+{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"languageName":"csharp","name":"csharp"},{"aliases":[],"languageName":"X#","name":"xsharp"}]}}
+
+#!markdown
+
+# Load the *XSharp Language kernel*, and *SQLite package*
+
+#!csharp
+
+// <-= Press on the Arrow to run Me
+#r "nuget:Microsoft.Data.Sqlite, 8.0.5"
+#r "nuget:XSharpInteractive"
+
+#!xsharp
+
+// Load the DLL in the XSharpInteractive context
+#r "C:\Users\fabri\.nuget\packages\microsoft.data.sqlite.core\8.0.5\lib\net8.0\Microsoft.Data.Sqlite.dll"
+
+#!markdown
+
+# Command
+
+First, we will need a table. To do so, we will send a **Command** to the SQLite engine.
+
+There are different types of **Command** : *Query* or *NonQuery*.
+- *NonQuery* are typically used for SQL statements without results (e.g. `UPDATE`, `INSERT`, ...) (In fact it returns the count of rows effected by the Query.)
+- *Query* are used for any result set with multiple rows/columns. We will get the result through a ***Reader*** (e.g. `SELECT elt1,elt2 FROM table1`)
+- *Scalar* are also a kind of *Query* and are used when your Query returns a single value. If it returns more than one, then the result is the first column of the first row.
+
+# NonQuery
+
+## Table Creation
+
+Based on the previous paragraph, we can guess we will have to execute a *NonQuery* **Command**.
+
+In the code below, we are creating a table called user, with 2 columns/fields.
+- **id** is an `Integer` and can`not` be `null`. It is a `Primary Key`, so unique and used as an index. Its value will be `AutoIncrement`ed when you add a record into the table.
+- **name** is a `Text` and can`not` be `null`.
+
+> Run the following code
+> *You can only run it **once** as : The File will exist, and so the Table*
+> If you want to run again, you will have to delete the **Test.db** file, and you may need to close the Notebook to do it.
+
+#!xsharp
+
+using System.IO
+using Microsoft.Data.Sqlite
+
+var connectionString := SqliteConnectionStringBuilder{}
+connectionString:DataSource := "Test.db"
+BEGIN USING var connection := SqliteConnection{connectionString:ToString()}
+ ? "Opening..."
+ connection:Open()
+ ? "Create DataBase..."
+ BEGIN USING var command := connection:CreateCommand()
+ command:CommandText := ;
+ " CREATE TABLE user (" +;
+ " id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"+;
+ " name TEXT NOT NULL"+;
+ " );"+;
+ " INSERT INTO user"+;
+ " VALUES (1, 'Diana'),"+;
+ " (2, 'Bruce'),"+;
+ " (3, 'Peter'),"+;
+ " (4, 'Natasha');"
+ command:ExecuteNonQuery()
+ END USING
+END USING
+
+#!markdown
+
+If you look closely to the pevious code, you can notice that we have created a BIG string with two SQL commands :
+- one to CREATE the table
+- one to INSERT records
+
+Now that the table exists, you may want to add some records.
+
+#!markdown
+
+# INSERT record
+
+To ease the writing, you can use an XSharp instruction who's form is `TEXT TO ... ENDTEXT`.
+To use it, you will need to `#include` an external definition file, here `XSharpDefs.xh`.
+This allows you to type a text in its *natural* form (with or without return/linefeed) and put it into a String \
+
+> Copy the following SQL INSERT command.
+
+ INSERT INTO user (name)
+ VALUES ('Clark'),
+ ('Barry'),
+ ('Lois'),
+ ('Alan')
+
+#!xsharp
+
+using System.IO
+using Microsoft.Data.Sqlite
+#include "Xsharpdefs.xh"
+
+var connectionString := SqliteConnectionStringBuilder{}
+connectionString:DataSource := "Test.db"
+BEGIN USING var connection := SqliteConnection{connectionString:ToString()}
+ connection:Open()
+ //
+ LOCAL myCommand AS STRING
+ TEXT TO myCommand
+ // Paste Here
+ ENDTEXT
+ //
+ BEGIN USING var command := connection:CreateCommand()
+ command:CommandText := myCommand
+ command:ExecuteNonQuery()
+ END USING
+END USING
+
+#!markdown
+
+# PARAMETERS
+
+A **Command** represents an SQL statement to execute against a data source, the one used by the **Connection**.
+
+In the previous sample, the list of user to add is constant, but you may have to enter a value that is the result of an operation or that the user has entered : This is where we will parameters.
+
+When you look at the INSERT command, it is organized this way :
+- **INSERT INTO** is the command itself
+- **user** is the table name
+- **( name )** is the list of field we will provide values for
+- **VALUES** indicate the beginning of the values
+- **('value1'), ('value2'),...** is the list of values
+
+You may want to write your personnal values this way :
+>
+ LOCAL myName AS STRING
+ myName := "Fabrice"
+ var commandText := "INSERT INTO user (name) VALUES ('" + nyName + "')"
+
+
+You should NOT do that !!!
+
+The risk is to have a **SQL Injection**. Adding strings to a SQL Command is a way to put some unwanted code into your command, and use your credentials when executing the Query.
+
+This is (one of the reason) why we will use parameters.
+
+In order to use parameters, you must create the **SQL Command** and put some *placeholders* starting with `@` where you want to have the values. Then use `AddWithValue` method to specify the *real* value.
+>
+ INSERT INTO ( , ) VALUES (@placeholder1, @placeholder2)
+
+> Copy the following code in the block, and test
+>
+ LOCAL myName AS STRING
+ myName := // Write your name here
+ ? "Adding user..."
+ command:CommandText = ;
+ " INSERT INTO user (name)"+;
+ " VALUES (@name)"
+ //
+ command:Parameters:AddWithValue("@name", myName)
+ command:ExecuteNonQuery()
+
+Modify the code to check another userId.
+
+#!xsharp
+
+using System.IO
+using Microsoft.Data.Sqlite
+#include "Xsharpdefs.xh"
+
+var connectionString := SqliteConnectionStringBuilder{}
+connectionString:DataSource := "Test.db"
+BEGIN USING var connection := SqliteConnection{connectionString:ToString()}
+ connection:Open()
+ //
+ BEGIN USING var command := connection:CreateCommand()
+ // Paste here
+ END USING
+END USING
diff --git a/SimpleSQL/04-Command_Reader.dib b/SimpleSQL/04-Command_Reader.dib
new file mode 100644
index 0000000..93894f9
--- /dev/null
+++ b/SimpleSQL/04-Command_Reader.dib
@@ -0,0 +1,120 @@
+#!meta
+
+{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"languageName":"csharp","name":"csharp"},{"aliases":[],"languageName":"X#","name":"xsharp"}]}}
+
+#!markdown
+
+# Load the *XSharp Language kernel*, and *SQLite package*
+
+#!csharp
+
+// <-= Press on the Arrow to run Me
+#r "nuget:Microsoft.Data.Sqlite, 8.0.5"
+#r "nuget:XSharpInteractive"
+
+#!xsharp
+
+// Load the DLL in the XSharpInteractive context
+#r "C:\Users\fabri\.nuget\packages\microsoft.data.sqlite.core\8.0.5\lib\net8.0\Microsoft.Data.Sqlite.dll"
+
+#!markdown
+
+# Command
+
+# Query : Use of Reader
+
+## SELECT
+
+The SQL SELECT statement returns a result set of rows, from one or more tables.
+
+To retrieve all the columns of the `user` table, you can write :
+>
+ SELECT id,name FROM user
+or
+>
+ SELECT * FROM user
+
+
+The SELECT statement has many following syntax:
+- **SELECT \** is the list of columns to be returned by the query.
+- **AS** is an option, and provides an alias for each column or expression in the SELECT list.
+- **FROM** specifies from which table to get the data.
+- **WHERE** specifies which rows to retrieve.
+- **GROUP** BY allow to aggregate values that are sharing a property.
+- **ORDER BY** specifies how to sort the returned rows.
+
+Like in the previous Notebook, you will set your command in the `CommandText` property, and use the `ExecuteReader()` method. It will return a [SqliteDataReader](https://learn.microsoft.com/en-us/dotnet/api/microsoft.data.sqlite.sqlitedatareader) object.
+To get the informations, you will have to `read()` the `SqliteReader`. Each time you do it you will advance in the ResultSet and the `SqliteReader`is filled with the data of the row.
+You can check the number of columns in the current row using the `FieldCount`Property, and use one of the `GetXXX()` methods indicating the column to read as a parameter.
+
+> Copy the following Query in the code block and test it.
+
+ command:CommandText := "SELECT * FROM user"
+ BEGIN USING var reader := command:ExecuteReader()
+ IF (reader:HasRows)
+ DO WHILE reader:Read()
+ var id := reader:GetInt32(0)
+ var firstName := reader:GetString(1)
+ ? i"{id} : {firstName}"
+ ENDDO
+ ELSE
+ ? "No User found."
+ ENDIF
+ END USING
+
+#!xsharp
+
+using System.IO
+using Microsoft.Data.Sqlite
+#include "Xsharpdefs.xh"
+
+var connectionString := SqliteConnectionStringBuilder{}
+connectionString:DataSource := "Test.db"
+BEGIN USING var connection := SqliteConnection{connectionString:ToString()}
+ connection:Open()
+ //
+ BEGIN USING var command := connection:CreateCommand()
+ // Paste here
+
+ END USING
+END USING
+
+#!markdown
+
+# PARAMETERS
+
+Don't forget that you can add some parameters to a Command.
+
+> Copy the following Query in the code block and test it.
+
+ VAR userId := 1
+ command:CommandText := "SELECT * FROM user WHERE id = @id"
+ command:Parameters:AddWithValue("@id", userId)
+ BEGIN USING var reader := command:ExecuteReader()
+ IF (reader:HasRows)
+ DO WHILE reader:Read()
+ var id := reader:GetInt32(0)
+ var firstName := reader:GetString(1)
+ ? i"{id} : {firstName}"
+ ENDDO
+ ELSE
+ ? "No User found."
+ ENDIF
+ END USING
+
+#!xsharp
+
+using System.IO
+using Microsoft.Data.Sqlite
+#include "Xsharpdefs.xh"
+
+var connectionString := SqliteConnectionStringBuilder{}
+connectionString:DataSource := "Test.db"
+BEGIN USING var connection := SqliteConnection{connectionString:ToString()}
+ connection:Open()
+ //
+ BEGIN USING var command := connection:CreateCommand()
+ // Paste here
+
+ END USING
+END USING
diff --git a/SimpleSQL/05-Command_Scalar.dib b/SimpleSQL/05-Command_Scalar.dib
new file mode 100644
index 0000000..0b520f0
--- /dev/null
+++ b/SimpleSQL/05-Command_Scalar.dib
@@ -0,0 +1,52 @@
+#!meta
+
+{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"languageName":"csharp","name":"csharp"},{"aliases":[],"languageName":"X#","name":"xsharp"}]}}
+
+#!markdown
+
+# Load the *XSharp Language kernel*, and *SQLite package*
+
+#!csharp
+
+// <-= Press on the Arrow to run Me
+#r "nuget:Microsoft.Data.Sqlite, 8.0.5"
+#r "nuget:XSharpInteractive"
+
+#!xsharp
+
+// Load the DLL in the XSharpInteractive context
+#r "C:\Users\fabri\.nuget\packages\microsoft.data.sqlite.core\8.0.5\lib\net8.0\Microsoft.Data.Sqlite.dll"
+
+#!markdown
+
+# Command
+
+# Scalar
+
+Based on the previous Notebook, you will set your command in the `CommandText` property, and use the `ExecuteScalar()` method.
+To method will return the first column of the first row of the resultSet, or null if there are not results.
+
+> Copy the following Query in the code block and test it.
+
+ command:CommandText := "SELECT Count() FROM user"
+ var result := command:ExecuteScalar()
+ IF result != NULL
+ ? i"{result}"
+ ENDIF
+
+#!xsharp
+
+using System.IO
+using Microsoft.Data.Sqlite
+#include "Xsharpdefs.xh"
+
+var connectionString := SqliteConnectionStringBuilder{}
+connectionString:DataSource := "Test.db"
+BEGIN USING var connection := SqliteConnection{connectionString:ToString()}
+ connection:Open()
+ //
+ BEGIN USING var command := connection:CreateCommand()
+ // Paste here
+
+ END USING
+END USING
diff --git a/SimpleSQL/06-DataTable_ADO.Net.dib b/SimpleSQL/06-DataTable_ADO.Net.dib
new file mode 100644
index 0000000..67a3329
--- /dev/null
+++ b/SimpleSQL/06-DataTable_ADO.Net.dib
@@ -0,0 +1,233 @@
+#!meta
+
+{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"languageName":"csharp","name":"csharp"},{"aliases":[],"languageName":"X#","name":"xsharp"}]}}
+
+#!markdown
+
+# Load the *XSharp Language kernel*
+
+#!csharp
+
+// <-= Press on the Arrow to run Me
+#r "nuget:XSharpInteractive"
+
+#!markdown
+
+In the next Notebooks, we will use some features that doesn't exists in `Microsoft.Data.Sqlite`, so we will have to load a new Nuget Package called `System.Data.SQLite`.
+
+#!csharp
+
+// <-= Press on the Arrow to run Me
+#r "nuget:System.Data.SQLite"
+
+#!markdown
+
+This package is mostly a wrapper around the standard Sqlite code, so the DLL will have to load into **XSharpInteractive** has a *strange* name.
+***Don't forget to adapt the path to your own configuration.***
+
+#!xsharp
+
+// Load the DLL in the XSharpInteractive context
+#r "C:\Users\fabri\.nuget\packages\stub.system.data.sqlite.core.netstandard\1.0.118\lib\netstandard2.0\System.Data.SQLite.dll"
+
+#!markdown
+
+# ADO.Net
+ADO.NET is a set of classes that expose data access services for .NET Framework programmers.
+
+## DataTable
+
+The [DataTable](https://learn.microsoft.com/en-us/dotnet/api/system.data.datatable) class is a central object in the ADO.NET library. Other objects that use DataTable include the [DataSet](https://learn.microsoft.com/en-us/dotnet/api/system.data.dataset) and the [DataView](https://learn.microsoft.com/en-us/dotnet/api/system.data.dataview).
+
+The DataTable stores data in memory, allowing for data manipulation without the need for a persistent database connection. This is useful for applications that need to work with data sets offline before updating a database.
+
+
+Here are some important ***methods*** of the **DataTable** class :
+- **NewRow()**: Creates a new DataRow with the same schema as the DataTable. This is used to add new rows to the table.
+Example: `var newRow = dataTable:NewRow()`
+- **Rows:Add()**: Adds a DataRow to the DataRowCollection for the DataTable. This can be a newly created row or an existing row from another table.
+Example: `dataTable:Rows:Add(newRow)`
+- **Clear()**: Clears all data (rows) from the DataTable but keeps the schema (columns and constraints).
+
+Here are some important ***properties*** of the **DataTable** class :
+- **TableName**: Specifies the name of the DataTable.
+- **Columns**: Provides access to the collection of DataColumn objects for the table.
+- **Rows**: Provides access to the collection of DataRow objects in the table. This allows you to add, update, or delete rows in the table.
+
+## DataRow
+
+The **DataRow** and **DataColumn** objects are primary components of a ***DataTable***.
+When you access the **Rows** property of a DataTable object you will have retrieve a ***DataRowCollection*** object.
+
+Each element of the Collection is a **DataRow** object : Use the **DataRow** object and its properties and methods to retrieve and evaluate, insert, delete, and update the values in the DataTable.
+Use the overloaded `Item[]` property to return or set the value of a DataColumn, using its *ordinal order* or its *name*.
+
+## SqlDataAdapter
+
+The **SqlDataAdapter** Object serves as a bridge between a DataTable and SQL Server for retrieving and saving data. It is used to execute SQL commands and fill the DataTable with results, and it can also update the database to reflect changes made in the DataSet.
+The **SqlDataAdapter** is a class that represents a set of SQL commands and a database connection.
+
+Here are some important ***methods*** of the **SqlDataAdapter** class :
+- **Fill()**: This method adds or refreshes rows in the DataTable to match those in the data source.
+- **Update()**: This method applies changes made in the DataTable back to the data source.
+
+## Practice
+
+> Copy and paste the following code :
+>
+ VAR sqlCommand := "SELECT * FROM user"
+ BEGIN USING var adapter := SQLiteDataAdapter{ sqlCommand, connection }
+ //
+ adapter:Fill( dt )
+ END USING
+
+> Then add you own code, to
+- Indicate how many Rows when have in the DataRowCollection. (***Hint***: Use `Length` to retrieve the size of **Arrays**, and `Count` for **Collection**s)
+- Enumerate all **DataRow**s object, and Print the value of the first `Item` of each DataRow. (Hint: Item collection starts at 0)
+- For each DataRow, Print the value of DataColumn called 'name'. (Hint: Put the name in the `[]`instead of the column number)
+
+#!xsharp
+
+using System.IO
+using System.Data
+#include "Xsharpdefs.xh"
+
+var connectionString := SQLiteConnectionStringBuilder{}
+connectionString:DataSource := "Test.db"
+var dt := DataTable{}
+BEGIN USING var connection := SQLiteConnection{connectionString:ToString()}
+ connection:Open()
+ // Paste here
+
+END USING
+// Add your own code here
+
+#!markdown
+
+Try the following code and adapt it to your needs :
+>
+ ? dt:Rows:Count
+ FOREACH oneRow AS DataRow IN dt:Rows
+ ? oneRow:Item[0]
+ ?? " : "
+ ?? oneRow["name"]
+ NEXT
+
+*Note that you can access the Value without using the **Item** property*
+
+#!markdown
+
+# CRUD
+### Create, Read, Update, Delete
+
+We have used the **Fill()** method of SQLiteDataAdapter, we will now use **Update()**.
+
+But in order to push the changes to the server, we will have to create an UPDATE command corresponding to the SELECT you have used to Fill() the DataTable : This is where the `SqlCommandBuilder` will help us.
+
+## SqlCommandBuilder
+
+The **SqlDataAdapter** does not automatically generate the SQL statements required to reconcile changes made to a DataTable with the associated instance of SQL Server.
+However, you can create a **SqlCommandBuilder** object to automatically generate SQL statements for single-table updates if you set the **SelectCommand** property of the **SqlDataAdapter**.
+
+> Copy the following code to check the **UPDATE** command. This will show the `CommandText` property of the Command
+
+ var sqlCommandBuilder := SQLiteCommandBuilder{adapter}
+ ? sqlCommandBuilder:GetUpdateCommand():CommandText
+
+> Then do the same to check the **INSERT** and **DELETE** commands.
+
+#!xsharp
+
+using System.IO
+using System.Data
+using System.Data.Sqlite
+#include "Xsharpdefs.xh"
+
+var connectionString := SQLiteConnectionStringBuilder{}
+connectionString:DataSource := "Test.db"
+var dt := DataTable{}
+BEGIN USING var connection := SQLiteConnection{connectionString:ToString()}
+ connection:Open()
+ // The Command used to choose the Data filling the DataTable
+ VAR sqlCommand := "SELECT * FROM user"
+ BEGIN USING var adapter := SQLiteDataAdapter{ sqlCommand, connection }
+ // Paste Here
+
+ END USING
+END USING
+
+#!markdown
+
+Now we can set the **UPDATE** command in the property `UpdateCommand` of the adapter, and do the same for **INSERT** and **DELETE**.
+
+> In the following code :
+- set the Commands in the adapter.
+- modify the `Item["name"]` of the second item (Index == 1) in the Rows collection : set its value to "Bruce Wayne".
+- **UPDATE** the **DataTable** using the adapter.
+- Write the content of the full **DataTable** to verify it has been **UPDATE**d
+
+#!xsharp
+
+using System.IO
+using System.Data
+using System.Data.Sqlite
+#include "Xsharpdefs.xh"
+
+var connectionString := SQLiteConnectionStringBuilder{}
+connectionString:DataSource := "Test.db"
+var dt := DataTable{}
+BEGIN USING var connection := SQLiteConnection{connectionString:ToString()}
+ connection:Open()
+ // The Command used to choose the Data filled into the DataTable
+ VAR sqlCommand := "SELECT * FROM user"
+ BEGIN USING var adapter := SQLiteDataAdapter{ sqlCommand, connection }
+ // Get the CommandBuilder
+ var sqlCommandBuilder := SQLiteCommandBuilder{adapter}
+ // Set Commands Here
+
+
+ // Fill the DataTable
+ adapter:Fill( dt )
+
+ // Write your modification and update code here
+
+
+ END USING
+END USING
+// The DataTable has been closed...
+
+BEGIN USING var connection := SQLiteConnection{connectionString:ToString()}
+ connection:Open()
+ // The Command used to choose the Data filled into the DataTable
+ var sqlCommand := "SELECT * FROM user"
+ BEGIN USING var adapter := SQLiteDataAdapter{ sqlCommand, connection }
+ // First Clear the DataTable
+ dt:Clear()
+ // Fill the DataTable
+ adapter:Fill( dt )
+ ? "---= DataTable Content =---"
+ FOREACH oneRow AS DataRow IN dt:Rows
+ ? oneRow:Item[0]
+ ?? " : "
+ ?? oneRow["name"]
+ NEXT
+ ? "---=====================---"
+ END USING
+
+END USING
+
+#!markdown
+
+# SandBox
+
+We have written some code to update a SQLite DB with some existing content.
+Use the following sandbox to enhance your application.
+
+- Add a new row in the DataTable with the INSERT Command
+- Reomve a row in the DataTable with the DELETE Command
+- Update the SQLite DB.
+- Print the content after your modifications
+
+#!xsharp
+
+? "SandBox"
diff --git a/SimpleSQL/07-PostgreSQL.dib b/SimpleSQL/07-PostgreSQL.dib
new file mode 100644
index 0000000..91536ed
--- /dev/null
+++ b/SimpleSQL/07-PostgreSQL.dib
@@ -0,0 +1,178 @@
+#!meta
+
+{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"languageName":"csharp","name":"csharp"},{"aliases":[],"languageName":"X#","name":"xsharp"}]}}
+
+#!markdown
+
+# Load the *XSharp Language kernel*
+
+#!markdown
+
+// <-= Press on the Arrow to run Me
+#r "nuget:XSharpInteractive"
+
+#!markdown
+
+# PostgreSQL
+
+PostgreSQL is a powerful, open source object-relational database system.
+
+In order to use the next Notebooks, you will need to have a running PostgreSQL server.
+
+## Installation
+You can download an installer for [PostgreSQL](https://www.postgresql.org/download/windows/), or you can get the [binaries for Windows](https://www.enterprisedb.com/download-postgresql-binaries).
+
+## Nuget Package
+In order to access the PostgreSQL server we will need install and use the **npgsql** package. You will find the same Classes/Methods/Properties that we had with **SQLite**, but they are prefixed with **Npgsql**.
+In order to use them, you will have to add a `using Npgsql`.
+
+#!csharp
+
+// <-= Press on the Arrow to install the package
+#r "nuget:npgsql"
+
+#!markdown
+
+## Loading
+Now, load the DLL into **XSharpInteractive** .
+***Don't forget to adapt the path to your own configuration.***
+
+#!xsharp
+
+// Load the DLL in the XSharpInteractive context
+#r "C:\Users\fabri\.nuget\packages\npgsql\8.0.4\lib\net8.0\Npgsql.dll"
+
+#!markdown
+
+## Usage
+
+## CREATE DATABASE
+
+First, we will create Database :
+- Create a ConnectionString not linked to a specific Database
+- Execute the CREATE DATABASE command
+
+> Execute the following code (only once!)
+
+#!xsharp
+
+using System.IO
+using Npgsql
+#include "Xsharpdefs.xh"
+
+var connectionString := NpgsqlConnectionStringBuilder{}
+connectionString:Host := "localhost"
+connectionString:Username := "postgres"
+connectionString:Password := "root" // Change the password according to your installation
+
+LOCAL dbCreate AS STRING
+TEXT TO dbCreate
+ CREATE DATABASE xsharpnotebooks
+ WITH OWNER = postgres
+ ENCODING = 'UTF8'
+ CONNECTION LIMIT = -1;
+
+ENDTEXT
+
+BEGIN USING var connection := NpgsqlConnection{connectionString:ToString()}
+ connection:Open()
+ //
+ BEGIN USING var command := connection:CreateCommand()
+ command:CommandText := dbCreate
+ command:ExecuteNonQuery()
+ END USING
+END USING
+
+#!markdown
+
+## CREATE TABLE
+Just like in the previous Notebooks, we will now **CREATE** the Table and **INSERT** few rows.
+
+> Copy the following SQL command, and paste between `TEXT ... ENDTEXT`
+( Hint : Leave a blank line between the last semi-colon and the `ENDTEXT` keyword)
+
+ DROP TABLE IF EXISTS "user";
+
+ DROP SEQUENCE IF EXISTS users_id_seq;
+
+ CREATE SEQUENCE users_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1;
+
+ CREATE TABLE "public"."user" (
+ "id" bigint DEFAULT nextval('users_id_seq') NOT NULL,
+ "name" character varying,
+ CONSTRAINT "users_pk" PRIMARY KEY ("id")
+ ) WITH (oids = false);
+
+ INSERT INTO "user" ("id", "name") VALUES
+ (1, 'Diana'),
+ (2, 'Bruce Wayne'),
+ (3, 'Peter Parker'),
+ (4, 'Natasha');
+
+#!xsharp
+
+using System.IO
+using Npgsql
+#include "Xsharpdefs.xh"
+
+var connectionString := NpgsqlConnectionStringBuilder{}
+connectionString:Host := "localhost"
+connectionString:Database := "xsharpnotebooks"
+connectionString:Username := "postgres"
+connectionString:Password := "root"
+
+LOCAL tableDef AS STRING
+TEXT TO tableDef
+
+
+ENDTEXT
+
+BEGIN USING var connection := NpgsqlConnection{connectionString:ToString()}
+ connection:Open()
+ //
+ BEGIN USING var command := connection:CreateCommand()
+ command:CommandText := tableDef
+ command:ExecuteNonQuery()
+ END USING
+END USING
+
+#!markdown
+
+## Reading
+
+Now that we have the structure of code, with the right names for Classes and Methods, you can check the code it self is the same as in the previous [Notebook about the Reader object](./04-Command_Reader.dib)
+
+> Copy this code
+
+ command:CommandText := "SELECT * FROM public.user"
+ BEGIN USING var reader := command:ExecuteReader()
+ IF (reader:HasRows)
+ DO WHILE reader:Read()
+ var id := reader:GetInt32(0)
+ var firstName := reader:GetString(1)
+ ? i"{id} : {firstName}"
+ ENDDO
+ ELSE
+ ? "No User found."
+ ENDIF
+ END USING
+
+#!xsharp
+
+using System.IO
+using Npgsql
+
+var connectionString := NpgsqlConnectionStringBuilder{}
+connectionString:Host := "localhost"
+connectionString:Database := "xsharpnotebooks"
+connectionString:Username := "postgres"
+connectionString:Password := "root"
+
+BEGIN USING var connection := NpgsqlConnection{connectionString:ToString()}
+ connection:Open()
+ //
+ BEGIN USING var command := connection:CreateCommand()
+ // Paste here
+
+ END USING
+END USING
diff --git a/Simple SQL/customers.sqlite b/SimpleSQL/customers.sqlite
similarity index 71%
rename from Simple SQL/customers.sqlite
rename to SimpleSQL/customers.sqlite
index 371dffe..7f8c2df 100644
Binary files a/Simple SQL/customers.sqlite and b/SimpleSQL/customers.sqlite differ