master
bicijinlian 2 months ago
parent 109cedc27e
commit 30653a7837

@ -1,25 +1,13 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "b1dedd01",
"metadata": {
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"导航目录结构\r\n"
]
}
],
"cell_type": "markdown",
"id": "a1287583",
"metadata": {},
"source": [
"Console.WriteLine(\"导航目录结构\");"
"目录结构\n",
"=======\n",
"-------"
]
}
],

@ -1,314 +0,0 @@
# 混合使用多语言并共享变量
混合使用多种语言(C#、F#、Powershell、SQL、KQL、Python、Html、JavaScript、JavaScript、Mermaind等语言),是多语言笔记的最大特性,并且支持各语言之间共享变量这一创新功能。
## 语言及共享变量的支持情况
| 语言 | 变量共享 |
| ---- | ---- |
| C# | 支持 |
| F# | 支持 |
| PowerShell | 支持 |
| JavaScript | 支持 |
| SQL | 支持 |
| KQL | 支持 |
| Python | 支持 |
| R | 支持 |
| HTML | 不支持 |
| Mermaid | 不支持 |
## 初始化
```C#
//全局初始化
#!import "./Base.ipynb"
//共享
using Microsoft.DotNet.Interactive;
using Microsoft.DotNet.Interactive.Commands;
```
## 使用 SQL(SQLite为例)
+ 引用NuGet包
```C#
#r "nuget:Microsoft.DotNet.Interactive.SQLite,*-*"
```
+ 连接 SQL内核(使用 `#!connect` 魔法命令)
```C#
/* 简单使用
#!connect sqlite --kernel-name SQLiteSharedKernel --connection-string "Data Source=.\assets\database\study.db;"
*/
//优化方法
using Microsoft.DotNet.Interactive;
using Microsoft.DotNet.Interactive.Commands;
{
//内核名:魔法命令中的内核名,执行后会自动加 sql- 前缀,做为内核名被使用
string magicCommandKernelName = "SQLiteSharedKernel";
string completeKernelName = "sql-" + magicCommandKernelName;
//引入内核:可重复执行
if(Microsoft.DotNet.Interactive.Kernel.Root.FindKernelByName(completeKernelName) == null)
{
var connectKernelCode = $"#!connect sqlite --kernel-name {magicCommandKernelName} --connection-string \"{SharedDbConnect.SQLiteConnectionString}\"";
await Kernel.Root.SendAsync(new SubmitCode( connectKernelCode, "csharp"));
}
else
{
Console.WriteLine($"名为 {completeKernelName} 的内核已存在。需要新内核时,请为--kernel-name参数使用不同的值, 本次执行不做任何更改!");
}
}
```
+ 使用SQL内核(SQL语句操作数据库)
```C#
#!sql-SQLiteSharedKernel
--查询年龄最大的5名学生
SELECT * FROM Student order by Age Desc LIMIT 5;
--查询学生总数
SELECT COUNT(*) AS Count FROM Student;
```
## 使用 KQL(效果图)
因为环境比较难搭建,用效果图代替。
![KQL效果](./assets/images/KQL_Samples.jpg)
## 使用 Raw Value Storage
使用value内核可以方便的管理共享文本数据。
```C#
#!value --name SharedDataFromValueKernel --from-file ./shared/file/data.json
```
## C#
### 一般使用
```C#
var student = new {Id=1, Name="张三",Age=18};
student.Display();
```
### 使用Value内核共享的变量
```C#
#!set --name fromSharedFileData --value @value:SharedDataFromValueKernel
fromSharedFileData.Display();
```
### 共享变量
使用C#查询数据库中的数据,共享出来,给其它语言(F#、html、js等使用)
```C#
#r "nuget:Microsoft.Data.SqlClient"
#r "nuget:Microsoft.Data.Sqlite"
using System.Data;
using System.Data.Common;
using System.Data.SqlTypes;
using Microsoft.Data.SqlClient;
using Microsoft.Data.Sqlite;
//使用 Ado.Net 从SQLite中获取数据再共享出去
//查询数据
var SharedStudents = new List<Student>();
{
DataSet ds = new DataSet();
using(var sqlConnection = new SqliteConnection(SharedDbConnect.SQLiteConnectionString))
{
sqlConnection.Open();
var querySql =
"""
SELECT * FROM Student LIMIT 5;
""";
var cmd = sqlConnection.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = querySql;
using (var reader = await cmd.ExecuteReaderAsync())
{
//循环输出行数据
while (await reader.ReadAsync())
{
var user = new Student()
{
Id = reader.GetFieldValue<int>("Id"),
Name = reader.GetFieldValue<string>("Name"),
Age = reader.GetFieldValue<int>("Age"),
};
SharedStudents.Add(user);
}
}
sqlConnection.Close();
sqlConnection.Dispose();
}
}
//共享数据:默认SharedStudents已共享其它语言引用即可
SharedStudents.Display();
```
## F#
### 简单示例
```C#
printfn "Hello World from F#"
```
### 使用C#共享数据
```C#
#!set --value @csharp:SharedStudents --name SharedStudents
SharedStudents
```
## Powershell
```C#
#!powershell
Write-Host "hellow powershell"
```
## 使用Python
+ 前提系统要安装Python环境推荐使用Anaconda安装直接安装Python也可以
+ 作用:使多语言笔记,直接支持 Python语言
+ 连接到 Python 内核
```C#
using Microsoft.DotNet.Interactive;
using Microsoft.DotNet.Interactive.Commands;
//引入Python内核使用C# 执行引入语句
if(Kernel.Root.FindKernelByName("pythonkernel2") == null)
{
//Console.WriteLine("正在导入Python内核....");
var importPythonkernel = "#!connect jupyter --kernel-name pythonkernel2 --kernel-spec python3";
await Kernel.Root.SendAsync(new SubmitCode( importPythonkernel, "csharp"));
Console.WriteLine("Python内核导入完成");
}
else
{
Console.WriteLine("Python内核已经导入");
}
```
+ 使用 Python
```C#
# 明确指定内核:优先级高于 "单元格选择的内核"
#!pythonkernel2
#!set --value @csharp:SharedStudents --name SharedStudents
print("你好,我是 Ployglot Notebooks 使用 Python语言内核打印的内容")
```
## 在 Python 中使用C#共享变量
```C#
#!pythonkernel2
#!set --value @csharp:SharedStudents --name SharedStudents
print(SharedStudents)
```
## 使用 html
```C#
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>完整html文档</title>
</head>
<body>
<div style="color:green">
<h2 id="MyTitle">使用Html文档当然也可以使用Html片断。注意纯HTML不支持共享变量</h2>
<table id="student" border="1" style="color:#fff">
<caption>C#共享数据:学生表</caption>
<thead>
<tr>
<th>编号</th>
<th>姓名</th>
<th>年龄</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</body>
</html>
```
## 使用 JavaScript
+ 简单使用
```C#
console.log("输出js日志");
```
+ 操作html
```C#
//改变上面html中h2文本颜色执行后上面看效果
document.getElementById("MyTitle").style.color = "red";
```
+ 使用C#共享数据,填充数据表格
```C#
#!set --value @csharp:SharedStudents --name SharedStudentsInJs
// 获取表格的tbody部分
const tableBody = document.querySelector('#student tbody');
// 遍历数据数组
SharedStudentsInJs.forEach(item => {
// 创建一行
const row = document.createElement('tr');
// 创建单元格并填入数据
const nameCell = document.createElement('td');
nameCell.textContent = item.Id;
row.appendChild(nameCell);
const ageCell = document.createElement('td');
ageCell.textContent = item.Name;
row.appendChild(ageCell);
const jobCell = document.createElement('td');
jobCell.textContent = item.Age;
row.appendChild(jobCell);
// 将该行添加到表格的tbody中
tableBody.appendChild(row);
});
```
## HTTP Request
## 基本请求
```C#
### 请求 京东
get https://www.jd.com
### 请求QQ
get https://www.qq.com
```
+ 高级请求(使用变量等)
```C#
@host=https://www.qq.com
get {{host}}
```
## 使用 mermaind
```C#
stateDiagram-v2
[*] --> Active
state Active {
[*] --> NumLockOff
NumLockOff --> NumLockOn : EvNumLockPressed
NumLockOn --> NumLockOff : EvNumLockPressed
--
[*] --> CapsLockOff
CapsLockOff --> CapsLockOn : EvCapsLockPressed
CapsLockOn --> CapsLockOff : EvCapsLockPressed
--
[*] --> ScrollLockOff
ScrollLockOff --> ScrollLockOn : EvScrollLockPressed
ScrollLockOn --> ScrollLockOff : EvScrollLockPressed
}
```
```C#
stateDiagram-v2
state fork_state <<fork>>
[*] --> fork_state
fork_state --> State2
fork_state --> State3
state join_state <<join>>
State2 --> join_state
State3 --> join_state
join_state --> State4
State4 --> [*]
```

@ -1,458 +0,0 @@
操作数据库
=========
原理:使用#!connect命令连接子内核进行操作。可以连接MSSQL、SQLite、PostgreSQL等
## 初始化
运行各单元格之前,必须先执行一次。
```csharp
//全局初始化
#!import "./Base.ipynb"
#r "nuget:Microsoft.Identity.Client,4.66.2"
//共享
using Microsoft.DotNet.Interactive;
using Microsoft.DotNet.Interactive.Commands;
```
## 连接数据库(SQL Server 2019为例)
### 第一步C#内核单元格中,引入相关的 nuget 包
每种数据库都有自己的包,形如 Microsoft.DotNet.Interactive.DbName, SQL Server的包为 Microsoft.DotNet.Interactive.SqlServer
+ 在 `VS Code 终端的 NuGet 包管理` 里输入 `Microsoft.DotNet.Interactive.` 查询(把预览给勾上)
![image](./assets/images/NuGet1.jpg)
+ 在[NuGet官网](https://www.nuget.org/)搜索 `Microsoft.DotNet.Interactive.`
![image](./assets/images/NuGet2.jpg)
```csharp
//引入 SqlServer 的 NuGet 包
#r "nuget:Microsoft.DotNet.Interactive.SqlServer,*-*"
```
### 第二步:使用 #connect 命令的语法,连接数据库子内核
使用魔术命令 `#!connect mssql --kernel-name SqlServerKernelDemo "Server=.\SQL2019;Database=study;User Id=sa;Password=gly-bicijinlian;TrustServerCertificate=true;"`
实测有缺点:
+ 不能重复执行:重复执行会报错
+ 连接字符串必须是真实字符串:不能是变量,不灵活;明文"不安全",比如演示环境
变通用法在C#程序中,拼接好魔术命令`#!connect`,再发送给内核执行
```csharp
// 连接魔术命令:
// #!connect mssql --kernel-name SqlServerKernelDemo "Server=.\SQL2019;Database=study;User Id=sa;Password=密码;TrustServerCertificate=true;"
//优化用法
{
//内核名:魔法命令中的内核名,执行后会自动加 sql- 前缀,做为内核名被使用
string magicCommandKernelName = "SqlServerKernelDemo";
string completeKernelName = "sql-" + magicCommandKernelName;
//引入内核:可重复执行
if(Microsoft.DotNet.Interactive.Kernel.Root.FindKernelByName(completeKernelName) == null)
{
var connectKernelCode = $"#!connect mssql --kernel-name {magicCommandKernelName} \"{SharedDbConnect.MsSqlConnectionString}\"";
await Kernel.Root.SendAsync(new SubmitCode( connectKernelCode, "csharp"));
}
else
{
Console.WriteLine($"名为 {completeKernelName} 的内核已存在。需要新内核时,请为--kernel-name参数使用不同的值, 本次执行不做任何更改!");
}
}
```
## 查询数据库(SQL Server 2019为例)
使用SQL语法进行数据库操作
```sql-SqlServerKernelDemo
--语句选择 #!connect 命令设置的SQL内核名
#!sql-SqlServerKernelDemo
--原始SQL查询语句
select top 10 * from student
```
使用SQL语法进行数据库操作
```sql-SqlServerKernelDemo
--右下方选择的SQL内核
--原始SQL查询语句
select top 10 * from student
```
## 连接数据库:使用 `--create-dbcontext` 参数,自动创建 EFCore 上下文 `DbContext`
说明目前默认情况下Microsoft.DotNet.Interactive.SqlServer里引用的Microsoft.Identity.Client包与环境中不一样故需要单独引用特定版本的Microsoft.Identity.Client包不知道后续官方是否会改正。目前单独包引用放在初始化单元格。
### 带 `--create-dbcontext` 参数的连接
```csharp
/* 连接魔术命令
给 #!connect mssql 加 --create-dbcontext 参数:连接操作,会执行下面的任务:
1、搭建EFCore基架,并初始化 DBContext 的实例: xxxx
2、安装包相关Nuget包,详情见输出
3、添加新的子内核 #!sql-xxx
**/
//注意由于引用的Microsoft.Identity.Client包版本冲突目前把 Microsoft.Identity.Client的引用放在 Microsoft.DotNet.Interactive.SqlServer之前
// #r "nuget:Microsoft.Identity.Client,4.66.2" 放入初始化
//优化用法
{
//内核名:魔法命令中的内核名,执行后会自动加 sql- 前缀,做为内核名被使用
string magicCommandKernelName = "SqlServerKernelWithEF";
string completeKernelName = "sql-" + magicCommandKernelName;
//引入内核:可重复执行
if(Microsoft.DotNet.Interactive.Kernel.Root.FindKernelByName(completeKernelName) == null)
{
var connectKernelCode = $"#!connect mssql --kernel-name {magicCommandKernelName} \"{SharedDbConnect.MsSqlConnectionString}\" --create-dbcontext";
await Kernel.Root.SendAsync(new SubmitCode( connectKernelCode, "csharp"));
}
else
{
Console.WriteLine($"名为 {completeKernelName} 的内核已存在。需要新内核时,请为--kernel-name参数使用不同的值, 本次执行不做任何更改!");
}
}
```
### SQL内核中使用(与一般连接一样使用)
```sql-SqlServerKernelWithEF
--原始SQL查询语句
select top 5 * from student
```
### C#语句中,直接使用 DbContext 上下文
```csharp
//连接建立后执行环境中就有了相关的类DBContext等
/* 因为类型版本问题,不能实际执行;后续看
{
//直接查询
var t = SqlServerKernelWithEF.Students.Take(5).ToList();
display(t);
//EF内执行SQL语句
FormattableString fs = $"select top 3 * from Student;";
var c = SqlServerKernelWithEF.Database.ExecuteSql(fs);
display(c);
}
*/
```
## 共享数据库操作结果数据(仅支持SQL Server数据库以SQL Server 2019为例)
### 1、引入NuGet包(引入一次就好,为明确步骤再次引入)
```csharp
#r "nuget:Microsoft.DotNet.Interactive.SqlServer,*-*"
```
### 2、连接内核命令生成新SQL内核
```csharp
//优化变通方式
{
//内核名:魔法命令中的内核名,执行后会自动加 sql- 前缀,做为内核名被使用
string magicCommandKernelName = "SqlServerKernelShared";
string completeKernelName = "sql-" + magicCommandKernelName;
//引入内核:可重复执行
if(Microsoft.DotNet.Interactive.Kernel.Root.FindKernelByName(completeKernelName) == null)
{
var connectKernelCode = $"#!connect mssql --kernel-name {magicCommandKernelName} \"{SharedDbConnect.MsSqlConnectionString}\"";
await Kernel.Root.SendAsync(new SubmitCode( connectKernelCode, "csharp"));
}
else
{
Console.WriteLine($"名为 {completeKernelName} 的内核已存在。需要新内核时,请为--kernel-name参数使用不同的值, 本次执行不做任何更改!");
}
}
```
### 3、共享方式使用第2步生成的新SQL内核操作数据库(使用了 --name 命令参数)
```sql-SqlServerKernelShared
#!sql-SqlServerKernelShared --name sqlServerKernelQueryShared
SELECT top 5 Id,Name,Age from Student;
SELECT 5 as Count;
```
### 4、多种方式使用查询共享数据
```csharp
//C#语言中使用:查询共享数据
#!set --name fromMsSqlQueryShared --value @sql-SqlServerKernelShared:sqlServerKernelQueryShared
using Microsoft.DotNet.Interactive.Formatting.TabularData;
using Microsoft.DotNet.Interactive.Formatting;
/**
说明这里获取到的共享SQL结果实质是SQL操作结果的Json序列化字符串
在C#单元格里,默认被反序列化为了 System.Text.Json.JsonElement,其根元素RootElement是结果集合(几条SQL语句结果集就有几项)
.dot interactive程序集中序列化时实际是使用 System.Text.Json 序列化的 TabularDataResource 类(在Microsoft.DotNet.Interactive.Formatting.TabularData命名空间)的集合
所以C#中使用共享数据有两种方式直接操作System.Text.Json.JsonElement对象和反序列化为TabularDataResource数组
*/
//使用共享数据直接操作JsonElement对象
{
//有几个操作结果fromMsSqlQuery.RootElement中就有几个结果集
var dataSet = fromMsSqlQueryShared.RootElement;
//第一个查询结果集
var dataTable1 = dataSet[0];
//列结构
var fieldNames = dataTable1.GetProperty("schema").GetProperty("fields").EnumerateArray().Select(s=>s.GetProperty("name"));
Console.WriteLine(string.Join(" ",fieldNames));
//数据
var data = dataTable1.GetProperty("data").EnumerateArray().AsEnumerable();
foreach(var row in data)
{
Console.WriteLine($"{row.GetProperty("Id")} {row.GetProperty("Name")} {row.GetProperty("Age")}");
}
//第2个结果集
var count = fromMsSqlQueryShared.RootElement[1].GetProperty("data")[0].GetProperty("Count");
//优化使用Html展示
}
//使用共享数据反序列化为TabularDataResource数组
{
var dataSetJsonText = fromMsSqlQueryShared.RootElement.GetRawText();
var dataTables = System.Text.Json.JsonSerializer.Deserialize<List<TabularDataResource>>(dataSetJsonText);
//结果1
dataTables[0].Display();
//结果2
dataTables[1].Display();
}
//使用共享数据直接转换为TabularDataResource数组
{
var resource = fromMsSqlQueryShared.RootElement.ToTabularDataResource().Data.ToList();
resource[0].First(s => s.Key=="data").Value.Display();
}
```
```javascript
//js中使用查询共享数据
#!set --name fromMsSqlQueryShared --value @sql-SqlServerKernelShared:sqlServerKernelQueryShared
console.log(fromMsSqlQueryShared[0].data)
```
## 各数据库示例
### SQL Server 数据库
```csharp
#r "nuget:Microsoft.DotNet.Interactive.SqlServer,*-*"
```
```csharp
using Microsoft.DotNet.Interactive;
using Microsoft.DotNet.Interactive.Commands;
//优化方式
{
//内核名:魔法命令中的内核名,执行后会自动加 sql- 前缀,做为内核名被使用
string magicCommandKernelName = "SqlServerKernelStudy";
string completeKernelName = "sql-" + magicCommandKernelName;
//引入内核:可重复执行
if(Microsoft.DotNet.Interactive.Kernel.Root.FindKernelByName(completeKernelName) == null)
{
var connectKernelCode = $"#!connect mssql --kernel-name {magicCommandKernelName} \"{SharedDbConnect.MsSqlConnectionString}\"";
await Kernel.Root.SendAsync(new SubmitCode( connectKernelCode, "csharp"));
}
else
{
Console.WriteLine($"名为 {completeKernelName} 的内核已存在。需要新内核时,请为--kernel-name参数使用不同的值, 本次执行不做任何更改!");
}
}
```
```sql-SqlServerKernelStudy
--直接执行SQL语句不共享结果
#!sql-SqlServerKernelStudy
SELECT top 5 * from Student;
```
```sql-SqlServerKernelStudy
--共享操作结果(可多个操作语句)
#!sql-SqlServerKernelStudy --name sqlServerQuerySharedDemo
SELECT top 5 Id,Name,Age from Student;
SELECT 5 as Count;
```
```csharp
#!set --name fromMsSqlQuery --value @sql-SqlServerKernelStudy:sqlServerQuerySharedDemo
using Microsoft.DotNet.Interactive.Formatting.TabularData;
using Microsoft.DotNet.Interactive.Formatting;
/**
说明这里获取到的共享SQL结果实质是SQL操作结果的Json序列化字符串
在C#单元格里,默认被反序列化为了 System.Text.Json.JsonElement,其根元素RootElement是结果集合(几条SQL语句结果集就有几项)
.dot interactive程序集中序列化时实际是使用 System.Text.Json 序列化的 TabularDataResource 类(在Microsoft.DotNet.Interactive.Formatting.TabularData命名空间)的集合
所以C#中使用共享数据有两种方式直接操作System.Text.Json.JsonElement对象和反序列化为TabularDataResource数组
*/
//使用共享数据直接操作JsonElement对象
{
//有几个操作结果fromMsSqlQuery.RootElement中就有几个结果集
var dataSet = fromMsSqlQuery.RootElement;
//第一个查询结果集
var dataTable1 = dataSet[0];
//列结构
var fieldNames = dataTable1.GetProperty("schema").GetProperty("fields").EnumerateArray().Select(s=>s.GetProperty("name"));
Console.WriteLine(string.Join(" ",fieldNames));
//数据
var data = dataTable1.GetProperty("data").EnumerateArray().AsEnumerable();
foreach(var row in data)
{
Console.WriteLine($"{row.GetProperty("Id")} {row.GetProperty("Name")} {row.GetProperty("Age")}");
}
//第2个结果集
var count = fromMsSqlQuery.RootElement[1].GetProperty("data")[0].GetProperty("Count");
//优化使用Html展示
}
//使用共享数据反序列化为TabularDataResource数组
{
var dataSetJsonText = fromMsSqlQuery.RootElement.GetRawText();
var dataTables = System.Text.Json.JsonSerializer.Deserialize<List<TabularDataResource>>(dataSetJsonText);
//结果1
dataTables[0].Display();
//结果2
dataTables[1].Display();
}
//使用共享数据直接转换为TabularDataResource数组
{
var resource = fromMsSqlQuery.RootElement.ToTabularDataResource().Data.ToList();
resource[0].First(s => s.Key=="data").Value.Display();
}
```
```javascript
#!set --name fromMsSqlQuery --value @sql-SqlServerKernelStudy:sqlServerQuerySharedDemo
console.log(fromMsSqlQuery[0].data)
```
### PostgreSQL 数据库
```csharp
#r "nuget:Microsoft.DotNet.Interactive.PostgreSQL,*-*"
```
```csharp
//魔术命令
//#!connect postgres --kernel-name PostgreSQLStudy "Host=localhost;Port=5432;Username=postgres;Password=替换成真实密码;Database=Study;"
using Microsoft.DotNet.Interactive;
using Microsoft.DotNet.Interactive.Commands;
//优化方法
{
//内核名:魔法命令中的内核名,执行后会自动加 sql- 前缀,做为内核名被使用
string magicCommandKernelName = "PostgreSQLStudy";
string completeKernelName = "sql-" + magicCommandKernelName;
//引入内核:可重复执行
if(Microsoft.DotNet.Interactive.Kernel.Root.FindKernelByName(completeKernelName) == null)
{
var connectKernelCode = $"#!connect postgres --kernel-name {magicCommandKernelName} \"{SharedDbConnect.PSQLConnectionString}\"";
await Kernel.Root.SendAsync(new SubmitCode( connectKernelCode, "csharp"));
}
else
{
Console.WriteLine($"名为 {completeKernelName} 的内核已存在。需要新内核时,请为--kernel-name参数使用不同的值, 本次执行不做任何更改!");
}
}
```
```sql-PostgreSQLStudy
#!sql-PostgreSQLStudy
SELECT * FROM "Student" LIMIT 2;
SELECT COUNT(*) as Count FROM "Student";
```
### SQLite 数据库
```csharp
#r "nuget:Microsoft.DotNet.Interactive.SQLite,*-*"
```
```csharp
/* 各种连接参数
相对位置:当前目录
#!connect sqlite --kernel-name SQLiteSharedKernel --connection-string "Data Source=.\assets\database\study.db;"
绝对目录位置
#!connect sqlite --kernel-name MySQLiteDemo "Data Source=C:\Database\study.db;"
缓存共享
#!connect sqlite --kernel-name MySQLiteDemo "Data Source=.\assets\database\study.db;Cache=Shared;"
使用带密码
#!connect sqlite --kernel-name MySQLiteDemo "Data Source=.\assets\database\study.db;Cache=Shared;Password=MyEncryptionKey;"
只读模式
#!connect sqlite --kernel-name MySQLiteDemo "Data Source=.\assets\database\study.db;Mode=ReadOnly"
读写创建模式
#!connect sqlite --kernel-name MySQLiteDemo "Data Source=.\assets\database\study.db;Mode=ReadWriteCreate"
读写模式
#!connect sqlite --kernel-name MySQLiteDemo "Data Source=.\assets\database\study.db;Mode=ReadWrite"
私有内存模式
#!connect sqlite --kernel-name MySQLiteDemo "Data Source=:memory:"
共享内存模式
#!connect sqlite --kernel-name MySQLiteDemo "Data Source=Sharable;Mode=Memory;Cache=Shared"
*/
using Microsoft.DotNet.Interactive;
using Microsoft.DotNet.Interactive.Commands;
//优化方法
{
//内核名:魔法命令中的内核名,执行后会自动加 sql- 前缀,做为内核名被使用
string magicCommandKernelName = "SQLiteSharedKernel";
string completeKernelName = "sql-" + magicCommandKernelName;
//引入内核:可重复执行
if(Microsoft.DotNet.Interactive.Kernel.Root.FindKernelByName(completeKernelName) == null)
{
var connectKernelCode = $"#!connect sqlite --kernel-name {magicCommandKernelName} --connection-string \"{SharedDbConnect.SQLiteConnectionString}\"";
await Kernel.Root.SendAsync(new SubmitCode( connectKernelCode, "csharp"));
}
else
{
Console.WriteLine($"名为 {completeKernelName} 的内核已存在。需要新内核时,请为--kernel-name参数使用不同的值, 本次执行不做任何更改!");
}
}
```
```sql-SQLiteSharedKernel
#!sql-SQLiteSharedKernel
SELECT * FROM Student LIMIT 2;
SELECT COUNT(*) AS Count FROM Student;
```
### DuckDB 数据库
```csharp
#r "nuget:Microsoft.DotNet.Interactive.DuckDB,*-*"
```
```csharp
//下面这种被注释的方式:不可重复执行
// #!connect duckdb --kernel-name DuckDBSharedKernel --connection-string "Data Source=:memory:?cache=shared"
// #!connect duckdb --kernel-name DuckDBSharedKernel --connection-string SharedDbConnect.DuckDBConnectionString
using Microsoft.DotNet.Interactive;
using Microsoft.DotNet.Interactive.Commands;
//优化方法
{
//内核名:魔法命令中的内核名,执行后会不加 sql- 前缀
string magicCommandKernelName = "DuckDBSharedKernel";
//引入内核:可重复执行
if(Kernel.Root.FindKernelByName(magicCommandKernelName) == null)
{
var connectKernelCode = $"#!connect duckdb --kernel-name {magicCommandKernelName} --connection-string \"{SharedDbConnect.DuckDBConnectionString}\"";
await Kernel.Root.SendAsync(new SubmitCode( connectKernelCode, "csharp"));
//初始化数据表及填充数据
var connectKernelCode2 =
"""
#!DuckDBSharedKernel
CREATE TABLE Student (
Id INTEGER,
Name TEXT,
Age INTEGER
);
INSERT INTO Student VALUES (1, '张三', 10), (2, '李四', 33), (3, '王五', 66);
""";
await Kernel.Root.SendAsync(new SubmitCode( connectKernelCode2, "csharp"));
}
else
{
Console.WriteLine($"名为 {magicCommandKernelName} 的内核已存在。需要新内核时,请为--kernel-name参数使用不同的值, 本次执行不做任何更改!");
}
}
```
```DuckDBSharedKernel
#!DuckDBSharedKernel
SELECT * FROM Student ORDER BY Id
```

@ -1,112 +0,0 @@
# 使用 扩展库 Microsoft.DotNet.Interactive.ExtensionLab
Microsoft.DotNet.Interactive.ExtensionLab 是 .NET Interactive 的扩展工具库,主要用于增强交互式编程环境的功能性,提供实验性功能模块和高级数据处理能力。
## 初始化
```csharp
#!import "./Base.ipynb"
```
## 添加 Nuget 包
添加包成功后,有用法摘要提示!
```csharp
//添加 Nuget 包
#r "nuget:Microsoft.DotNet.Interactive.ExtensionLab,*-*"
#r "nuget:Microsoft.Data.Analysis"
using Microsoft.DotNet.Interactive;
using Microsoft.DotNet.Interactive.Commands;
using Microsoft.DotNet.Interactive.Documents;
using Microsoft.DotNet.Interactive.Utility;
using Microsoft.DotNet.Interactive.ExtensionLab;
using Microsoft.Data.Analysis;
```
## 使用 #!linqify 魔法命令
`#!linqify` 魔法命令, 将一个普通的(通常是 C#)代码块转换为 LINQ 查询表达式。这允许你使用更简洁、更声明式的 LINQ 语法来操作数据集合。
注意没有提供新的Linq功能只是对原生linq的增强(隐匿返回)方便在Polyglot Notebooks 中使用而已。使用原生还是增强的,随个人喜好。
增强:
+ 隐式返回
在普通的 C# 代码中,你需要显式地 return LINQ 查询的结果才能看到输出。 使用 #!linqify查询结果会自动作为单元格的输出返回无需显式 return 语句。这在交互式 notebook 环境中更方便。
+ 简洁性
#!linqify 允许你省略一些样板代码。例如,你不需要定义一个单独的变量来存储 LINQ 查询的结果。
+ 交互性
#!linqify是为了更好地适应交互式 notebook 环境而设计的。它简化了在 notebook 中快速编写、执行和查看 LINQ 查询结果的过程。
`C#原生Linq``linqify魔法命令` 使用对比
```csharp
//公用数据
var students = new List<Student>()
{
new Student(){Id = 1, Name = "张三", Age = 8},
new Student(){Id = 2, Name = "李四", Age = 95},
new Student(){Id = 3, Name = "王五", Age = 60},
new Student(){Id = 4, Name = "张三丰", Age = 54},
new Student(){Id = 5, Name = "王二小", Age = 21},
new Student(){Id = 6, Name = "刘胡兰", Age = 74},
new Student(){Id = 7, Name = "列宁", Age = 19},
new Student(){Id = 8, Name = "江姐", Age = 52},
new Student(){Id = 9, Name = "张海迪", Age = 33},
new Student(){Id = 10, Name = "周小呼", Age = 46},
};
```
```csharp
//原生Linq
var result = students.Where(s => s.Name.StartsWith("张")).ToList();
result.Display();
//或者
var result2 = from s in students
where s.Name.StartsWith("张")
select s;
result.Display();
```
```csharp
#!linqify
students.Where(it => it.Age > 20)
```
总结:
+ #!linqify 不是为了实现 LINQ 本身。
+ #!linqify 是为了简化 LINQ 在 .NET Interactive Notebooks 中的 交互式使用,通过提供隐式返回和更简洁的语法。
+ 在功能上,使用 #!linqify 和直接编写 C# LINQ 代码是等价的。 你可以选择哪种方式更适合你的编码风格和工作流程。
## ExplainCode
这个扩展使用 Mermaid 内核为 csharp 代码生成序列图.以直观的图形方式,展现代码的执行流程。
```csharp
#!explain
var data = new[] { 1, 2, 3 };
for (var i = 0; i < data.Length; i++)
{
Console.WriteLine(i.ToString());
}
```
```csharp
#!explain
"Hello Explain".Display();
```
## 使用 #!inspect 命令,检查代码编译详细信息!
#!inspect 命令,允许您查看C#单元格代码的反编译、IL和JIT Asm. 注意深色模式下展示区顶部的Tab[C# | IL JIT | ASM]不明显但可以正常点击切换或者换个VSCode主题。
```csharp
#!inspect
foreach(var x in Enumerable.Range(1,10))
{
Console.WriteLine(x);
}
```
## 使用 #!record命令来保存您运行的代码的副本
一旦您使用 #!recore 启用转录本记录每个代码提交包括重新运行的单元格都记录在指定的文件中。所使用的JSON格式与.NET Interactive stdio和http API识别的格式相同可用于通过自动化回放交互式会话。
1. 使用 record 魔法命令,设定转录文件
```csharp
#!record --output recore.json
//会在笔记所在的目录生成 recore.json 文件
Console.WriteLine("hello,record!");
```
2. 读取转录文件
```csharp
using System.IO;
var fileContent = await File.ReadAllTextAsync("recore.json",System.Text.Encoding.UTF8);
Console.WriteLine(fileContent);
```

@ -1,209 +0,0 @@
在笔记中共享数据(变量)
====================
使用 .NET 交互式内核,可以在单个笔记本中以多种语言编写代码。为了利用每种语言的不同优势,您会发现在它们之间共享数据很有用。即一种语言的变量,可以在其它语言中使用。
默认情况下,.NET Interactive 支持多种不同的语言其中大多数语言都允许使用magic 命令`#!set` 和 `#!shared` 进行共享。
## 变量共享的语言支持情况
| 语言 | 变量共享 |
| ---- | ---- |
| C# | 支持 |
| F# | 支持 |
| PowerShell | 支持 |
| JavaScript | 支持 |
| SQL | 支持 |
| KQL | 支持 |
| Python | 支持 |
| R | 支持 |
| HTML | 不支持 |
| Mermaid | 不支持 |
## 同种内核 默认共享数据
同种内核的不同单元格之间,无需任何操作,变量默认共享,后续单元格直接使用前面已执行单元格的数据。
+ JS 各单元格共享示例:
```javascript
//声明变量
JsShared = "jsShared";
```
```javascript
//直接使用上面单元格变量
console.log(JsShared);
```
+ C# 各单元格共享示例:
```csharp
//声明变量
string CsharpShared = "CsharpShared";
```
```csharp
//直接使用
Console.WriteLine(CsharpShared);
```
## 使用 `#!set``#!shared` 魔法命令共享数据
> `#!shared` 魔法命令从 .NET Interactive 的早期就已经存在,而 `#!set` 是较新的命令,它提供了` #!share` 功能的超集。由于 `#!set` 具有更丰富的功能并且更具可读性,因此就优先使用`#!set`。
> 把 `#!share` 命令重写为 `#!set` 命令很容易
`#!share` 用法的示例:
```javascript
//声明一个要被共享的js变量
//共享的变量声明不要加var、let、const关键字,加了变局部变量
jsVar = "js变量值";
#!share --from javascript jsVar --as csVarFromJs22222
```
```csharp
//共享变量
#!share --from javascript jsVar --as csVarFromJs
Console.WriteLine(csVarFromJs);
```
改写为 等价的 `#!set` 命令:
```csharp
#!set --name csVarFromJs --value @javascript:jsVar
Console.WriteLine(csVarFromJs);
```
`#!share`命令通过 --from 选项,声明了共享数据来源,通过 --as 选项 声明共享变量的新名称,方便后续使用;
`#!set`命令通过更加明确的选项 `--name``--value` 选项, 指明了共享数据的值(形如:@来源:值形)和新名称;
## 变量视图:管理变量
![变量共享](./assets/images/shared.001.jpg)
## 内核之间共享数据
示例C# 运行中的变量,被其它语言共享。
```csharp
//定义变量:存储网关
string getway = "192.168.1.1";
```
+ PowerShell 中使用
```pwsh
# Poweshell中使用 前面C#单元中定义的变量
# 特别注意因为PS中变量名必须以$开头,所以在命令中 name 参数名在PS中使用时必须加$前辍
#!set --value @csharp:getway --name gw
Write-Host $gw
```
+ F# 中使用
```fsharp
#!set --value @csharp:getway --name getway
Console.WriteLine(getway)
```
+ 在javascrip中使用
```javascript
#!set --value @csharp:getway --name getway
console.log(getway);
```
## 从用户输入中设置变量
共享数据变量的值,不但能直接设置、来自其它变量,还可以是由用户输入的。这在需要用户交互时,非常有用,比如:需要用户输入密码、流程控制由用户选择等。
注意执行后会在VS Code顶部弹出一个小的用户输入窗口用户输入内容并且确认后用户的输入内容会被存储为变量的值.
可以在魔法命令中使用一个@input前缀直接从用户输入中设置一个值。比如
```csharp
#!set --name userName --value @input("请输入姓名");
Console.WriteLine($"输入的姓名是:{userName}")
```
如果希望用户输入在UI中被遮盖(比如不希望在屏幕上显示的秘密),可以使用@password前缀来代替@input
```csharp
#!set --name userPassword --value @password("请输入密码");
Console.WriteLine($"输入的密码是:{userPassword}");
```
通过和前缀请求用户输入的能力不仅仅局限于共享数据的魔法命令,还可以在程序中使用。比如:
```csharp
using Microsoft.DotNet.Interactive;
var input = await Kernel.GetInputAsync("Pick a number.");
Console.WriteLine($"输入为:{input}")
```
## MIME 类型
在.NET Interactive中当变量在子内核之间共享时通常需要将其转换为某种字符串表示形式。这是因为.NET Interactive中的许多子内核运行在不同的进程中。例如核心内核在其自己的.NET进程中运行而多语言笔记本扩展在VS Code进程中运行。你还可以在远程机器上运行子内核。子内核也可以在不同的平台上实现例如.NET和JavaScript。
因此,虽然在共享进程时,.NET语言之间可以通过引用共享变量但共享的主要用例涉及某种形式的序列化。序列化格式由MIME类型指定用户可以通过可选的选项来指定。如果不指定选项则默认使用text/plain MIME类型用于变量共享。
这意味着请求的变量将由源内核序列化为JSON然后可选地由目标内核进行反序列化。对于基于.NET的内核序列化使用特定的方法进行。在基于.NET的目标内核中使用的反序列化策略如下
| 源json类型 | 目标.NET类型 |
| ---- | ---- |
| boolean | System.Boolean |
| number | System.Double |
| string | System.String |
| other | System.Text.Json.JsonDocument |
将变量转换为指定的MIME类型是通过使用.NET Interactive格式化API完成的这些API可以定制。
## 引用共享
默认情况下的共享是值共享(即副本共享),在特定情况下,引用类型变量可以通过引用进行共享。但要注意:
+ 源和目标内核必须在同一进程中运行。
+ 源和目标内核必须基于公共语言运行时如C#、F#、PowerShell
+ 如果使用的是#!set--byref引用共享仅在使用该选项时启用。
+ 如果使用的是#!share--mime-type引用共享是默认行为但在使用该选项时会禁用。
因此,如果共享一个可变对象,其状态的更改将在子内核间立即可见,这与默认的基于序列化的共享不同。
```fsharp
//F# 声明数组
open System.Collections.Generic;
let messages = List<string>()
messages.Add "由F#添加"
```
```csharp
//C#获取并修改
#!set --byref --value @fsharp:messages --name msgList
msgList.Add("由C#添加");
msgList.Display();
```
```fsharp
//F#中的原数组已被C#修改
messages
```
## `#!value` 内核,直接设置值
在笔记本中使用文本是很常见的需求。这些文本可能是JSON、CSV、XML或其他格式。它们可能存在于文件中、剪贴板上或者在网页上。
为了尽可能方便地将这些文本导入到笔记本中的变量里,我们提供了`#!value`魔法命令。需要知道的重要一点是,这是一个别名,指向一个专门设计用于存储值的子内核。这意味着一旦将某些内容存储在其中,就可以通过或从另一个子内核访问它(`#!set` `#!share`)
有三种方法可以使用来将数据导入到你的笔记本会话中:
+ 剪贴板
最简单的使用方法是将一些文本粘贴到单元格中。文本将被存储为字符串但与在C#、F#或PowerShell中使用字面量不同这里不需要转义任何内容。
```value
#!value --name StudentJson
{
"Id":2,
"Name":"小李",
"Age":33
}
```
```csharp
#!set --name fromValueKernel --value @value:StudentJson
fromValueKernel.Display();
```
+ 文件
数据存储在一个文件中时,使用带有选项的命令:#!value--from-file, 获取共享数据。
```value
#!value --name fromFileData --from-file ./shared/file/data.json
```
```csharp
#!set --name fromValueFileData --value @value:fromFileData
fromValueFileData.Display();
```
+ URL
也可以使用--from-url选项从一个URL地址获取数据
```value
#!value --name fromUrlData --from-url https://www.qq.com
```
```csharp
#!set --name fromValueUrlData --value @value:fromUrlData
fromValueUrlData.Display();
```
## 指定 MIME 类型
无论使用哪种方法,都可以使用`--mime-type`选项在提交时选择在笔记本中显示值。如果笔记本前端知道如何显示mime类型可以看到它格式正确
```value
#!value --name JsonData --mime-type application/json
{
"Id":2,
"Name":"小张",
"Adress":{
"Code":"0394",
"info":"变法路36号101"
}
}
```

@ -1,106 +0,0 @@
运行 xUnit 单元测试
=====================
Polgylot Notebooks 并没有直接支持单元测试框架。不能像VS里那样方便的进行单元测试。简单远行的话可以使用下面的方案
## 1、引入必要的NuGet包到.NET Interactive环境中
```csharp
// 默认包源
#i "nuget:https://api.nuget.org/v3/index.json"
//Nuget 包引用
#r "nuget:xunit"
#r "nuget: xunit.assert"
#r "nuget:xunit.runner.visualstudio"
#r "nuget:xunit.runner.console"
```
## 2、定义测试类和测试方法
```csharp
using Xunit;
public class CalculatorTests
{
[Fact]
public void Add_ReturnsCorrectSum()
{
var calculator = new Calculator();
var result = calculator.Add(2, 2);
Assert.Equal(4, result);
}
[Fact]
public void Subtract_ReturnsCorrectDifference()
{
var calculator = new Calculator();
var result = calculator.Subtract(5, 3);
Assert.Equal(2, result);
}
}
public class Calculator
{
public int Add(int x, int y) => x + y;
public int Subtract(int x, int y) => x - y;
}
```
## 3、执行测试并获取结果。在.NET Interactive中直接运行xUnit测试并不像在Visual Studio或通过命令行那样直接。你需要手动实例化测试类并调用测试方法或者使用反射来自动发现和执行测试方法。以下是一个简化的例子展示如何手动执行测试方法并输出结果。
+ 手动调用测试
```csharp
// 手动执行测试方法
var calculatorTests = new CalculatorTests();
// 执行Add_ReturnsCorrectSum测试
calculatorTests.Add_ReturnsCorrectSum();
// 执行Subtract_ReturnsCorrectDifference测试
calculatorTests.Subtract_ReturnsCorrectDifference();
Console.WriteLine("Tests executed successfully.");
```
+ 使用反射的例子
```csharp
#r "nuget: xunit"
#r "nuget: xunit.assert"
using Xunit;
using System.Reflection;
```
```csharp
//被测试类
public class Calculator
{
public int Add(int a, int b) => a + b;
}
//测试类
public class CalculatorTests
{
[Fact]
public void Add_TwoNumbers_ReturnsSum()
{
var calculator = new Calculator();
int result = calculator.Add(1, 2);
Assert.Equal(3, result); //:ml-citation{ref="3,4" data="citationList"}
}
}
//使用反射,调用被测试类
var testInstance = new CalculatorTests();
var testMethods = testInstance.GetType()
.GetMethods()
.Where(m => m.GetCustomAttributes(typeof(FactAttribute), false).Length > 0);
foreach (var method in testMethods)
{
try
{
method.Invoke(testInstance, null);
Console.WriteLine($"✅ {method.Name} 通过");
}
catch (Exception ex)
{
Console.WriteLine($"❌ {method.Name} 失败: {ex.InnerException?.Message}");
}
}
```
Loading…
Cancel
Save