【Hi.Ltd.SqlServer 专题】第1:概述、双入口与线首工单通

Hi.Ltd.SqlServer 专题 · 精简加深版第 1 期|合并旧稿第1+2期
NuGet:Hi.Ltd.SqlServer 2026.9.7.1642
目标框架:net462 / net481 / net6.0-windows7.0 / net8.0-windows7.0
依赖:Hi.Ltd(Result 拼写 Successed,载荷 Content)、Microsoft.Data.SqlClient(≥7.0.2);Windows TFM 另含 SMO
读者:工控上位机 / HMI 宿主 / 配方·工单·报警·班次 / 采集落库 / MES 线边库


本期目标

把一条产线从「开机连库」走到「工单落库可查」整条通路钉死在 2026.9.7.1642 公开面上:

  1. 分清 SqlEntities(显式实例)Factory(全局句柄 + 模型同步 + 自愈 CRUD) 两条入口,知道各自该用在什么工位。
  2. 所有连接 / DDL / DML 按 XML 使用 Result / Result<T>,成败看 Successed(不是 Succeeded),业务值看 Content
  3. ConfigureSqlServerLoadModelAssemblyInitialSqlServer(或最小路径 CreateDatabase + CreateTable + InsertTable)完成首条工单写入。
  4. 理解 [Auto] 何时适合原型热补结构、何时必须改成部署期一次同步;知道 SchemaSyncOptions 生产默认该怎么关风险开关。
  5. 对照「手写 ADO / 散落 SQL」看清本包在上位机场景里真正省掉的样板与风险点。

读完你应能在 WinForm / WPF / 触摸屏工控机项目里,复制本期完整样例,改连接参数后直接跑通「建库 → 建工单表 → 插入 → Inqure 回读」。


适用范围

场景为什么适合 Hi.Ltd.SqlServer
WinForm / WPF / 嵌入式触摸屏上位机落库POCO + 特性映射,建表/CRUD/TVP/过程一体,少写 ADO 样板
配方、工单、报警、班次、采集点位元数据后续可用内置 Tables.Ics.* / Tables.Sys.*;本期先自建 POCO 跑通
与 MES / 线边库共用 SQL ServerFactory 多 Profile、InitialSqlServer / SchemaStartupAdvisor 适合部署对齐
需要批量采集落库公开入口是 InsertTables(复数),可配 SqlBulkInsertSettings
进程内希望句柄失效自动重建Factory.Insert / Query 等带 ExecuteSafe 自愈重试

不该用 / 慎用

场景说明
当完整 ORM / 变更迁移生态AlterTable / SyncTableSchemaWhenExists 有风险;生产结构变更要走评审 + 备份
毫秒级时序库 / 海量原始采样曲线联查默认物化为 List;超大数据请分页或 ExecuteReader 流式(后续期展开)
InitialSqlServer 当每次扫码热路径全量同步是部署/班次切换级操作,不是工单扫码级
抄 README 的 bool connected = sql.Connection()XML:ConnectionResult<bool>,必须判 Successed 再看 Content
跳过权限与备份的「一键 Drop」DropDatabase / DropTable 不可逆

工控故事线:A 线早班开工单

假设你在做 A 产线上位机。早晨班长在 HMI 点「开工」,系统要:

  1. 确认能连上线边 SQL Server(192.168.10.20\SQL2019,库名 LineA_Mes)。
  2. 确认工单表、报警表结构与程序集里的 POCO 一致(新版本可能多了「计划产量」「班次码」列)。
  3. 写入一条工单:WO-20260920-A001,产品 SKU-泵体-01,计划量 1200,班次 早班
  4. 操作员在「在制工单」画面能立刻查到这条记录。
  5. 若中午 SQL 服务重启导致连接句柄异常,下午扫码入库不应整机崩掉——至少 Factory 路径要能自愈重试一次。

这条故事线贯穿本期:先讲 Result 约定与双入口,再讲 Factory 启动骨架与 Auto,最后用完整可运行样例把「首条工单」落库。

现场还有 B 线共用同一套上位机程序、不同库——这就引出 多连接 Profile;配方表希望首次写入自动补表——引出 [Auto];结构漂移要先报告再改——引出 SchemaSyncOptions.SyncTableSchemaWhenExists=false 与顾问(第6期深化)。


怎么用

0. 安装引用

Install-Package Hi.Ltd.SqlServer -Version 2026.9.7.1642
dotnet add package Hi.Ltd.SqlServer --version 2026.9.7.1642
<PackageReference Include="Hi.Ltd.SqlServer" Version="2026.9.7.1642" />
using Hi.Ltd;          // Result / Successed / Content
using Hi.Ltd.SqlServer;

TFM 提醒:net6.0-windows7.0 / net8.0-windows7.0 带 Windows Forms 框架引用与 SMO;纯控制台也能引用,但包面向 Windows 工控宿主。

1. Result 约定

本包大量 API 的 XML returns 写明返回 Result / Result<T>(依赖 Hi.Ltd):

  • 成功执行r.Successed == true
  • 业务载荷:在 Content(部分 XML 措辞仍写 Value,与 Result<T>.Content 同属载荷侧;本系列示例统一用 Content
  • 失败Successed == false,看 Message(可再接 Logging)
  • 拼写是 Successed(少一个 e),写成 Succeeded 会编译失败

Result<bool> 还要分两层:

情况含义上位机该怎么做
Successed=false执行层失败(异常被包装进 Result)Message,记日志,禁止继续 DDL/DML
Successed=trueContent=false命令跑完,但结论为「未通过/未连上」同样当失败处理,不要只看 Successed
Successed=trueContent=true真正可用进入业务
Result<bool> conn = sql.Connection();
if (!conn.Successed)
{
    $"SQL 连接执行失败: {conn.Message}".Error();
    return;
}
if (!conn.Content)
{
    "SQL 连接测试未通过(Content=false)".Warn();
    return;
}
"SQL 连接 OK".Info();

2. 最小路径:SqlEntities 显式实例(适合单机调试 / 测试隔离)

SqlEntities 提供两个入口(XML):

成员说明
SqlEntities.Create公共静态只读字段,默认单例式入口
SqlEntities.CreateNew()每次新建实例;测试隔离或 Factory 句柄替换时用

自行持有引用时:没有 Factory 的 ExecuteSafe 重建包装——句柄生命周期与异常由你负责。

var sql = SqlEntities.CreateNew();
sql.DataSource = @"192.168.10.20\SQL2019";
sql.InitialCatalog = "LineA_Mes";
sql.UserId = "hmi_line_a";           // 不填则走 Windows 身份验证路径
sql.Password = Environment.GetEnvironmentVariable("LINEA_SQL_PWD");
sql.Timeout = 15;
sql.Encrypt = false;                 // 现场内网常关;跨网段按规范开 TLS
sql.TrustServerCertificate = true;   // 仅明确信任自签时

Result<bool> conn = sql.Connection();
if (!conn.Successed || !conn.Content)
    throw new InvalidOperationException(conn.Message);

Result<bool> db = sql.CreateDatabase("LineA_Mes");
if (!db.Successed) throw new InvalidOperationException(db.Message);

Result<bool> table = sql.CreateTable<WorkOrderRow>("LineA_Mes");
if (!table.Successed) throw new InvalidOperationException(table.Message);

Result<bool> ins = sql.InsertTable(new WorkOrderRow
{
    OrderCode = "WO-20260920-A001",
    ProductCode = "SKU-泵体-01",
    PlanQty = 1200m,
    ShiftCode = "早班",
    CreatedAt = DateTime.Now
});
if (!ins.Successed || !ins.Content)
    throw new InvalidOperationException(ins.Message);

// 批量采集请用 InsertTables(复数),不要用 README 里的 InsertTable(List)
// Result<bool> bulk = sql.InsertTables(list);

对应 POCO(特性全部来自 Hi.Ltd.SqlServer 命名空间,不要混当成 EF 同名特性「一定等价」):

[Table("WorkOrder", Schema = "dbo")]
public class WorkOrderRow
{
    [Key]
    public int Id { get; set; }

    [Required, MaxLength(64)]
    public string OrderCode { get; set; }

    [MaxLength(64)]
    public string ProductCode { get; set; }

    [Decimal(18, 4)]
    public decimal PlanQty { get; set; }

    [MaxLength(32)]
    public string ShiftCode { get; set; }

    public DateTime CreatedAt { get; set; }
}

DLL 校正提醒

  • MaxLength 无参时 默认长度 500(XML 原文;README 写 50 的以 DLL 为准)。短码字段请显式 [MaxLength(64)]
  • 查询方法名是 InqureTable(不是 Inquire)。
  • 批量插入方法名是 InsertTables(复数)。
  • 认证相关枚举名是 IntergratedSecurity(少字母 g)。

3. 进阶路径:Factory 全局句柄 + InitialSqlServer

适合:进程内单例访问、启动对齐库表、与 [Auto] 配合、多产线 Profile、希望 CRUD 句柄自愈。

Factory.ConfigureSqlServer(c =>
{
    c.DataSource = @"192.168.10.20\SQL2019";
    c.InitialCatalog = "LineA_Mes";
    c.UserId = "hmi_line_a";
    c.Password = Environment.GetEnvironmentVariable("LINEA_SQL_PWD");
    c.Encrypt = false;
    c.TrustServerCertificate = true;
    c.Timeout = 15;
});

Factory.LoadModelAssembly(typeof(WorkOrderRow).Assembly);

var options = new SchemaSyncOptions
{
    EnsureDatabase = true,
    EnsureTable = true,
    EnsureTableType = true,
    EnsureMergeProcedure = true,
    EnsureForeignKeys = true,
    SyncTableSchemaWhenExists = false, // 生产建议先关,改用顾问报告
    ValidateModelsBeforeSync = true,
    // StandardBuiltInTables = StandardTable.AllIcs // 需要内置工控表时再开
};

Result<SchemaSyncReport> sync = Factory.InitialSqlServer(typeof(WorkOrderRow).Assembly, options);
if (!sync.Successed)
{
    $"结构同步失败: {sync.Message}".Error();
    return;
}
SchemaSyncReport report = sync.Content;
if (report != null && !report.Successed)
{
    // 外层 Result 成功,但报告标记业务级未完全收敛(如 TVP/过程漂移)
    $"同步告警: {report}".Warn();
}

Result<bool> conn = Factory.SqlServer.Connection();
if (!conn.Successed || !conn.Content)
{
    $"连接失败: {conn.Message}".Error();
    return;
}

Result<bool> ins = Factory.Insert(new WorkOrderRow
{
    OrderCode = "WO-20260920-A001",
    ProductCode = "SKU-泵体-01",
    PlanQty = 1200m,
    ShiftCode = "早班",
    CreatedAt = DateTime.Now
});
if (!ins.Successed)
{
    $"工单插入失败: {ins.Message}".Error();
    return;
}

Result<List<WorkOrderRow>> q = Factory.Query(new WorkOrderRow { OrderCode = "WO-20260920-A001" }, fuzzy: false);
if (q.Successed)
{
    foreach (var row in q.Content)
    {
        // 绑定到 HMI 在制工单列表
    }
}
Factory 关键 API(公开面摘要)
API作用
Factory.SqlServer全局 ISqlServer;内部 EnsureSqlServer(),必要时新建并应用连接快照
ConfigureSqlServer / SetSqlServer / Initialize配置或替换句柄并捕获快照
UseSqlEntitiesCreate恢复为与 SqlEntities.Create 对齐的句柄
LoadModelAssembly / ModelTypes登记 POCO
Synchronize / InitialSqlServerResult<SchemaSyncReport>
Insert / Update / Delete / Query + *AsyncResult 包装;句柄失效时重建重试一次
InsertTables / InsertTablesAsync(Factory 侧亦有明确批量入口)避免与 InsertAsync(List) 歧义
RegisterProfile / SetDefaultProfile / With / ResolveSqlServer多连接 Profile
EmitStartupAutoHint启动期 Auto 提示
ClearProfiles / RemoveProfile / ProfileNames / DefaultProfileProfile 管理

Initialize vs InitialSqlServer:前者侧重替换/初始化句柄;后者 = 加载模型 + Synchronize,返回 Result<SchemaSyncReport>。不要混名。

4. 多连接 Profile(A 线 + B 线)

Factory.RegisterProfile("LineA", c =>
{
    c.DataSource = "10.0.0.11";
    c.InitialCatalog = "LineA_Mes";
    c.UserId = "u_a";
    c.Password = "p_a";
    c.Encrypt = false;
});
Factory.RegisterProfile("LineB", c =>
{
    c.DataSource = "10.0.0.12";
    c.InitialCatalog = "LineB_Mes";
    c.UserId = "u_b";
    c.Password = "p_b";
    c.Encrypt = false;
});
Factory.SetDefaultProfile("LineA");

using (FactoryProfileScope scope = Factory.With("LineB"))
{
    // 作用域内走 LineB
    Factory.Insert(new WorkOrderRow { OrderCode = "WO-B-001", ProductCode = "SKU-X", PlanQty = 100m, ShiftCode = "早班", CreatedAt = DateTime.Now });
}

// 模型也可标注默认 Profile(特性详解见第2期)
// [SqlProfile("LineA"), Table("WorkOrder")]

RegisterProfile / SetDefaultProfile 本身也返回 Result——Profile 名拼错时先判 Successed,不要假设一定成功。

5. [Auto] 自动托管(原型友好,生产要克制)

XML:AutoAttribute —— 标记表模型由运行期自动托管结构工件(库/表/类型/过程/外键)。仅在与 [Table] 同时存在时生效;只有 [Auto] 没有 [Table] 会被忽略。

[Table("AlarmRecord", Schema = "dbo"), Auto]
public class AlarmRecordRow
{
    [Key] public int Id { get; set; }
    [Required, MaxLength(64)] public string AlarmCode { get; set; }
    [MaxLength(256)] public string AlarmText { get; set; }
    public DateTime AlarmTime { get; set; }
}

效果要点:

  1. InsertTable / UpdateTable / DeleteTable / InqureTable(及对应 Async)路径会 EnsureAutoArtifacts(库/表/TVP/MERGE 过程/外键等,受内部 Auto 选项约束)。
  2. Factory.Insert / Query 落到同一套 API,行为一致。
  3. 全量 Synchronize / InitialSqlServer 成功后会 AutoTableRegistry.InvalidateEnsured(),下次 DML 重新校验 Auto 就绪状态。
  4. 默认自愈策略与 AutoSelfHealPolicy 相关(如 AddMissing);DML 遇 schema 漂移 SqlException 时是否自愈重试,由策略与安全门控制。

推荐分工:

  • 部署期一次对齐:关键业务表不加 Auto,启动 InitialSqlServer,结构变更走评审。
  • 原型 / 临时报警缓冲表[Table, Auto] + 程序集已 LoadModelAssembly
  • 禁止:所有表都 Auto,又打开 SyncTableSchemaWhenExists——难审计、难回滚。

6. SchemaSyncOptions 常用开关(部署清单)

属性含义生产建议
EnsureDatabase / LazyCreateDatabase库是否确保 / 懒创建线边库可开 Ensure;中央库慎自动建
EnsureTable缺表则建部署期可开
SyncTableSchemaWhenExists已存在时纠偏列默认建议 false,先顾问报告
EnsureTableType / EnsureMergeProcedure / EnsureForeignKeysTVP / 过程 / FK用到 MERGE/TVP 再开
StandardBuiltInTablesStandardTable 掩码需要内置 Ics/Sys 时再设
IncludeOnlyTableNames / ExcludeTableNames / TypeFilter / IncludeNamespacePrefixes / AutoOnly筛选大程序集务必筛选
ValidateModelsBeforeSync同步前 POCO 校验建议 true
ColumnTypeMismatch类型不一致策略按变更流程选型

外层 Result.Successed=trueSchemaSyncReport.Successed=false 很常见:表已演进,TVP/过程仍是旧版。从 2026.4.21 起 UpdateTableType 会先删引用过程再重建;升级旧库建议先全量同步再进 DML(TVP 细节见第5期)。

7. 回读工单:Inqure(拼写勿「纠正」)

Result<List<WorkOrderRow>> list = Factory.SqlServer.InqureTable(new WorkOrderRow
{
    OrderCode = "WO-20260920-A001"
}, fuzzy: false);

if (!list.Successed)
{
    $"查询失败: {list.Message}".Error();
    return;
}
var rows = list.Content ?? new List<WorkOrderRow>();

公开 API 就是 Inqure。写成 Inquire 会找不到成员——这不是文档笔误,是 DLL 成员名(拼写错误,后续批量纠正)。


手写对照优势

维度手写 ADO / 散落 SQLHi.Ltd.SqlServer 2026.9.7.1642
连接自检SqlConnection.Open + try/catch 满天飞Connection()Result<bool>,统一 Successed/Content/Message
建库建表手写 IF DB_ID / CREATE TABLE 脚本,易与 POCO 漂移CreateDatabase / CreateTable<T>InitialSqlServer
工单插入参数拼装、列顺序、自增回填各写一套InsertTable / Factory.Insert;批量 InsertTables
结构升级运维脚本与程序版本两套真相模型程序集 + SchemaSyncOptions(仍要评审,但真相源在代码)
句柄失效自己捕获 ObjectDisposed,自己重建连接Factory.* CRUD 捕获可恢复异常并重建重试一次
多产线多套连接字符串散落配置RegisterProfile + With + [SqlProfile]
原型补表忘建表就运行时炸[Auto] 热路径 Ensure(生产仍建议部署期同步)
统一失败语义有的抛、有的返回 bool、有的返回 -1公开面统一 Result;拼写固定为 Successed

本包不是要替代 DBA 的变更管理,而是让上位机团队把「落库工具箱」收敛到一套公开 API,减少现场「脚本与 exe 版本对不上」的事故面。


完整可运行样例(可抄进项目)

下面示例假设:本地或线边已有 SQL Server;账号有建库建表权限;用 SqlEntities 最小路径演示首条工单,再用 Factory 演示启动同步与查询。演示密码请换成现场密钥/环境变量。

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Hi.Ltd;
using Hi.Ltd.SqlServer;

namespace LineA.Hmi
{
    [Table("WorkOrder", Schema = "dbo")]
    public class WorkOrderRow
    {
        [Key]
        public int Id { get; set; }

        [Required, MaxLength(64)]
        public string OrderCode { get; set; }

        [MaxLength(64)]
        public string ProductCode { get; set; }

        [Decimal(18, 4)]
        public decimal PlanQty { get; set; }

        [MaxLength(32)]
        public string ShiftCode { get; set; }

        public DateTime CreatedAt { get; set; }
    }

    [Table("AlarmRecord", Schema = "dbo"), Auto]
    public class AlarmRecordRow
    {
        [Key]
        public int Id { get; set; }

        [Required, MaxLength(64)]
        public string AlarmCode { get; set; }

        [MaxLength(256)]
        public string AlarmText { get; set; }

        public DateTime AlarmTime { get; set; }
    }

    public static class LineBootstrap
    {
        public static bool Boot(out string message)
        {
            // —— 路径 A:显式实例自检 + 建库建表 + 首条工单 ——
            var sql = SqlEntities.CreateNew();
            sql.DataSource = "(local)";
            sql.InitialCatalog = "LineA_Mes";
            sql.UserId = "sa";
            sql.Password = "***"; // 演示;产线用环境变量/密钥库
            sql.Timeout = 15;
            sql.Encrypt = false;
            sql.TrustServerCertificate = true;

            Result<bool> conn = sql.Connection();
            if (!conn.Successed)
            {
                message = "连接执行失败: " + conn.Message;
                return false;
            }
            if (!conn.Content)
            {
                message = "连接测试未通过: " + conn.Message;
                return false;
            }

            Result<bool> db = sql.CreateDatabase("LineA_Mes");
            if (!db.Successed)
            {
                message = "建库失败: " + db.Message;
                return false;
            }

            Result<bool> tb = sql.CreateTable<WorkOrderRow>("LineA_Mes");
            if (!tb.Successed)
            {
                message = "建工单表失败: " + tb.Message;
                return false;
            }

            Result<bool> ins = sql.InsertTable(new WorkOrderRow
            {
                OrderCode = "WO-20260920-A001",
                ProductCode = "SKU-泵体-01",
                PlanQty = 1200m,
                ShiftCode = "早班",
                CreatedAt = DateTime.Now
            });
            if (!ins.Successed || !ins.Content)
            {
                message = "工单插入失败: " + ins.Message;
                return false;
            }

            // —— 路径 B:注入 Factory,做模型同步与查询 ——
            Factory.SetSqlServer(sql);
            Factory.LoadModelAssembly(typeof(WorkOrderRow).Assembly);

            var options = new SchemaSyncOptions
            {
                EnsureDatabase = true,
                EnsureTable = true,
                EnsureTableType = false,
                EnsureMergeProcedure = false,
                EnsureForeignKeys = false,
                SyncTableSchemaWhenExists = false,
                ValidateModelsBeforeSync = true
            };

            Result<SchemaSyncReport> sync = Factory.InitialSqlServer(typeof(WorkOrderRow).Assembly, options);
            if (!sync.Successed)
            {
                message = "InitialSqlServer 失败: " + sync.Message;
                return false;
            }

            Result<List<WorkOrderRow>> q = Factory.Query(
                new WorkOrderRow { OrderCode = "WO-20260920-A001" },
                fuzzy: false);
            if (!q.Successed)
            {
                message = "查询失败: " + q.Message;
                return false;
            }

            // Auto 表:首次写入触发确保(原型场景)
            Result<bool> alarm = Factory.Insert(new AlarmRecordRow
            {
                AlarmCode = "E001",
                AlarmText = "进料压力低",
                AlarmTime = DateTime.Now
            });
            if (!alarm.Successed)
            {
                message = "报警写入失败: " + alarm.Message;
                return false;
            }

            message = "OK,工单与报警通路已通,查询行数=" + (q.Content?.Count ?? 0);
            return true;
        }
    }

    // MainForm_Load 示例
    public partial class MainForm : Form
    {
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            if (!LineBootstrap.Boot(out var msg))
            {
                MessageBox.Show(msg, "数据库启动", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
            {
                // 正常进入班次/工单画面
            }
        }
    }
}

采集批量落库补充(同一 sql 实例):

var batch = new List<WorkOrderRow>();
for (int i = 0; i < 100; i++)
{
    batch.Add(new WorkOrderRow
    {
        OrderCode = $"WO-BATCH-{i:D4}",
        ProductCode = "SKU-泵体-01",
        PlanQty = 10m,
        ShiftCode = "早班",
        CreatedAt = DateTime.Now
    });
}
Result<bool> bulk = sql.InsertTables(batch); // 复数!
if (!bulk.Successed || !bulk.Content)
    throw new InvalidOperationException(bulk.Message);

避坑清单

  1. README 仍写 bool Connection()
    2026.9.7.1642 XML:ISqlServer.ConnectionResult<bool>。照 README 抄会编译不过或误判。

  2. Successed 拼成 Succeeded
    Hi.Ltd 既定拼写。全系列、全项目统一搜一遍。

  3. 只判 Successed 不看 Content(对 Result<bool>
    连接测试「执行成功但连不上」会漏掉。

  4. InqureTable / Visiable / IntergratedSecurity / ICreateProcdure
    历史拼写是公开 API 的一部分,勿「纠正」。

  5. 批量用错方法名
    单条 InsertTable;批量 InsertTables。README 若写 InsertTable(List) 以 DLL 为准。

  6. MaxLength 默认 500
    不是 50。配方短码、工单号请显式长度,避免索引键过宽或误导评审。

  7. InitialSqlServer 当热路径
    扫码、心跳、每秒采集不要全量同步。部署/升级/班次切换再跑。

  8. 生产打开 SyncTableSchemaWhenExists
    可能改列。先备份,先顾问报告,先在预发验证。

  9. [Auto] 但未 LoadModelAssembly
    类型未登记,热路径可能不按你预期补齐。

  10. 外层同步 Successed=true、报告 Successed=false
    TVP/过程漂移。先收敛结构再 DML,不要只看外层。

  11. 多实例共用默认调度却不做负载预设
    多线程采集请后续结合 ApplyWorkloadPreset(第7期工具篇)。

  12. 用 sa 上产线
    演示可以,产线用最小权限账号;建库权限与日常 DML 权限分离。

  13. 命名实例反斜杠
    C# 字符串写 @"host\INSTANCE""host\\INSTANCE"

  14. Encrypt 随 SqlClient 变严
    升级驱动后突连失败,显式配置 Encrypt / TrustServerCertificate(第2期展开)。

  15. 把 XML 未出现的本地实验 API 写进产线
    本系列只认 2026.9.7.1642 已发布公开面。


FAQ

Q:必须用 Factory 吗?
A:不必。单机上位机 SqlEntities.CreateNew() 足够。要启动同步、[Auto]、多 Profile、句柄自愈时用 Factory。

Q:和 EF Core 比?
A:本库偏「工控落库工具箱」(建库表、TVP、过程、联查、内置 Ics 表、结构同步顾问),不是完整变更迁移生态。MES 中台若已统一 EF,线边 HMI 仍可用本包做采集落库。

Q:异步怎么用?
A:有 InsertTableAsync / InsertTablesAsync / InqureTableAsync / Factory.InsertAsync / QueryAsync 等,支持 CancellationToken。采集线程建议异步 + 有界队列,避免 UI 线程堵死。

Q:内置工单表能不能直接用?
A:包内有 Tables.Ics.IcsWorkOrderIcsAlarmRecordIcsShift 等(第7期)。本期用自建 POCO 跑通通路;正式项目评估 StandardTable 掩码。

Q:Factory.Insertsql.InsertTable 差别?
A:前者走全局句柄 + 可恢复异常重建重试,失败多转为 Result;后者直接打你持有的实例。对 [Auto] 类型,两者都会触发自动结构确保。

Q:能否注入自定义 ISqlServer
A:可以,Factory.SetSqlServer(myImpl)。测试替身或特殊连接池包装时有用。

Q:同步失败但库表其实已存在?
A:看 Message 与报告明细。已存在通常仍可能走成功路径;真正失败常见于权限、类型不兼容、过程占用 TVP。用 ExistsDatabase / ExistsTable 做分支诊断。

Q:首条工单插入成功但画面查不到?
A:确认查询条件、是否连错 Profile/库、模糊查询 fuzzy 是否误开、以及是否查的是同步前的旧库名。



深化:为什么上位机需要「双入口」而不是单一全局单例

产线软件和互联网后台有一个关键差异:进程生命周期与网络抖动绑在一起。触摸屏工控机可能连续跑几个班次不关;也有人中午断电、下午换班登录;还有人在维护窗口重启 SQL Server 服务却不忘关上位机。

因此本包同时提供:

  1. SqlEntities:你看得见、摸得着的 ISqlServer 实例。适合单元测试、临时工具窗体、维护程序、以及「我就想自己持有引用」的清晰控制流。
  2. Factory:进程级句柄 + 连接快照 + 模型目录 + Profile 路由 + CRUD 自愈。适合正式 HMI 主程序:启动配一次,页面到处 Factory.Insert / Factory.Query

两者不是互斥。常见现场组合是:

  • 启动窗体用 SqlEntities.CreateNew() 做连接自检与首次建库;
  • 自检通过后 Factory.SetSqlServer(sql) 注入;
  • LoadModelAssembly + InitialSqlServer
  • 业务窗体只碰 Factory。

这样「谁创建了连接」有据可查,又享受全局 CRUD 便利。

若反过来:业务页直接改 SqlEntities.Create 静态字段上的属性,而 Factory 另有快照——容易出现「页面以为改了库名,同步还在打旧库」的幽灵问题。配置入口要单一:要么全程 SqlEntities 自持,要么全程 ConfigureSqlServer / Profile,不要两边偷偷改。


深化:首条工单通路的现场检查单

把故事线拆成可执行检查项,班组长与软件工程师可以共用:

  1. 网络:工控机能否 ping 通 SQL 主机;命名实例是否启用浏览器服务或写死端口。
  2. 认证:SQL 混合模式是否开启;hmi_line_a 是否被策略锁定;Windows 认证时服务账户是否有登录权。
  3. LineA_Mes 是否已存在;不存在时账号是否有 CREATE DATABASE
  4. dbo.WorkOrder 是否已存在;存在时列是否与 POCO 一致(尤其新增 ShiftCode)。
  5. 权限:日常账号是否只需 DML;部署账号是否临时提升。
  6. 结果语义:所有步骤是否判了 SuccessedResult<bool> 是否还判了 Content
  7. 回读InqureTable / Factory.Query 条件是否精确匹配工单号。
  8. 日志:失败 Message 是否写入本地日志,便于夜班交接。

很多现场事故不是「API 不会用」,而是第 6 步只写了 if (r.Successed) 就继续——连接测试 Content=false 被当成成功,后续插入才报一长串登录失败,排查成本翻倍。


深化:班次切换与模型同步的节奏

建议把结构同步从「业务热路径」里剥离:

时机建议动作不建议
软件首次安装EnsureDatabase/EnsureTable=true,完整 InitialSqlServer让操作工在生产画面点「同步」
版本升级(多了列)预发验证 → 备份 → 维护窗口同步;SyncTableSchemaWhenExists 仅在评审后短时开启生产高峰自动纠偏列类型
每班开机Connection() 自检 + 可选 SchemaStartupAdvisor 只读体检全量 Synchronize
扫码/采集仅 DML:Insert / InsertTables / Inqure任何 DDL
临时试验表[Auto] 允许热补把关键工单表也标 Auto 且依赖热路径改结构

班次切换时,若只是换 ShiftCode 字典或当前班次全局变量,不必同步结构。只有程序集里的表模型相对库发生了「形状变化」,才需要同步。


深化:SchemaSyncReport 双层 Successed 怎么读

现场日志里最容易误解的是:

InitialSqlServer: Successed=true
SchemaSyncReport.Successed=false
Message: … TVP / 过程 …

请按两层解读:

  1. 外层 Result:同步命令链路有没有「炸」(异常、权限直接失败等)。Successed=true 表示流程跑完并给出了报告。
  2. 报告 Successed:业务上是否认为结构已收敛。false 通常表示部分工件(尤其 TVP、MERGE 过程、外键)仍有漂移或失败项。

上位机启动策略建议:

  • 外层失败:阻止进入生产画面,弹模态错误。
  • 外层成功但报告失败:按配置决定「仅告警可登录」还是「阻止登录」。对依赖 TVP/MERGE 的落库路径,建议阻止;对只用普通 InsertTable 的简单工单,可告警后放行并建运维工单。

升级旧库时,优先安排一次全量收敛,再谈「告警是否可登录」。


深化:Factory CRUD 自愈边界(不要神话)

Factory.Insert / Update / Delete / Query 在捕获 ObjectDisposedException / NullReferenceException 等「句柄失效」时,会重建实例、应用连接快照并重试一次。这解决的是:

  • 长时间运行后句柄被释放;
  • 某些路径误 Dispose;
  • 快照仍有效时的快速恢复。

解决:

  • 密码错、账号锁、防火墙丢包(会以失败 Result 返回,不会靠重试变成功);
  • 业务唯一键冲突;
  • 表不存在且未走 Auto/同步;
  • 死锁与超时(需业务层重试策略,可结合后续并发预设)。

所以自愈是「基础设施级」的,不是「业务失败自动抹掉」。HMI 仍要提示操作工:插入失败时不要重复猛点,先看 Message


深化:多产线 Profile 的配置纪律

双线共屏(一台工控机管 A/B 线)时,推荐:

  1. 启动时 RegisterProfile("LineA", …) / RegisterProfile("LineB", …),全部返回值判 Successed
  2. SetDefaultProfile 设为当前物理线或上次选择。
  3. 画面切线时用 using (Factory.With("LineB")) { … },避免全局改默认导致后台采集线程写错库。
  4. 模型上可加 [SqlProfile("LineA")] 固定某些表永远打 A 库(例如只在 A 线存在的专用校准表)。
  5. 切线时写审计:谁在什么时间把作用域切到哪条线。

反模式:在定时器回调里改 Factory.SqlServer.InitialCatalog,同时 UI 线程 With 另一个 Profile——竞态下会出现「报警进了工单库」。Profile 的价值就是把连接参数变成命名快照,而不是运行时散改属性。


深化:与 MES 对接时的边界

本包擅长线边落库与结构对齐;MES 常在另一套库或另一台服务器。推荐边界:

  • 线边库:工单执行、报警、班次、采集摘要——用 Hi.Ltd.SqlServer。
  • MES 库:计划下发、质量闭环——由 MES 接口或中间服务写入;上位机只读或通过受控账号写回有限字段。
  • 同步方向:MES → 线边可用接口落「当日计划工单」;线边 → MES 用批量出站,不必让 HMI 直接对 MES 库跑 InitialSqlServer

若强行让上位机对 MES 中心库 EnsureTable=true,一旦模型写错,风险面远大于线边库。权限上也应禁止 HMI 账号在 MES 库执行 DDL。


深化:最小权限模型(演示 sa,产线分离)

角色权限使用时机
部署账号建库/建表/建过程/备份安装与升级维护窗口
运行账号业务库 DML + 有限读系统视图日常班次
只读账号报表查询看板/审计

InitialSqlServer 用部署账号;Factory.ConfigureSqlServer 在维护结束后可切回运行账号(或维护程序与 HMI 分进程)。不要因为演示代码写了 sa 就复制到产线。


深化:完整样例之外的「失败注入」练习

建议在预发环境故意制造失败,确认 HMI 文案与日志:

  1. 改错密码 → 应看到 Successed=falseContent=false,画面不可进入。
  2. 删掉 WorkOrder 表且关闭 Auto → Insert 失败信息可读。
  3. 打开 Auto 再插入临时表 → 表被补回(仅限试验表)。
  4. 注册错误 Profile 名 → SetDefaultProfile 失败。
  5. 同步时关掉 EnsureTable 且表不存在 → 报告/后续 DML 暴露问题。

没有失败注入,等于没验收 Result 语义。


深化:代码评审时对照的「必须出现」项

给同事做 Code Review 时,打开落库相关 PR,核对:

  1. 包版本是否锁定 2026.9.7.1642(或团队统一更高已发布版本,但本系列示例锁定该版)。
  2. 是否 using Hi.Ltd 且判断 Successed
  3. 是否出现错误拼写 Succeeded / InquireTable / InsertTable(list)
  4. 字符串字段是否显式 MaxLength,避免误用默认 500 却按 50 做 UI 校验。
  5. 生产配置里 SyncTableSchemaWhenExists 默认值。
  6. 是否把同步塞进采集定时器。
  7. 密钥是否进仓库。
  8. 是否区分部署账号与运行账号。

这八条能挡下大半「能在开发机跑、到产线爆炸」的提交。


深化:工单号、班次码与时间字段的建模提示(预告第2期)

即使本期只用最小特性,也建议尽早养成:

  • 工单号:[Required, MaxLength(64)],业务唯一时考虑唯一索引(可用后续 Execute 建索引或 DBA 脚本)。
  • 班次码:短字符串,显式长度;不要靠默认 500。
  • 时间:DateTime 用服务器本地还是 UTC,全厂统一;默认值可用第2期 DefaultSql
  • 数量:[Decimal(18,4)],不要用 float 落金额/重量。
  • 主键:默认 [Key] 的 Identity 语义适合绝大多数工单表;业务主键用 DatabaseGenerated.None(第2期)。

特性是「模型即文档」。产线新人读 POCO 应能猜出表长什么样。


深化:启动日志模板(可粘贴)

[LineA] boot start
[LineA] Connection Successed={0} Content={1} Message={2}
[LineA] CreateDatabase Successed={0} Message={1}
[LineA] CreateTable WorkOrder Successed={0} Message={1}
[LineA] InitialSqlServer Successed={0} Report.Successed={1} Message={2}
[LineA] Insert WO Successed={0} Content={1} Message={2}
[LineA] Query count={0}
[LineA] boot end ok={0}

{0} 换成实际值。夜班若只说「连不上」,没有这七行,几乎无法远程判断是网络、认证、库名还是同步报告告警。


深化:何时该停下来读第2期而不是继续堆 POC

若你已经:

  • 能插入工单并回读;
  • 理解 Successed/Content;
  • 能选 SqlEntities 或 Factory;

下一步卡点通常是:

  • 主从表外键怎么标;
  • 列名与属性名不一致;
  • 中文排序规则;
  • 连接加密突然失败;
  • Windows 认证与 SQL 认证切换。

这些正是 v2 第2期 的范围。不要在第1期 POC 里手写半套特性映射器——那是重复造轮子。


深化:一条产线从「空库」到「可报工」的九十秒口述稿

给你的实施同事一段可以照着讲的话,方便培训:

「上位机启动先配数据源和库名,调用 Connection,先看 Successed,再看 Content。通了以后,要么用 CreateDatabase 和 CreateTable 建工单表,要么走 Factory 的 ConfigureSqlServer、LoadModelAssembly、InitialSqlServer。同步时生产环境先把 SyncTableSchemaWhenExists 关掉,只确保库表存在。然后 Factory.Insert 写开工单,用 Query 或 InqureTable 回读。批量采集一定要走 InsertTables 复数方法。Result 的拼写是 Successed,少一个 e。字符串 MaxLength 默认五百,短码自己写长度。查询方法名叫 Inqure,不是 Inquire。这一套是 NuGet 二零二六九点七点一六四二的公开面,不要抄 README 里返回 bool 的老例子。」

把这段话练顺,现场支持电话里就能挡住一半错误示范。


深化:工单状态机与落库时机(业务层,不在包内)

本包不内置工单状态机,但上位机几乎总会有。建议状态与落库时机解耦:

状态含义落库建议
草稿班长预填未确认可写本地或线边,允许改
已下达MES/计划确认插入或更新线边工单主表
生产中开工按钮更新状态字段 + 写班次
暂停设备故障写报警 + 更新状态,勿删主单
完工数量达标更新实际产量,出站给 MES
作废取消软删除标记,保留审计

UpdateTable / Factory.Update 改状态时,务必带主键;条件更新要清楚 fuzzy 语义,避免模糊匹配误更新多行(CRUD 专期展开)。本期只要保证「已下达」能插入成功、「生产中」能查到。


深化:采集落库与工单落库的队列隔离

工单是低频、要强一致提示的操作;采集是高频、可批量的操作。架构上建议:

  1. UI 线程:工单 Insert/Update,失败立刻 MessageBox。
  2. 后台采集线程:入内存队列,批量 InsertTables,失败写重试文件。
  3. 两者可共享 Factory 句柄,但不要共享「未完成的事务观感」——工单失败不应卡采集,采集堆积不应堵开工按钮。

本包提供 API,不强制线程模型;但若把每秒上百点位写成每次 InsertTable 单条,SQL 与网络会被打满。批量入口存在的意义就在这里。


深化:AutoSelfHeal 与「表被 DBA 改过」现场

真实产线常有:DBA 半夜加了列、改了可空、加了默认值,而上位机 POCO 未更新。表现可能是:

  • 插入仍成功(多列有默认值);
  • 或插入失败(强制非空无默认);
  • 或 Auto 自愈尝试补齐/对齐时与 DBA 变更冲突。

策略建议:

  1. 关键列变更走「先改 POCO 再同步」的发布流程。
  2. DBA 独占管理的列打 ExternallyManaged(第2期),避免 Auto ALTER。
  3. 生产关闭激进纠偏;Auto 仅用于明确约定的附属表。
  4. 出现漂移 SqlException 时,把 Message 完整留存,对照 SchemaStartupAdvisor(后续期)。

不要指望 Auto 替代变更管理会议。


深化:SqlEntities.Create 与 CreateNew 的选择矩阵

需求选择原因
正式 HMI 主程序CreateNew + Factory.SetSqlServer 或直接 ConfigureSqlServer生命周期清晰,便于测试替换
快速控制台实验SqlEntities.Create少一行
并行测试用例每例 CreateNew避免静态状态串扰
Factory 内部重建框架走 CreateNew 路径与快照重绑

避免在多个窗体各自 CreateNew 又各自改连接,导致「一屏连 A 库、一屏连 B 库」且没有 Profile 审计。能收敛到 Factory 就收敛。


深化:InitialSqlServer 参数怎么选程序集

LoadModelAssembly / InitialSqlServer(..., assembly, ...) 会扫描候选表模型。大解决方案里常见坑:

  • 扫到了测试工程里的演示表;
  • 扫到了已废弃但未删除的旧 POCO;
  • 扫到了桌面控件项目里误加的 [Table]

对策:

  1. 表模型集中在 LineA.Mes.Models 程序集,只 Load 它。
  2. IncludeNamespacePrefixes / ExcludeTableNames / TypeFilter / AutoOnly 收紧。
  3. 废弃表先去特性再删类,或排除名单。
  4. 同步前看 Factory.ModelTypes 列表,打印到启动日志。

「扫错程序集」比「连错服务器」更隐蔽,因为连接成功、只是多建了怪表。


深化:成功标准(本期验收)

完成下列项,第1期才算过关:

  1. 在目标 SQL Server 上,空库或已有库,跑通完整样例,日志七行齐全。
  2. 故意输错密码,确认无法进入主画面。
  3. 插入工单后,用 SSMS 能看见行;用 InqureTable/Query 也能看见。
  4. 批量 InsertTables 插入不少于一百行试验数据,耗时可接受。
  5. 代码库中全局搜索不到 SucceededInquireTablebool connected = .*Connection(
  6. 生产配置默认 SyncTableSchemaWhenExists=false
  7. 培训口述稿能不看笔记讲完。

未达验收不要急着上特性与联查——地基不牢,后面每期都会反复踩连接与 Result 的坑。


深化:与旧 README 示例的逐条对照(防误抄)

README 常见写法2026.9.7.1642 正确写法
bool connected = sql.Connection();Result<bool> r = sql.Connection(); if (!r.Successed || !r.Content) …
暗示 MaxLength 默认 50XML:默认 500
InsertTable(list) 批量InsertTables(list)
文档脚注旧版本号包版本以 2026.9.7.1642 为准
过程接口名按英文直觉拼类型名 ICreateProcdure(方法仍可能叫 CreateProcedure

把这张表贴到团队 Wiki,比反复口头提醒有效。


深化:报警表用 Auto、工单表不用 Auto 的理由再讲一遍

工单表是审计与产量统计的根;结构变更要留变更单、要备份、要告知 MES。Auto 热路径补列可能:

  • 在生产高峰加锁;
  • 生成与 DBA 规范不一致的列定义;
  • 让「线上结构」与「发布说明」脱节。

报警缓冲表、临时调试表、设备侧上传的宽表,往往更适合 Auto:宁可不丢报警,也不要因为忘建表导致整屏红叉。
同一程序集里混合策略是正常的——不要教条「全开」或「全关」。


深化:Content 为空列表与失败的区别

Query / InqureTable 成功且 Content 为空列表,表示「查成功,但没有行」——工单号打错、班次滤错、连错库,都可能。这不是异常。

只有 Successed=false 才是执行失败。HMI 文案要区分:

  • 「没有符合条件的工单」→ 业务空结果;
  • 「查询失败:…」→ 技术失败,需要维护。

把空列表当成失败去弹错误色,会训练操作工忽略真正的红字报警。


深化:从第1期跳到后续期的阅读地图

  • 还不会标外键、小数、并发列 → 第2期 Attributes。
  • 开机连不上、证书报错 → 第2期 Connection。
  • 要备份还原、附加分离 → 第3期。
  • 要更新删除复制批量调优 → 第4期。
  • 主从联查、过程、TVP → 第5期。
  • 结构顾问、Execute、事务、索引 → 第6期。
  • 统计工具、内置 Ics/Sys、视图全文、总避坑 → 第7期。

第1期是门禁;门禁过了再加深,效率最高。


深化:产线早会可以用的「落库健康」五问

每天早会不必投影代码,问五句就够:

第一,昨夜有没有结构同步失败或报告告警?有则先看 SchemaSyncReport,再决定是否允许开产。
第二,开工单是否在三秒内成功?失败是权限、唯一键还是连接?
第三,采集队列有没有堆积?堆积是不是单条插入没用 InsertTables?
第四,A/B 线有没有写串库?看 Profile 审计。
第五,有没有人把 SyncTableSchemaWhenExists 临时打开忘了关?

这五问把本包能力翻译成管理语言,便于设备、工艺、信息化三方对齐。


深化:首次部署脚本化步骤(人工版)

即使你用本包 API,也建议保留人工检查序:

步骤一,确认 SQL Server 版本与兼容性级别满足公司规范。
步骤二,创建运行账号与部署账号,拒绝共用 sa。
步骤三,用维护工具执行 ConfigureSqlServer(部署账号)与 InitialSqlServer。
步骤四,抽查关键表:工单、报警、班次、配方是否存在。
步骤五,切换为运行账号配置,执行 Connection 自检。
步骤六,插入一笔测试工单并删除或作废,确认 DML 权限足够且无 DDL 权限。
步骤七,打开 HMI 走一遍早班开工,保留截图与日志归档。
步骤八,备份新建库,作为「空白可用基线」。

自动化不是省略步骤,而是让步骤可重复。缺了基线备份,第一次 SyncTableSchemaWhenExists 试错都会令人心跳加速。


深化:对象命名与架构(Schema)约定

[Table("WorkOrder", Schema = "dbo")] 只是起点。建议厂内约定:

业务表放 dbomes
设备采集宽表放 acq
只读对接视图放 viz
内部技术表放 sysx(避免与 SQL 的 sys 混淆)。

Factory 同步时 Schema 来自特性。若有人手工建了 mes.WorkOrder,而 POCO 写 dbo.WorkOrder,你会得到「两张像双胞胎的表」,画面查一张、接口写另一张。同步日志里打印「架构+表名」全名,能早点发现。


深化:时间、时区与班次切割

工控机常跑本地时区,MES 可能用 UTC。本期插入用 DateTime.Now 仅作演示。正式约定应写进规范:

库内时间一律 UTC,显示再转本地;或
库内一律工厂本地时间,禁止混用服务器时区不同的机器。

班次切割不要只靠「上午八点」写死在客户端:夏令、节假日加班、跨零点班次都会让统计错位。班次表(后续内置 IcsShift)应成为时间归属的依据;工单上的 ShiftCode 是冗余便捷字段,统计以班次日历为准。


深化:唯一性与工单号生成

OrderCode 通常需要唯一。本包特性建模以列型与键为主,唯一索引可用后续 Execute 或 DBA 脚本补充。业务上:

工单号建议服务端或统一发号服务生成,避免两台 HMI 同时本地拼时间戳撞车。
若必须客户端生成,加上产线号与随机段,并在库侧建唯一约束,插入失败时提示「工单号冲突」而不是泛型错误。

InsertTable 返回失败时,把 Message 映射成操作工能懂的句子,是产品化的一部分。


深化:UI 绑定与 Content 空值防护

if (q.Successed)
{
    var data = q.Content ?? new List<WorkOrderRow>();
    grid.DataSource = data;
}
else
{
    grid.DataSource = null;
    ShowError(q.Message);
}

永远假设 Content 可能为 null(失败路径或实现细节)。WinForm 绑定到 null 有时不抛、有时抛,取决于控件。统一空列表更稳。


深化:日志中的敏感信息

Message 偶发可能含连接相关信息;自定义日志不要无条件把 Password 打出去。ConfigureSqlServer 闭包里读取环境变量即可,不要把密码常量写进仓库。连接失败时,日志给「账号、数据源、库名、Encrypt 开关」,足够排查;密码只存在密钥介质。


深化:版本锁定与还原策略

NuGet 允许浮动版本,但产线应锁定。本系列以 2026.9.7.1642 为唯一口径。升级时:

先读发行说明与 XML 变更;
在预发跑完整样例与失败注入;
确认 Successed 拼写与 InsertTables 等名称未变;
再滚动升级工控机。

「顺便升包」是现场经典事故源。


深化:与触摸屏性能有关的期望管理

触摸屏工控机 CPU/磁盘一般弱于办公 PC。期望:

启动同步:数秒到数十秒可接受(视表数量)。
开工插入:应在一秒内级反馈。
批量采集:靠 InsertTables 与队列,不靠提高同步频率。
查询默认前一千行类接口要避免当全表浏览;做条件查询与分页(后续期)。

不要在 UI 线程做 InitialSqlServer;放启动 Splash 或后台并禁用「开工」直到完成。


深化:教育新人时的三个演示顺序

演示一,只连 Connection,展示 Successed 与 Content 两种失败。
演示二,建表插入一条,SSMS 打开对照。
演示三,Factory 同步 + Query + 故意 Dispose 场景看自愈(若可安全演示)。

一次讲完双入口、Auto、Profile、TVP,新人会晕。第1期克制范围,是为了留记忆锚点:「先连上、先写入、先判 Result」。


深化:常见失败 Message 的阅读姿势

看到登录失败,先别改代码,先看 UserId 与 SQL 是否允许远程。
看到超时,先看 Timeout 与网络,再看是否同步太重。
看到对象名无效,先看 InitialCatalog 与表是否在当前库。
看到权限不足,区分 DDL 与 DML。
看到类型转换或截断,回头查 MaxLength 与 Decimal。

Message 是给工程师的原材料,产品层再翻译。直接把英文原样扔给操作工,只会换来「电脑坏了」的工单。


深化:本期知识图谱(便于内化)

中心节点是 Result。
连出去两条边:SqlEntities 与 Factory。
Factory 再连出:Configure、LoadModel、InitialSqlServer、Profile、CRUD 自愈、Auto。
SqlEntities 连出:Connection、CreateDatabase、CreateTable、InsertTable、InsertTables、InqureTable。
横切约束:Successed 拼写、Content 载荷、DLL 历史拼写、MaxLength 默认五百、生产关闭激进纠偏。

你能凭记忆画出这张图,第1期就真正学到手了。


深化:写给架构师的边界声明

Hi.Ltd.SqlServer 解决的是「Windows 工控宿主到 SQL Server 的结构化落库与结构对齐」。它不试图:

取代消息总线;
取代时序库;
取代完整 ORM 工作流;
取代 DBA 的备份与权限体系;
在未经评审时自动改造生产表结构。

在边界内,它非常强:POCO、Result、同步、批量、联查、过程、内置工控表。越界使用导致的问题,不应算作包的缺陷,而应算作架构选择问题。第1期明确边界,是为后续期的深度功能留安全绳。


深化:把「首条工单」做成自动化冒烟

建议在预发机器放一个控制台或测试项目,每晚跑:

配置连接(专用冒烟库,禁止指向生产);
Connection 必须成功;
InitialSqlServer 外层必须成功;
插入固定工单号前先按条件删除或使用随机后缀;
Insert 必须 Successed;
Query 必须能查回;
最后清理试验行。

冒烟失败就阻断次日发布。这比靠人「感觉能连」可靠得多。注意冒烟库也要备份策略,避免磁盘被试验数据撑满。


深化:HMI 画面文案示例(可直接改)

连接执行失败:无法执行数据库连接命令,请联系维护,并提供日志编号。
连接测试未通过:服务器拒绝或不可达,请检查网络、实例名与账号。
结构同步失败:启动对齐未完成,禁止开产,请维护员查看同步报告。
结构同步告警:可登录但限制依赖 TVP 的功能,请在维护窗口收敛。
工单插入失败:开工资讯未保存,请勿重复连续点击,先查看失败原因。
工单查询为空:没有符合条件的在制工单,请核对工单号与产线。
工单查询失败:读取在制工单时发生错误,请维护员处理。

文案区分「空结果」与「技术失败」,操作工体验会好一截。


深化:多解决方案复用模型程序集

若同厂多条线共用模型,把 POCO 放到独立程序集,版本随 MES 合同冻结。HMI 只引用模型包与 Hi.Ltd.SqlServer。同步选项按线区分:A 线 Include 表名单与 B 线不同。这样不会因为某线试验表污染另一线同步。模型包升级走变更单,与 HMI 发布列车可以偶合也可以解耦,但必须能回答「当前产线模型版本是多少」。


深化:从零到一的时间盒建议

给自己三个小时:

第一小时,只做 Connection 与 Result 判断,写日志。
第二小时,CreateTable 与 InsertTable,SSMS 对照。
第三小时,切 Factory,InitialSqlServer,Query,再做一次错误密码演练。

超时还没跑通,多半是环境(SQL 远程、防火墙、认证)而不是 API。把时间花在环境确认上,不要无改包版本碰运气。


深化:本期结语(进入第2期之前)

你已经具备:在工控上位机里用 Hi.Ltd.SqlServer 二零二六九点七点一六四二建立首条工单通路的能力;理解 SqlEntities 与 Factory 的分工;遵守 Successed 与 Content 的双层判断;知道 Auto 与同步的节奏;记住 Inqure、InsertTables、MaxLength 默认五百、Intergrated 等 DLL 拼写。

下一期把「表长什么样」与「怎么稳稳连上」一次讲透:属性建模决定结构质量,连接参数决定开机成败。两块都偏基础,却最常在现场被抄错。请带着本期跑通的工程继续,不要另起一个完全不同的连接写法,以免系列示例无法对照。


深化:附录——启动伪代码(逻辑序,便于板书)

第一步,读取配置中的数据源、库名、账号、超时、加密开关。
第二步,创建 SqlEntities 实例或调用 ConfigureSqlServer。
第三步,执行 Connection,若 Successed 为假或 Content 为假则中止启动。
第四步,若需要部署对齐,则 LoadModelAssembly 并 InitialSqlServer,解读双层 Successed。
第五步,将可用句柄交给 Factory(若尚未配置)。
第六步,业务允许后,插入工单并查询校验。
第七步,启动采集队列,批量写入走 InsertTables。
第八步,任何失败写日志与用户可读文案,不吞异常也不只弹英文。

把这八步画在白板上,比贴一堆 API 列表更有利于班组培训。细节参数仍以本文样例与 XML 为准。


深化:附录——团队约定模板(可复制到制度)

约定一,生产环境禁止默认打开同步纠偏开关。
约定二,所有数据库调用必须判断 Successed,布尔结果还要判断 Content。
约定三,禁止提交包含 Succeeded 或 InquireTable 或错误批量方法名的代码。
约定四,模型程序集变更必须附带同步影响说明与回滚方式。
约定五,sa 仅允许在隔离实验环境出现。
约定六,系列文档与示例以 NuGet 二零二六九点七点一六四二公开面为准,未发布 API 不得用于产线。
约定七,Auto 仅用于明确清单内的表。
约定八,连接密钥不入仓库,不入截图,不入聊天记录。

制度越短越能执行;执行越严,后面联查与过程篇才敢写「可运行样例」。


深化:附录——与采集网关共存时的注意点

不少工厂已有独立采集网关服务负责把 PLC 点位写入 SQL。此时上位机不要再对同一张原始点位表高频 InsertTables,以免双写。上位机应:

消费网关写好的摘要表或视图;
自己只管工单、配方、报警确认、班次;
若必须写同一库,表名前缀或架构分开,权限分开。

Hi.Ltd.SqlServer 很适合「上位机业务库」;与网关分工清晰后,InitialSqlServer 的表清单会小很多,启动更快,冲突更少。遇到「点位表被同步改列」这类事故,先查是否错误 Load 了网关模型程序集。


深化:附录——验收签字单(纸质可打)

项目名称:____________________ 产线:__________ 日期:__________

验收人确认:已使用包版本二零二六九点七点一六四二。
验收人确认:Connection 双层判断已演示通过。
验收人确认:首条工单插入与回读成功。
验收人确认:批量 InsertTables 试验完成。
验收人确认:生产配置未打开激进结构纠偏。
验收人确认:代码扫描无错误拼写 Succeeded 与 Inquire。
验收人确认:运行账号非 sa,密钥未入仓库。

签字:软件________ 设备________ 信息化________

没有签字单也能上线,但有签字单时,事后扯皮会少很多。建议与冒烟脚本一起归档。


深化:附录——读者自测十题(心里答即可)

Successed 少的是哪个字母?Content 是什么?Connection 返回什么类型?批量插入方法名是什么?查询方法名怎么拼?MaxLength 默认多长?Auto 能否单独生效?生产默不默认可纠偏已存在表结构?Factory 与 SqlEntities 谁带句柄自愈?外层同步成功但报告失败说明什么?

若十题都能快速作答,你可以进入第2期;若有两题含糊,请把本文避坑清单再读一遍,并在预发环境亲手踩一次对应失败。自测不是考试,是为了减少产线电话。


深化:附录——写完首条工单后的心情管理

第一次在产线工控机上看到工单网格里出现自己插入的记录,很值得高兴,但请立刻做两件事:备份当前库,把配置从 sa 换成运行账号再验证一次。高兴而不收尾,是现场事故的序章。收好尾,再打开第2期,把属性与连接补硬,后续 CRUD 与联查才会稳。

班组若仍有疑问,请带着日志中的 Successed、Content 与 Message 三字段来问,而不是只说「数据库坏了」;三字段齐全时,维护响应会快得多,产线停机也会更短。


深化:附录——和「只抄最小示例」说再见

最小示例能帮你建立信心,但不能直接当产线架构。产线至少要补齐:配置与密钥分离、启动日志模板、失败文案、Profile 纪律、同步节奏、批量队列、权限分离、冒烟与签字。本文后半大量「深化」段落,就是在逼你把最小示例升级成可交接的工程习惯。若你只把完整样例粘进 MainForm_Load 就发布,请至少把避坑清单打印贴在工控机侧门内侧,作为补丁级补偿措施。


深化:附录——变更冻结期怎么用本包

节假日与出货高峰常有「变更冻结」。冻结期内:禁止 InitialSqlServer 纠偏;禁止打开 SyncTableSchemaWhenExists;禁止新增 Auto 表依赖;只允许 Connection 自检与纯 DML。若必须热修程序,确保模型程序集相对库「只增不可见逻辑、不改表形」。冻结解除后再走维护窗口同步。把这条写进发布日历,比任何技术细节都更能减少节前事故。

冻结期若有人临时打开纠偏开关,必须在交接班记录里留下姓名、时间、原因与关闭确认;没有关闭确认,次日开产前视为高风险项,优先复查表结构与备份。


深化:附录——本文目录回顾(便于检索)

本文依次覆盖:目标与适用范围、早班工单故事线、安装与 Result 约定、SqlEntities 最小路径、Factory 与 InitialSqlServer、多 Profile、Auto、SchemaSyncOptions、Inqure 回读、手写对照、完整样例、避坑、FAQ,以及多组深化附录(双入口、检查单、班次节奏、报告双层、自愈边界、MES 边界、权限、失败注入、评审项、日志模板、口述稿、状态机、队列、命名、时区、唯一性、UI、密钥、版本、性能、演示顺序、Message、知识图谱、架构边界、冒烟、文案、复用模型、时间盒、结语、板书伪代码、团队约定、采集网关、签字单、自测十题、心情管理、冻结期)。检索时按需跳转,不必一次读完所有附录,但产线发布前建议至少读完避坑与验收相关段落。

下期预告

第2期:Attributes 全量建模 + Connection/Encrypt

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tang_0427

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值