以下是 Spectre.Console 的核心功能示例,涵盖最常用的输出和交互场景:
1. 基础富文本输出

using Spectre.Console;
// 使用 Markup 语法(类似 BBCode)
AnsiConsole.Markup("[bold green]成功![/] 文件已保存。\n");
AnsiConsole.Markup("[red]错误:[/] 无法连接到服务器。\n");
// 混合样式
AnsiConsole.Markup("[underline blue]https://example.com[/]\n");
// 自动换行写
AnsiConsole.Write(new Panel("[yellow]警告[/] 磁盘空间不足")
.Header("系统通知")
.Border(BoxBorder.Rounded));2. 表格

var table = new Table();
table.AddColumn("[u]ID[/]");
table.AddColumn(new TableColumn("[u]名称[/]").Centered());
table.AddColumn("[u]状态[/]");
table.AddRow("1", "订单服务", "[green]运行中[/]");
table.AddRow("2", "支付网关", "[red]离线[/]");
table.AddRow("3", "消息队列", "[yellow]警告[/]");
AnsiConsole.Write(table);3. 进度条 / 状态指示


// 带进度条的循环任务
await AnsiConsole.Progress()
.StartAsync(async ctx =>
{
var task1 = ctx.AddTask("[green]下载文件[/]", maxValue: 100);
var task2 = ctx.AddTask("[green]处理数据[/]", maxValue: 100);
while (!ctx.IsFinished)
{
task1.Increment(1.5);
task2.Increment(0.8);
await Task.Delay(50);
}
});
// 不确定时长的旋转状态
await AnsiConsole.Status()
.StartAsync("正在连接...", async ctx =>
{
await Task.Delay(3000); // 模拟工作
AnsiConsole.MarkupLine("[green]连接成功![/]");
});4. 交互式提示

// 确认
if (AnsiConsole.Confirm("是否继续安装?"))
{
// 执行安装
}
// 文本输入(带验证)
var name = AnsiConsole.Prompt(
new TextPrompt<string>("请输入用户名:")
.ValidationErrorMessage("[red]用户名不能为空[/]")
.Validate(input => !string.IsNullOrWhiteSpace(input)));
// 选择列表
var fruit = AnsiConsole.Prompt(
new SelectionPrompt<string>()
.Title("请选择最喜欢的水果")
.AddChoices(new[] { "苹果", "香蕉", "橙子", "葡萄" }));
AnsiConsole.MarkupLine($"你选择了:[green]{fruit}[/]");
// 多选
var colors = AnsiConsole.Prompt(
new MultiSelectionPrompt<string>()
.Title("请选择颜色")
.AddChoices(new[] { "红色", "绿色", "蓝色", "黄色" }));5. 树形结构

var root = new Tree("[yellow]项目结构[/]");
var src = root.AddNode("src");
src.AddNode("Program.cs");
src.AddNode("Services");
src.AddNode("Models");
var tests = root.AddNode("tests");
tests.AddNode("UnitTests");
AnsiConsole.Write(root);6. 布局 / 网格

var layout = new Layout("Root")
.SplitColumns(
new Layout("Left").Size(30),
new Layout("Right")
.SplitRows(
new Layout("Top"),
new Layout("Bottom")));
layout["Left"].Update(new Panel("导航栏"));
layout["Top"].Update(new Panel("主内容区"));
layout["Bottom"].Update(new Panel("日志输出"));
AnsiConsole.Write(layout);7. 实时更新

var table = new Table().AddColumn("Time").AddColumn("Message");
table.AddRow("10:00", "系统启动");
await AnsiConsole.Live(table)
.StartAsync(async ctx =>
{
for (int i = 1; i <= 5; i++)
{
await Task.Delay(1000);
table.AddRow($"10:0{i}", $"事件 {i}");
ctx.Refresh(); // 刷新显示
}
});8. 带样式的异常显示

try
{
throw new InvalidOperationException("操作失败");
}
catch (Exception ex)
{
AnsiConsole.WriteException(ex, ExceptionFormats.ShortenPaths);
}9. 日历

var calendar = new Calendar(2026, 4);
calendar.AddCalendarEvent(2026, 4, 22); // 标记日期
calendar.HighlightStyle(Style.Parse("yellow bold"));
AnsiConsole.Write(calendar);10. 条形图 / 柱状图

AnsiConsole.Write(new BarChart()
.Width(60)
.Label("[green bold]项目进度[/]")
.CenterLabel()
.AddItem("后端", 80, Color.Green)
.AddItem("前端", 65, Color.Blue)
.AddItem("测试", 40, Color.Red));11. 分解图

AnsiConsole.Write(new BreakdownChart()
.Width(60)
.AddItem("CPU", 45, Color.Red)
.AddItem("内存", 30, Color.Blue)
.AddItem("磁盘", 25, Color.Green));12. 规则线

AnsiConsole.Write(new Rule("[red]警告区域[/]").RuleStyle("red").LeftJustified());
AnsiConsole.WriteLine("内容");
AnsiConsole.Write(new Rule().RuleStyle("green"));13. 文本样式

// 大字标题
AnsiConsole.Write(new FigletText("Hello").Color(Color.Green));
// Emoji
AnsiConsole.Markup(":check_mark_button: 成功 :cross_mark: 失败");14. 网格

var grid = new Grid();
grid.AddColumn(new GridColumn().NoWrap().PadRight(4));
grid.AddColumn();
grid.AddRow("[b]名称[/]", "Spectre.Console");
grid.AddRow("[b]版本[/]", "0.49.1");
grid.AddRow("[b]许可[/]", "MIT");
AnsiConsole.Write(grid);在MySQL中,没有单条SQL语句能直接优化和修复所有表。不过,有以下两种常用方法可以实现这个需求:
1. 使用MySQL命令行工具:
mysqlcheck -u root -p --auto-repair --optimize --all-databases这条命令会提示输入密码,然后自动修复并优化所有数据库中的所有表。
2. 如果确实需要在MySQL客户端内执行,可以生成批处理语句:
SELECT CONCAT('OPTIMIZE TABLE ', table_schema, '.', table_name, '; REPAIR TABLE ', table_schema, '.', table_name, ';')
FROM information_schema.tables
WHERE table_schema NOT IN ('information_schema','mysql','performance_schema','sys');执行此查询后,复制结果中的所有语句再执行。
注意:执行表优化和修复操作需要相应权限,且在高负载生产环境中应谨慎操作,最好在低峰期进行。
今天发现在 .NET Framework 和 .NET 9 中使用 System.Uri.EscapeDataString() 方法对字符串进行编码,会产生不同的结果。
譬如“(”符号,前者视其为非保留字符,不进行转义,后者视为保留字符,转义为“%28”。
原因是 .NET Framework 4.8 主要遵循 RFC 2396,而 .NET 9 遵循 RFC 3986。
在跨平台签名验证场景中,对 URL 编码的一致性要求极高,任何细微差别都会导致签名校验失败。
以下是以 RFC 3986 标准为核心、优先使用各平台内置的高一致性方案。
对于 .NET 9,直接使用 Uri.EscapeDataString()。
string encodedData = System.Uri.EscapeDataString(dataToEncode);对于 .NET Framework,以下是一个遵循 RFC 3986 严格标准的自定义编码方法示例。
static string Rfc3986EscapeDataString(string input)
{
// 定义 RFC 3986 中明确的未保留字符集(不编码)
var unreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
var result = new StringBuilder();
byte[] data = Encoding.UTF8.GetBytes(input); // 统一转换为 UTF-8 字节
foreach (byte b in data)
{
char currentChar = (char)b;
// 如果是未保留字符,直接输出
if (unreservedChars.IndexOf(currentChar) != -1)
{
result.Append(currentChar);
}
else
{
// 否则,进行百分号编码(%XX,大写)
result.Append('%').Append(b.ToString("X2"));
}
}
return result.ToString();
}对于 PHP,直接使用内置的 rawurlencode() 函数。这个函数的设计初衷就是严格遵循 RFC 3986 标准。
$encoded_data = rawurlencode($data_to_encode);对于 JavaScript,encodeURIComponent 函数严格遵循 RFC 3986 标准。
let encodedData = encodeURIComponent(dataToEncode);重要提示:无论使用哪种语言,务必在编码前明确指定字符串使用 UTF-8 编码。编码不一致是导致乱码和签名失败最常见的原因之一 。
使用 Linq 语法调用数据库时,需要包含导航属性(外键表数据)会用到 Include 方法,但是如果引用的程序集搞错了,就不会有数据输出,应该:
using Microsoft.EntityFrameworkCore;
而不是
using System.Data.Entity;
using System.Runtime.InteropServices;
/// <summary>
/// Windows 资源管理器的文件名排序规则(允许在非 Windows 平台使用)
/// <para>调用 Windows API(StrCmpLogicalW 函数)实现,仅在 Windows 平台环境中使用</para>
/// <para>基本规则如下:</para>
/// <para>将文件名中的数字作为数值来处理。</para>
/// <para>不区分大小写。</para>
/// <para>字符类型的大致优先级顺序为:特殊符号 → 数字 → 字母。</para>
/// <para>对于中文文件名,排序方式取决于系统的区域设置:拼音(默认)、笔画。</para>
/// </summary>
public class FileLogicalComparer : IComparer<string>
{
[DllImport("Shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern int StrCmpLogicalW(string psz1, string psz2);
private static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
public int Compare(string? x, string? y)
{
// 处理 null 值情况
if (x == null && y == null) return 0;
if (x == null) return -1;
if (y == null) return 1;
try
{
if (IsWindows)
{
// Windows 平台:使用原生 API
return StrCmpLogicalW(x, y);
}
else
{
// 非 Windows 平台:使用 C# 实现的回退方案
return NaturalCompareFallback(x, y);
}
}
catch (Exception) // 捕获 DllNotFound、EntryPointNotFoundException 等异常
{
// 异常时使用默认的字符串比较器
return string.Compare(x, y, StringComparison.Ordinal);
}
}
/// <summary>
/// C# 实现的自然排序回退方案(代码来自 AI,未测试!)
/// </summary>
private static int NaturalCompareFallback(string x, string y)
{
if (x == y) return 0;
int i = 0, j = 0;
while (i < x.Length && j < y.Length)
{
if (char.IsDigit(x[i]) && char.IsDigit(y[j]))
{
// 提取连续数字并进行数值比较
string num1 = ExtractNumber(x, ref i);
string num2 = ExtractNumber(y, ref j);
if (long.TryParse(num1, out long n1) && long.TryParse(num2, out long n2))
{
if (n1 != n2)
return n1.CompareTo(n2);
}
else
{
// 解析失败时按字符串比较
int strCompare = string.Compare(num1, num2, StringComparison.Ordinal);
if (strCompare != 0)
return strCompare;
}
}
else
{
// 非数字字符直接比较
if (x[i] != y[j])
return x[i].CompareTo(y[j]);
i++;
j++;
}
}
return x.Length.CompareTo(y.Length);
}
/// <summary>
/// 从字符串中提取连续的数字序列
/// </summary>
private static string ExtractNumber(string str, ref int index)
{
int start = index;
while (index < str.Length && char.IsDigit(str[index]))
{
index++;
}
return str.Substring(start, index - start);
}
}
在 Linux 上运行 .NET 网站,通过
HttpContext.Connection.RemoteIpAddress获取客户端的 IP 地址,结果是
::ffff:127.0.0.1解决方法:
打开 Program.cs 文件,在 var app = builder.Build(); 之前(尽量往前)添加以下代码:
if (OperatingSystem.IsLinux())
{
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor
| ForwardedHeaders.XForwardedProto
| ForwardedHeaders.XForwardedHost;
// 清除 KnownNetworks 和 KnownProxies,表示信任来自本机的代理(如 Nginx)
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
Console.WriteLine("ForwardedHeaders enabled (Running on Linux)");
}然后在 app.UseRouting(); 之前添加以下代码:
if (OperatingSystem.IsLinux())
{
app.UseForwardedHeaders();
Console.WriteLine("UseForwardedHeaders() applied.");
}其中,OperatingSystem.IsLinux() 用于判断只在 Linux 环境中生效,你可以视自身情况作判断。
AutoUpdater.NET 是一个开源库,专为 .NET 桌面应用程序设计,支持 Windows Forms 和 WPF 应用。它通过从服务器获取 XML 文件来检测新版本信息,当发现新版本时向用户显示更新对话框。
相对于 ClickOnce,AutoUpdater.NET 的配置更简单一些。
首先通过 NuGet 包管理器安装 Autoupdater.NET.Official,然后在应用程序入口点添加以下代码:
AutoUpdater.Start("http://yourserver.com/path/to/update.xml");XML 文件结构如下:
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>2.0.0.0</version> <!--必填:最新版本号-->
<url>http://yourserver.com/path/to/updatefile.zip</url> <!--必填:更新文件下载地址-->
<changelog>http://yourserver.com/path/to/changelog.txt</changelog> <!--可选:更新日志-->
<mandatory>False</mandatory> <!--可选:是否强制更新-->
</item>Windows Forms 在 Program.cs 文件的 Main() 方法中,WPF 在 App.xaml.cs 的 OnStartup() 中添加:
AutoUpdater.Start("http://yourserver.com/path/to/update.xml");
AutoUpdater.CheckForUpdateEvent += (e) =>
{
if (e.Error != null)
{
MessageBox.Show($"检查版本更新失败:{e.Error.Message}");
}
if (e.IsUpdateAvailable)
{
if (e.Mandatory.Value)
{
// 强制更新
AutoUpdater.DownloadUpdate(e);
}
else
{
// 可选更新
if (MessageBox.Show("发现新版本,是否立即更新?", "更新提示", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes)
{
AutoUpdater.DownloadUpdate(e);
}
}
}
};这样就能简单实现打开应用时判断是否有更新。具体用法参:GitHub
对于控制台应用程序,AutoUpdater.NET 并不直接支持。可以使用 GeneralUpdate 等轻量级自动更新库。
其它:
若希望通过标准安装流程(如添加到开始菜单),优先选择 ClickOnce,适合长期维护的内部工具。
若追求快速集成和无感更新,AutoUpdater.NET 更灵活。
无论哪种方案,务必对程序进行代码签名(如购买企业证书或生成测试证书),否则系统可能拦截安装。(你要允许来自未知发布者的此应用对你的设备进行更改吗)
可以购买“OV 代码签名”证书或“EV 代码签名”证书,注意:“代码签名证书”与“域名证书”互不通用。
IIS 中使用 HttpPlatformHandler 模块部署 python web 项目时遇到 502.3 网关错误:
HTTP 错误 502.3 - Bad Gateway
There was a connection error while trying to route the request.
最可能的原因:
CGI 应用程序没有返回一组有效的 HTTP 错误。
由于父网关中出现错误,充当代理或网关的服务器无法处理该请求。
可尝试的操作:
使用 DebugDiag 排查 CGI 应用程序。
确定此错误是否由代理或网关引起。
详细错误信息:
模块 httpPlatformHandler
通知 ExecuteRequestHandler
处理程序 httpplatformhandler
错误代码 见下方表格
详细信息:
当 CGI 应用程序未返回一组有效的 HTTP 头,或者代理或网关无法将请求发送至父网关时,便会出现此错误。您可能需要获取一个网络跟踪,或者与代理服务器管理员联系(如果不是 CGI 的问题)。
| 错误代码 | 可能的错误原因 |
|---|---|
| 0x8007053d | 未安装相关组件库,建议执行命令:pip install -r requirements.txt |
| 0x80070005 | 若显示配置错误“由于权限不足而无法读取配置文件”则是应用代码目录未配置相应用户的读取权限; 访问受限,请正确配置 IIS 中的网站凭据、应用程序池标识、项目目录“读取”、解释器目录“读取和执行”权限等 |
| 0x80072ee2 | 执行超时,一般发生在批量读写的时候 |
| 0x8007042b | |
| 任何错误 | 查看日志文件。 可能的错误原因:
|
在代码中添加“不跟踪”(No-Tracking)功能,以提高查询性能(避免实体状态跟踪)。
确保后续操作无需更新返回的实体(如没有 SaveChanges 操作)。
在条件(Where)、排序(OrderBy、OrderByDescending)、分页(Skip、Take)等之前添加 .AsNoTracking(),如:db.Table.AsNoTracking().Where(...).ToList()。
如果查询中包含导航属性,它们也会因主查询的不跟踪而保持不跟踪状态。
AsNoTracking() 适用于查询简单无嵌套关系,若查询包含 Include/ThenInclude,建议用 AsNoTrackingWithIdentityResolution() 代替,后者更适合处理树形结构或循环引用数据。
添加(Add/AddRange)、修改、删除(Remove/RemoveRange)等有 SaveChanges 操作的场景不能加 .AsNoTracking()。
查询时是否需要加 .AsNoTracking() 参考下表:
数据查询操作体系
├── 限定符 (Any, All) ✔️ 推荐
├── 聚合函数 (Count, Sum, Min, Max, Average) ❌ 不需要(但在分页逻辑中 Count 与 ToList 使用共同条件筛选时建议加)
├── 集合操作 (Distinct, Union) ⚠️ 推荐用于只读
├── 元素操作 (First, Single) ⚠️ 推荐用于只读
└── 即时执行操作 (ToList, ToArray, ToDictionary) ⚠️ 推荐用于只读