博客 (211)

方法一:打开 事件查看器-> Windows 日志-> 应用程序。

QQ20251223-081955.png


方法二:启用ASP.NET Core Module Stdout 日志。

修改网站根目录下的 web.config 文件,在 <aspNetCore> 节点中启用标准输出日志。你需要先手动创建 logs 文件夹并确保有写入权限。

<aspNetCore processPath="dotnet" 
            arguments=".\YourAppName.dll" 
            stdoutLogEnabled="true" 
            stdoutLogFile=".\logs\stdout" 
            hostingModel="inprocess" />

重现错误后,即可在 logs 目录下查看生成的日志文件。注意:出于性能考虑,问题解决后建议将 stdoutLogEnabled 设为 false。

xoyozo 半个月前
346

今天发现在 .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 编码。编码不一致是导致乱码和签名失败最常见的原因之一 。

xoyozo 1 个月前
443

今天遇到一个向基于 .NET Framework 框架开发的 Web 网站 POST 数据响应 405 Method Not Allowed 的问题。

服务端接口地址是 https://***/api/abc/Default.aspx,

若请求 https://***/api/abc/Default.aspx 或 https://***/api/abc/ 则正常 ,

若请求 https://***/api/abc 就会出现这个问题。

原因可能是服务器可能返回 301 重定向到 https://***/api/abc/Default.aspx,

导致最终接口接受到的请求方法不是 POST 。

xoyozo 2 个月前
511

使用 Linq 语法调用数据库时,需要包含导航属性(外键表数据)会用到 Include 方法,但是如果引用的程序集搞错了,就不会有数据输出,应该:

using Microsoft.EntityFrameworkCore;

而不是

using System.Data.Entity;

xoyozo 2 个月前
271

在 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 环境中生效,你可以视自身情况作判断。

xoyozo 4 个月前
3,994

.NET 项目发布到 Linux / CentOS / nginx 上,调用 RedirectToAction 方法跳转到 127.0.0.1:443,而不是正在访问的域名,怎么办?

打开该网站的 nginx 配置文件,找到反射代理配置(proxy_pass),将:

proxy_set_header Host 127.0.0.1:$server_port;

改为

proxy_set_header Host $host;


xoyozo 4 个月前
3,772

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 代码签名”证书,注意:“代码签名证书”与“域名证书”互不通用。

xoyozo 5 个月前
6,655

VS2019 的“查找和替换”功能,即使文件类型为 !*\bin\*;!*\obj\*;!*\.*,仍然会在 obj 目录中的文件中查找内容。

解决方法:将查找范围由“整个解决方案”改为“当前项目”。

image.png

xoyozo 7 个月前
455

AsNoTracking 设置未追踪查询

var customers = dbContext.Customers.AsNoTracking().ToList();

这对于只读查询非常有用,因为它可以减少内存使用并提高性能,因为它不需要维护实体的更改跟踪信息。

何时需要 AsNoTracking()?


ExecuteDelete 和 ExecuteUpdate 批量操作

context.Logs.Where(c => c.Time < new DateTime(2000, 1, 1)).ExecuteDelete();

从 EF Core 7 开始,ExecuteDelete 和 ExecuteUpdate 是官方原生支持的批量操作方法。直接操作数据库,不需要调用 SaveChanges():不加载实体到内存,减少内存消耗和网络往返。

若需要分页删除和大批量插入,或在高频、大规模场景,推荐使用 Zack.EFCore.Batch

context.Logs.Where(c => c.Time < new DateTime(2000, 1, 1)).DeleteRangeAsync(batchSize: 1000);


考虑用 Union 代替 OR

// Where 后行数多时(如分页前)用 OR
var q = db.dt_crm__contract.AsNoTracking();
q = q.Where(c => c.dt_crm__customer.SalesmanId == uid || myIns.Contains(c.IndustryId));

// 用于合并的 q1、q2 的行数少时用 Union
var q1 = db.dt_crm__contract.AsNoTracking().Where(c => c.dt_crm__customer.SalesmanId == uid);
var q2 = db.dt_crm__contract.AsNoTracking().Where(c => myIns.Contains(c.IndustryId));
var q = q1.Union(q2);


“ToDictionary + Count”之前先 Select

// 不推荐
var dic = q.GroupBy(c => c.Date)
    .ToDictionary(k => k.Key, v => v.Count());
// 推荐
var dic = q.GroupBy(c => c.Date)
    .Select(g => new { g.Key, Count = g.Count() })
    .ToDictionary(k => k.Key, v => v.Count);



xoyozo 8 个月前
996

image.png

现象:

VS2022 的“管理 nuget 程序包”页面的“更新”和“已安装”选项卡无限循环加载刷新,状态栏一直循环显示“正在还原 NuGet 程序包”、“就绪”。点开来有很多“正在加载 IntelliSense 任务已成功完成”。我有很多解决方案和项目,只有其中一个出现上述情况。

解决方法:

image.png

点击“设置”,在“常规”页面点击“清除所有 NuGet 存储”。

image.png

xoyozo 8 个月前
624