当前位置: 首页 > news >正文

722:零侵入;DBG;

1

零侵入;注入

零侵入 = 不动别人的代码

常规做法(侵入式): 你要加功能,得去改 Program.cs、App.xaml.cs → 主程序代码被你改了 零侵入: 你写一个独立的库,放个实现了 IHostingStartup 的类 → 主程序一行不用动,自动发现并加载

注入 = 往里塞东西

// 在 Configure 里,你想加什么就加什么publicvoidConfigure(IWebHostBuilderbuilder){builder.ConfigureServices(services=>{services.AddSingleton<IMyService,MyService>();// 注入新服务});}

对比

侵入式: 零侵入 + 注入: 主程序 App.cs 你的插件.cs ┌──────────────┐ ┌────────────────┐ │ 改这行 │ │ IHostingStartup │ │ 加那行 ←── 你插进去的 │ Configure() │ │ 删第三行 │ │ 往里塞服务 │ └──────────────┘ └────────────────┘ ↓ 自动被主程序发现并执行

零侵入 = 不碰原代码;注入 = 把自己的东西塞进容器。两者配合,实现插件式扩展。

2

DBG
刚才所有日志前面的[DBG]=Debug(调试级别)

日志等级

[VRB] Verbose ─ 最啰嗦,什么都记 [DBG] Debug ─ 调试用,启动/初始化信息 [INF] Information ─ 常规信息,如 "Now listening on..." [WRN] Warning ─ 警告,可能有问题 [ERR] Error ─ 出错了 [FTL] Fatal ─ 致命错误,程序要挂

这个项目里

注册用的是 Serilog,配置了按级别输出:

[DBG] ≈ 给开发人员看的,用户看不见 [INF] ≈ 关键节点信息,如启动成功 [ERR] ≈ 需要关注的错误

所以之前那些带[DBG]的日志都是调试级别,正常运行时可能就不会输出,只在调试模式下才记录。不影响功能,也不代表有问题。

3

2026-07-21 15:13:25.556 [INF] Hosting environment: Production

人话

“当前运行环境:生产模式”

为什么是 Production

ASP.NET Core 默认就是Production,除非你显式设置了环境变量:

ASPNETCORE_ENVIRONMENT=Development → 开发模式(详细信息、开发者异常页) ASPNETCORE_ENVIRONMENT=Staging → 预发布 (不设置) → 默认 Production

对项目的影响

模式影响
Development开发者异常页、详细错误信息
Production(当前)不暴露内部错误详情、性能优化

跑在Production是合理的。

4

2026-07-21 15:13:25.556 [INF] Application started. Press Ctrl+C to shut down.

5

2026-07-21 15:13:25.891 [DBG] Creating DbConnection.
这是整个数据库操作的第一步——创建连接对象。

完整顺序回顾

15:13:25.891 Creating DbConnection ← 你这条(第1步:准备连接对象) 15:13:25.897 Created DbConnection (2ms) ← 连接对象创建完毕 15:13:25.903 Creating DbCommand ← 准备 SQL 命令 15:13:25.908 Initialized DbCommand (5ms) ← 命令就绪 15:13:25.917 Opening connection ← 真正打开连接 15:13:25.933 Opened connection ← 连上了
Create(创建) → Open(打开) → Execute(执行) ↑你在这里

创建连接对象 ≠ 打开连接。就像买了根网线 ≠ 插上去通了。Creating DbConnection是 new 了一个连接对象,16ms 后才真正Opening connection连数据库。
DbConnection

DbConnection= 跟数据库之间的那根线,EF Core 用它连 MySQL。

类比

DbConnection = 电话线 Creating DbConnection → 准备话机、扯好线 Opening connection → 拨号 Opened connection → 接通了,可以说话 Executing command → 说话(发 SQL) Close / Dispose → 挂电话

代码里你不需要手动管

EF Core 和连接池自动处理:

awaitusingvarcontext=await_factory.CreateDbContextAsync();// DbConnection 由 EF Core 内部创建和打开,你感知不到varresult=awaitcontext.Components.FindAsync(1);// 内部:获取连接 → 打开 → 执行 SQL → 返回结果 → 连接归还连接池(不是关闭)// await using 结束,DbContext 归还池

连接池

DbConnection 也不是用完就销毁的 ┌──────────────────────┐ │ 连接池 (MySQL) │ │ [空闲] [空闲] [空闲] │ ← 一直保持打开,下次直接用 └──────────────────────┘ 比每次都 new + open 快得多

DbContext 池(1024个)管理的是 C# 对象,DbConnection 的底层连接是 MySQL 的连接池在管,两个池各管各的。

6

2026-07-21 15:13:25.897 [DBG] Created DbConnection. (2ms).
连接对象创建完成,花了 2ms。

这 2ms 做了什么

new MySqlConnection("Server=localhost;Database=xray_v1;...") ↓ 检查连接字符串格式、初始化连接池引用 ↓ 打印:Created DbConnection (2ms)

此时连接还没打开(电话线准备好了但没拨号),16ms 后才Opening connection

891ms: Creating → 897ms: Created (2ms) → 917ms: Opening → 933ms: Opened 创建对象 创建完成 拨号 通了

就是确认一句:连接字符串没问题,对象 new 出来了。

7

2026-07-21 15:13:25.903 [DBG] Creating DbCommand for ‘ExecuteScalar’.
连接对象好了,现在准备 SQL 命令。

线准备好了 → 准备要说的话 Creating DbCommand = 拼好这句 SQL:"查一下这些表在不在"

下一步 908ms:Initialized DbCommand(拼好了)。

ExecuteScalar

前面解释过,快速回顾:

执行一条 SQL,只返回一个值。

不是返回多行表数据 → 不是 ExecuteReader 不是插入修改删除 → 不是 ExecuteNonQuery 就是一个值 → ExecuteScalar

这里查的是:"表存在吗?"→ 返回一个数字(4 代表有,0 代表没有)。

分开讲:

1. ExecuteScalar

是一种执行方式,不是 SQL 内容。

ExecuteScalar → 执行查询,只返回一个值(如:数字 4) ExecuteReader → 执行查询,返回多行数据 ExecuteNonQuery → 执行更新,返回影响行数

2. 检查表是否存在的 SQL

查询的内容,EF Core 内部自动生成的,类似:

-- EF Core 帮你写的,你不用管SELECT表数量FROMMySQL系统表WHERE表名IN(...)

3. EF Core 内部

你看不到的 EF Core 源码里的逻辑。

拼起来

EF Core 内部生成 SQL: "查一下那 4 张表在不在" ← 自动的,你不用写 ↓ 包装成 DbCommand,标记为 'ExecuteScalar' 类型 ← 执行方式:返回一个值 ↓ 发给 MySQL ← 查到结果:在/不在

ExecuteScalar = 返回单个值的执行方式。检查表是否存在的 SQL = EF Core 自动生成的查询内容。两回事,一个是怎么执行,一个是在查什么。

8

2026-07-21 15:13:25.907 [DBG] Created DbCommand for ‘ExecuteScalar’ (1ms).

命令对象包好了,耗时 1ms。下一步是连接数据库执行。
对象=DbCommand for ‘ExecuteScalar’ =检查表是否存在的 SQL
对,理解正确。

DbCommand for 'ExecuteScalar' │ ├── 包裹的 SQL = "这 4 张表在不在?" │ └── 执行方式 = ExecuteScalar(返回一个数字)

三者关系:

DbCommand 对象(信封) └── 里面装着:检查表是否存在的 SQL(信的内容) └── 执行类型标记:ExecuteScalar(只需要一个返回值)

9

2026-07-21 15:13:25.908 [DBG] Initialized DbCommand for ‘ExecuteScalar’ (5ms).
初始化完成,命令就绪可以执行。从创建 (903ms) 到初始化 (908ms) 共 5ms。
对。Created(创建好了)→Initialized(就绪,随时可以发)→Opening(开始连数据库)。Initialized就是摩拳擦掌准备上场的那一步。

10

[Parameters=[], CommandType=‘Text’, CommandTimeout=‘30’]
SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END
FROM information_schema.tables
WHERE table_type = ‘BASE TABLE’ AND table_schema = ‘_v1’
这条日志显示的是 EF Core 执行 SQL 的完整命令信息

[Parameters=[], CommandType='Text', CommandTimeout='30']

分解解释:

  • Parameters=[]— 参数为空。这条 SQL 不需要外部传参,表名'_v1'是直接写在 SQL 里的硬编码字符串。

  • CommandType=‘Text’— 命令类型是文本,也就是一条原始 SQL 字符串。EF Core 直接把这串 SELECT 语句发给 MySQL,不走存储过程。

  • CommandTimeout=‘30’— 超时时间 30 秒。如果这条查询 30 秒内没返回结果,就会抛超时异常。

下面的 SQL 就是上一轮讲过的:EnsureCreatedAsync 在检查_v1库里到底有没有表COUNT(*) = 0→ 没表就返回 TRUE(需要建表),有表返回 FALSE(啥也不干)。

简单说,就是 EF Core 启动时问 MySQL:“兄弟,_v1 库里有表吗?没有的话我帮你建。” 这条日志就是这次"询问"的完整记录。

EnsureCreatedAsync是什么

这是 EF Core 提供的一个方法,一句话概括:

“库和表不存在就自动建,已经存在就啥也不干。”


它怎么跑的

AppDbContextExtensions.cs注册了一个后台服务:

internalclassEnsureDatabaseCreatedHostedService:BackgroundService{protectedoverrideasyncTaskExecuteAsync(CancellationTokenstoppingToken){usingvarcontext=_dbContextFactory.CreateDbContext();// ← 从池子拿一个 DbContextawaitcontext.Database.EnsureCreatedAsync(stoppingToken);// ← 核心:自动建库建表}}

程序启动 → 这个服务自动运行 → 调用EnsureCreatedAsync


它干了什么(分步骤)

步骤做什么对应你看到的日志
1打开 MySQL 连接Opening connection to '_v1' on 'localhost'
2information_schema.tables,看库里有没有表SELECT ... FROM information_schema.tables WHERE table_schema = '_v1'
3a有表→ 直接结束,啥也不做日志就停了
3b没表→ 根据你的AppDbContext里定义的 4 个实体类,自动生成CREATE TABLE语句并执行你会看到一堆 CREATE TABLE 日志

和另一个方法MigrateAsync的区别

EnsureCreatedAsyncMigrateAsync
适用场景开发/小项目,不用迁移文件生产环境,用迁移文件管理版本
建库建表✅ 自动✅ 通过迁移文件
表已存在时啥也不干执行未应用的迁移
表结构变了不会更新,需要删库重建✅ 通过新迁移文件增量更新
本项目用哪个✅ 这个

你们项目注释也写了:"自动建表(EnsureCreated 适用于无迁移文件的场景)"


简单类比

就像你租了个空房子(MySQL 服务器),进门时:

  1. 先看看卧室厨房有没有家具(查information_schema.tables
  2. 没有 → 帮你搬进来(CREATE TABLE
  3. 已经有了 → 直接合租,不动你的东西

所以你日志里那条SELECT就是"看有没有家具"这一步。后续没看到CREATE TABLE,说明数据库之前已经建好了。

这是筛选条件,限定只统计"用户真正创建的表":


拆解

WHERE table_type = 'BASE TABLE' ← 只要"实体表" AND table_schema = '_v1' ← 只看 _v1 这个库

为什么加table_type = 'BASE TABLE'

MySQL 每个库里不只有你建的表,还有五花八门的东西:

table_type是什么举例
BASE TABLE你建的实体表recipescomponents
VIEW视图(虚拟表)查询结果伪装成的表
SYSTEM VIEW系统视图information_schema自己的表不算在内

如果不加这个过滤,COUNT(*)可能把视图也算进去。

举个例子:假设你的库是这样的:

xray_v1/ ├── recipes ← BASE TABLE(你建的) ├── components ← BASE TABLE(你建的) ├── my_view ← VIEW(视图,不算实体表)
  • 不加过滤:COUNT(*) = 3TRUE
  • 加了过滤:COUNT(*) = 2TRUE

EF Core 要确认的是有没有实体表需要它管,视图不是它建的,它不管,所以要过滤掉。


table_schema = '_v1'

锁定只看_v1这一个库,别把其他库的表算进来。information_schema.tables存的是整个 MySQL 服务器上所有库的所有表信息。

11

这是 X-Ray 设备上电启动的标准流程:


加载轴限位配置 → 读取运动轴的软/硬限位参数(轴能跑多远、别撞了) 通电 X-Ray 光管 → 给 X 射线源上电(核心成像部件,类似灯泡点亮) 扫图丢弃投影数 写入PLC → 告诉 PLC:扫描时开头丢掉几帧图像(去除不稳定帧) 进出板方向 写入PLC → 告诉 PLC:PCB 板从左进右出还是右进左出 Smema模式 写入PLC → 告诉 PLC:上下料通讯协议模式(SMEMA 是产线设备通讯标准) 正在连接复判站 Socket → 连到复判工位(检测完人工复核的那个工位)

整体流程

数据库 ✅ → 加载机械配置 → 点亮光管 → 发参数给 PLC → 连复判站

PLC 是设备的大脑,前面几步都在往 PLC 里写运行参数。光管、轴限位、进出板、SMEMA 这些都是实际物理硬件的初始化,说明程序已经从"软件准备"进入了"硬件就绪"阶段。

12

2026-07-21 15:13:27.471 [INF] [SupXDriver] 初始化,开始
2026-07-21 15:13:27.765 [INF] [SupXDriver] 搜索到1个设备,使用设备0
2026-07-21 15:13:27.767 [INF] [SupXDriver] 校准中,请稍后…
2026-07-21 15:13:33.260 [INF] [SupXDriver] 校准结束
2026-07-21 15:13:33.261 [INF] [SupXDriver] 初始化,成功
光管驱动初始化,一次教科书级别的硬件自检流程:


初始化,开始 → 驱动启动 搜索到1个设备,使用设备0 → 扫描到 1 个 X-Ray 探测器/光管,选第 0 个(第一个) 校准中,请稍后... → 自动校准(探测器跟光管对位、参数自整定) 校准结束 → 校准完成,花了约 5.5 秒 初始化,成功 → 驱动就绪

关键信息

  • 搜索到 1 个设备— 说明硬件连接正常,驱动能找到探测器。如果这条日志说"搜索到 0 个设备",那就是硬件线没插或驱动没装。
  • 校准 5.5 秒(27.767 → 33.260)— 这是探测器在做自动校准,比如暗场校正、增益校准之类的,确保图像质量正常。
  • 为什么硬件初始化比连数据库慢?数据库操作是纯 CPU + 网络,毫秒级。硬件校准是真物理设备在跑,秒级,5.5 秒非常正常。

整体启动时序已经推进到:

数据库 ✅ → PLC 参数 ✅ → 光管通电 ✅ → 探测器校准 ✅

硬件层基本就绪,接下来就该加载业务模块了。

13

2026-07-21 15:13:33.264 [INF] 图像服务初始化完成,相机读取任务已启动
2026-07-21 15:13:33.266 [INF] [APP] 自动清理过期文件…
两步收尾动作:


图像服务初始化完成,相机读取任务已启动

探测器校准完,图像采集服务正式跑起来了。"相机读取任务已启动"说明后台起了一个持续循环任务,不断从探测器拿图像数据,有板子进来就采图。

[APP] 自动清理过期文件...

打扫卫生——把过期的日志、临时图片、缓存文件删掉,免得硬盘撑爆。


当前启动进度

数据库 ✅ → PLC 参数 ✅ → 光管通电 ✅ → 探测器校准 ✅ → 图像采集启动 ✅ → 清理临时文件...

到这里硬件和基础服务基本全部就绪,设备处于待机状态,就等 PCB 板进来触发检测流程了。

http://www.cnnetsun.cn/news/3604824.html

相关文章:

  • ARM Cortex-M4硬故障与MPU实战:从崩溃定位到内存隔离
  • Vue Vite开发者大会2026
  • B2B外贸询盘管理白皮书:询盘接收·智能回复·客户跟进全流程
  • 2026化工厂人员定位系统怎么选?这5个维度的排名比品牌更重要
  • iPhone17护眼钢化膜怎么选?悟赫德三大黄金标准避坑指南
  • RAG技术:解决LLM幻觉问题的关键实践
  • AI工具全景地图:200+实用工具分类与选型指南
  • AI微调技术:从通用模型到行业专家的关键路径
  • 第35讲:Vibe模式多轮对话管理——外设单独迭代、互不干扰
  • CDGA|夯实数据供给与可信治理 激活数据要素内生价值
  • 如何用嘎嘎降AI处理统计学论文:统计学毕业论文降AI4.8元知网达标完整操作教程
  • 心理学论文降AI工具免费推荐:2026年心理学毕业论文降AI99.26%达标知网完整指南
  • 双分支残差网络在低光照图像增强中的应用与优化
  • 数字人口型同步技术已进入毫秒级军备竞赛:2024最新Benchmark对比(Whisper+FaceFormer+NeRF-Lips实测数据)
  • 【限时解密】头部MCN机构内部AI视频工具评分矩阵(含23项硬指标):不看广告,只看CUDA核心利用率与重渲染失败率
  • 深入解析GPIO寄存器设计:从基础概念到ARM Cortex-M实战配置
  • 三角形是怎么变成“一格格像素“的?——揭秘光栅化
  • 【JAVA毕设源码分享】基于JAVA的情绪宣泄平台的设计与实现(程序+文档+代码讲解+一条龙定制)
  • 自适应多步前瞻解码:提升扩散语言模型生成效率与质量
  • Tiva™ TM4C123BH6ZRB引脚功能表深度解析与高效配置实战
  • 大模型学习指南:从真实需求出发,一步步掌握核心技能(收藏版)
  • Tiva C系列MCU HIB模块超低功耗休眠与唤醒实战指南
  • CDN与边缘计算:普通人参与的分布式网络革命
  • 工业SerDes技术解析:从嵌入式时钟到信号调理的远距离高速传输实战
  • 【iOS】3G-Share仿写总结
  • DS92LV8028串行器芯片:8通道LVDS高速传输与内置BIST测试实战解析
  • 迅雷盘获取直链解析,在线操作就能满速下载
  • UTM虚拟机:跨架构虚拟化技术与移动生产力实践
  • 涨薪技术|项目构建工具Maven使用教程
  • 079、STM32Cube.AI的异常检测案例