首页
关于
Search
1
C语言中的类型衰变
180 阅读
2
C#对接chatgpt
171 阅读
3
winform中combobox绑定数据源
162 阅读
4
反射异步调用
122 阅读
5
dynamic在反序列化中的用途
119 阅读
技术分享
后端分享
前端分享
福利活动
资源分享
天天向上
登录
/
注册
Search
标签搜索
C#
.NET
DotNet
异步
Sqlserver
Mysql
axios
vue
Javascript
RabbitMQ
反射
反序列化
dynamic
c
winform
ChatGpt
Stream
SSE
西街长安
累计撰写
13
篇文章
累计收到
45
条评论
首页
栏目
技术分享
后端分享
前端分享
福利活动
资源分享
天天向上
页面
关于
搜索到
10
篇与
的结果
2026-02-07
关于那消失的 8 小时:Axios 与数据库 datetime 的合谋
说实话,盯着代码看时差,比调 Bug 累多了。本来本地跑得好好的,17:00 点的数据存进数据库再取出来,直接变成了凌晨 01:00。这种 8 小时的凭空“瞬移”,常让我在屏幕前怀疑人生。如果你也踩过这个坑,别急着在前端手动 +8,那只会让逻辑变得更乱。这事儿得从头捋。自动转换的“冷箭”讲真,很多人觉得 Axios 是原样传输,其实它调了 JSON.stringify。当你的对象里包含 Date 类型时,JS 原型链里的 .toJSON() 方法会自动激活,效果等同于 .toISOString()。不管你本地电脑是什么时区,它都会被强行转成 UTC 零时区,结尾带个明显的 “Z”。本地: 2026-02-07 17:00 (GMT+8)传给后端时: "2026-02-07T09:00:00.000Z"这个“Z”,就是一切混乱的开端。数据库的“失忆症”后端接收到带“Z”的字符串,解析为 C# 的 DateTime(Kind 为 Utc)时还没出问题。麻烦出在写入 SQL Server 或 MySQL 的 datetime 字段时。datetime 就像一个只会记数字的记事本。 它只记下了 2026-02-07 09:00:00,至于时区,它根本没地方存。读取时,噩梦闭环了:后端读回数字,因为没有时区信息,C# 默认它为 Unspecified(未指定)。回传给前端时,JSON 变成了没有“Z”的字符串。浏览器看到没后缀的 ISO 字符串,默认按本地时间解析。于是,原本是 UTC 的 9 点,被当作了本地的 9 点。这就是消失的 8 小时。别再修补了,直接换零件别去纠结怎么计算时差。与其在业务代码里打补丁,不如从数据定义上直接解决。类型存储逻辑读取结果DateTime只存数字,丢弃偏移量时区全靠猜,极易出错DateTimeOffset存数字 + 偏移量(如 +00:00)原生支持时区,两端对齐最终建议:如果你不想改业务代码,最快的方法就是把数据库字段改成 DateTimeOffset。你会发现数据库里多出了 +00:00 的后缀,前端解析时再也不会产生“时空错位”。如果你非要守着 datetime 不放,那后端查出来后,必须手动执行 DateTime.SpecifyKind(time, DateTimeKind.Utc) 再返回,否则这锅你永远背不完。
2026年02月07日
7 阅读
0 评论
3 点赞
2026-02-05
谁偷了我的 SignalR 消息?—— 记一次 MassTransit 分布式“抢占”深坑排查
被坑惨了!SignalR 消息“随机消失”的灵异事件复盘兄弟们,最近在写 IM 系统的推送,被一个“玄学” Bug 折磨了好几天。那种“一下有、一下没有、断点还随缘命中”的感觉,真的差点让我怀疑人生。今天终于破案了,必须给大伙儿避避坑。01 诡异的“概率性”失灵在搞 IM 开发的时候,你有没有遇到过这种鬼故事:代码逻辑一模一样,入口 A 稳如泰山,入口 B 就“一下有一一下没有”。断点时而能断住,时而像被幽灵遮蔽了一样,程序直接滑过去。MQ 后台显示消息发了,也被人领走了,但你的控制台就是一片死寂。今天复盘的这个案例,就是因为 “多实例竞争消费” 导致的 SignalR 推送失灵。02 案发现场我的发消息入口很简单:就是一个 Web API Controller,接收前端 Post 过来的消息。但奇怪的是:只要是从这个 API 发出来的消息,大概率会失联,前端死活收不到推送。03 地毯式排查(全是血泪)怀疑点 A:AutoMapper 映射翻车?我先去翻了 RabbitMQ 的错误队列(_error),还真抓到了现行!报错显示 MessageCreatedEvent 转 MessageBaseVo 失败了。教训: MassTransit 消费端对字段类型很敏感。哪怕是 sbyte 和 Enum 这种微小差别,都能让映射直接崩溃。结果: 修正了 Profile 映射,报错没了,但消息消失的问题 依然存在。怀疑点 B:数据库事务“赛跑”?因为 API 逻辑里涉及不少更新(比如对话表、未读数),我怀疑是事务还没提交,MQ 消息就先发出去了,导致消费者去查库的时候出现了“幻读”。教训: 记得把 Publish 放在 transaction.Commit() 后面。结果: 顺序调对了,但推送还是“看心情”出现。怀疑点 C:真凶现身 —— 消失的 “Unacked”我盯着 RabbitMQ 后台看,发现消息进了一个叫 Unacked 的状态。这意思就是:消息已经发给某个程序了,但那程序领了之后没回信(Ack)。就在我盯着断点发呆的时候,我脑子里闪过一道闪电:卧槽,我是不是同时开了两个项目实例?04 真相大白:竞争消费者模式 (Competing Consumers)MassTransit 默认会让 Consumer 监听同名队列(比如 SignalREventHandler)。如果你在本地:VS 里开着调试,这是 项目 A。后台终端或者别的窗口还跑着一个没关掉的 项目 B。RabbitMQ 可不管谁是谁,它会觉得这是同一个服务的两个节点,于是玩起了 “轮询机制”:消息 1:发给了项目 A(你在调试的),断点中了,推送成功。消息 2:发给了项目 B(后台挂着的旧实例)。项目 B 默默消费了消息,但它没连着前端页面啊!消息直接进了黑洞。05 解决方案方案一:开发环境给队列“实名制”别让大家挤在一个队列里。给每个实例整一个唯一的队列名,把 Exchange 变成“广播”模式。cfg.ReceiveEndpoint($"SignalR-Queue-{Guid.NewGuid()}", e => { e.AutoDelete = true; // 程序一停,队列自动销毁,干净卫生 e.ConfigureConsumer<SignalREventHandler>(context); }); 方案二:生产环境上 Redis 底板多实例部署时,一定要配 SignalR Redis Backplane。这样不管消息被哪个节点抢走了,它都会通过 Redis 吼一嗓子,通知所有节点一起推送,确保用户不管连在哪都能收到。06 总结解决这种分布式 Bug 有三大法宝:看颜色: 给不同 Handler 的 Console.WriteLine 加上显眼的颜色,谁在干活一眼便知。看后台: RabbitMQ 的 Unacked 和 Consumers 数量永远不会骗你。清门户: 永远检查你身后是不是还开着一个“幽灵”实例。要是你也遇到了断点断不住的灵异事件,先别怀疑人生,去看看 RabbitMQ 里的消费者列表吧!
2026年02月05日
7 阅读
0 评论
1 点赞
2024-05-14
C#将列表转换为JS格式的数据
在编写API的时候后端将数据传给前端一般会将数据序列化为JS字符串格式,这样可以减少数据传输量,提高数据传输效率。引入Newtonsoft.Json命名空间,然后使用JsonConvert.SerializeObject方法可以将列表转换为JSON字符串。想要引用Newtonsoft.Json需要先去NuGet上下载,直接搜索Newtonsoft.Json第一个就是接下来看代码using Newtonsoft.Json;//引用Newtonsoft.Json命名空间namespace CS{public class User { public string Name; public string NiCk; } internal class Program { static void Main(string[] args) { List<User> users= new List<User>(); users.Add(new User() { Name = "姓名",NiCk = "昵称"}); users.Add(new User { Name = "用户", NiCk = "账号" }); string f = JsonConvert.SerializeObject(users);//这里使用JsonConvert.SerializeObject方法将列表转换为JS格式的字符串 List<User> d = JsonConvert.DeserializeObject<List<User>>(f);//将JS格式的字符串反序列化 } }}
2024年05月14日
63 阅读
0 评论
0 点赞
2024-01-23
C#委托和匿名方法
在C#中,委托是一种强大的类型,允许将方法作为参数传递,从而实现回调和事件处理等功能。本文将首先介绍委托的基础概念,然后深入探讨匿名方法和 Lambda 表达式,这两者是在C#中使用委托时的有力补充。{mtitle title="委托基础"/}using System; // 定义委托 delegate void MyDelegate(int x); class Program { static void Main() { // 创建委托实例并关联方法 MyDelegate myDelegate = new MyDelegate(PrintNumber); // 调用委托,实际上调用了关联的方法 myDelegate(42); } // 委托关联的方法 static void PrintNumber(int num) { Console.WriteLine("Number: " + num); } }上面的例子中,我们定义了一个委托 MyDelegate,它可以引用带有一个整数参数的方法。然后,我们创建了委托的实例,并将其关联到 PrintNumber 方法上,最终通过调用委托来执行方法。{mtitle title="匿名方法的应用"/}匿名方法是一种没有显式命名的方法,通常用于传递给委托或事件处理程序。它允许在需要时直接定义和使用方法,而无需为其命名。using System; class Program { delegate void MyDelegate(int x); static void Main() { // 使用匿名方法创建委托实例 MyDelegate myDelegate = delegate (int x) { Console.WriteLine("匿名方法被调用,参数为: " + x); }; // 调用委托 myDelegate(10); } } 在这个例子中,我们使用匿名方法创建了一个委托实例,它会在调用时输出参数的信息。{mtitle title="引出Lambda表达式"/}Lambda 表达式是C# 3.0及更高版本引入的一项功能,用于更简洁地表示匿名方法。它进一步简化了代码,使得处理委托变得更加方便。using System; class Program { delegate void MyDelegate(int x); static void Main() { // 使用 Lambda 表达式创建委托实例 MyDelegate myDelegateLambda = (int x) => { Console.WriteLine("Lambda 表达式被调用,参数为: " + x); }; // 调用委托 myDelegateLambda(20); // Lambda 表达式的更简洁形式 MyDelegate conciseLambda = x => Console.WriteLine("更简洁的 Lambda,参数为: " + x); conciseLambda(30); } } Lambda 表达式在语法上更为紧凑,使得代码更易读且更简洁。在处理简单的委托场景时,Lambda 表达式是更推荐的选择。{card-describe title="卡片描述"}通过委托、匿名方法和 Lambda 表达式,C# 提供了丰富的工具来处理方法引用和回调,使得代码更加灵活和可维护。在选择使用匿名方法或 Lambda 表达式时,可以根据代码的复杂性和可读性来做出合适的选择。{/card-describe}
2024年01月23日
78 阅读
1 评论
1 点赞
2024-01-23
C#中的Return Switch写法
C#中的Return Switch写法在C#中,处理条件分支时,我们经常会使用 switch 语句。在这篇博客文章中,我们将探讨传统的 switch 写法和一种更简洁、易读的 return switch 写法,并比较它们之间的区别和优势。原始写法首先,让我们看一下传统的 switch 写法:public static string GetDayOfWeek(int dayNumber) { string dayName = ""; switch (dayNumber) { case 1: dayName = "Sunday"; break; case 2: dayName = "Monday"; break; // ... 其他case ... default: dayName = "Invalid Day Number"; break; } return dayName; }在这个例子中,我们使用 switch 语句根据输入的 dayNumber 返回相应的字符串,处理了每个可能的情况,最后使用 default 处理了没有匹配的情况。引入Return Switch写法尽管上面的写法是有效的,但我们可以通过使用 return switch 表达式来使代码更加紧凑和可读。public static string GetDayOfWeek(int dayNumber) { return dayNumber switch { 1 => "Sunday", 2 => "Monday", // ... 其他case ... _ => "Invalid Day Number" }; }在这个写法中,我们直接在 return 语句中使用了 switch 表达式。每个情况都通过 => 关联一个结果,而 _则表示默认情况。区别与优势1. 简洁性原始写法: 需要声明变量,然后在每个 case 中赋值,最后返回该变量。Return Switch写法: 直接通过表达式返回结果,减少了冗余。2. 可读性原始写法: 使用 break 中断每个 case,需要额外的 default 处理默认情况。Return Switch写法: 表达式直观,减少了冗余语法,提高了可读性。3. 默认情况原始写法: 使用 default 处理没有匹配的情况。Return Switch写法: 使用 _表示默认情况,更加清晰。{callout color="#ef4d4d"}注意:此写法在C#8.0后才支持{/callout}总结通过使用 return switch 写法,我们可以更清晰、更简洁地处理条件分支,提高了代码的可读性。这种写法在处理简单的情况分支时特别有用,使代码更加优雅。在选择条件分支写法时,可以根据具体情况来考虑使用 return switch 的简便性和清晰性。
2024年01月23日
112 阅读
0 评论
1 点赞
1
2