应使用 guid.newguid() 生成新 guid,new guid() 仅返回全零值;解析用 tryparse,输出按场景选 tostring("n")/("b")/("d") 格式,ef core 中需明确主键策略。

直接用 Guid.NewGuid() 就行,99% 的场景下这是唯一需要的操作;别用 new Guid(),它永远返回全零值 "00000000-0000-0000-0000-000000000000",不是“新 ID”。
Guid.NewGuid() vs new Guid():一个生成,一个清零
这是最常踩的坑——把构造函数当工厂方法用。new Guid() 是结构体默认构造,语义上就是“初始化为零”,和 new int() 得 0、new bool() 得 false 一样自然,但它在业务中常被误当作“占位新 ID”。一旦用于数据库主键或 API 返回,所有记录都会共享同一个 Guid.Empty,轻则插入失败(SQL Server 报 Cannot insert the value NULL into column 'Id'),重则数据被覆盖。
而 Guid.NewGuid() 调用的是操作系统级密码学随机源(Windows 用 BCryptGenRandom,Linux/macOS 用 /dev/urandom),线程安全、无需配置、跨 .NET 版本行为一致。
- EF Core 中若实体属性是
public Guid Id { get; set; }且没显式赋值,插入时传的就是Guid.Empty - 判断是否未设置,写
guid == Guid.Empty,别用string.IsNullOrEmpty(guid.ToString())(永远 false)或guid.ToString() == ""(会抛NullReferenceException) - 需要可空语义,直接用
Guid?类型,比靠Guid.Empty约定更清晰
ToString() 格式选错,JSON 和 URL 就解析失败
GUID 默认转字符串是 "D" 格式(带短横线,36 字符),但不同场景对格式敏感:
- 存数据库字面量(如 SQL Server):用
guid.ToString("B")→"{a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8}",别手动拼"{" + guid + "}" - 传 JSON 或 URL 参数:推荐
guid.ToString("N")(32 字符无横线),避免Replace("-", "")多一次字符串分配,也防null崩溃 - 日志或调试输出:用
guid.ToString("D")或guid.ToString("D").ToUpperInvariant(),可读性高、易比对 - 别试
ToString("X")—— GUID 不支持该格式,直接抛FormatException
从字符串解析 GUID 必须用 TryParse
外部输入(API 请求体、数据库查出的字符串、用户粘贴)不可信。Guid.Parse() 遇到非法格式(比如多空格、中文、下划线、33 位十六进制)立刻炸;Guid.TryParse() 才是生产环境唯一安全选择。
它支持带/不带横杠、大小写混用、甚至带花括号,只要字符是合法的 32 或 36 位十六进制 + 分隔符即可:
-
Guid.TryParse("9b1deb4d3b584709850c61e63514d74f", out var g)→ 成功 -
Guid.TryParse("9b1deb4d-3b58-4709-850c-61e63514d74f", out var g)→ 同样成功 - 但
Guid.TryParse("9b1deb4d-3b58-4709-850c-61e63514d74f ", out var g)(末尾空格)→ 失败
别用 new Guid(string) 处理不可信输入,没有容错余地。
性能与序列化里容易被忽略的细节
GUID 本身是 16 字节值类型,开销极小;问题出在字符串操作和上下文约定上:
- 高频生成(如每秒万级日志 ID)时,保留
Guid类型变量,仅在必要输出时调ToString("N"),避免反复分配字符串 - 缓存 Key 或字典查找中,同一 GUID 若有时小写、有时大写(如
guid.ToString()vsguid.ToString().ToUpper()),会被当成两个不同 Key - JSON 序列化默认把
Guid.Empty输出为"00000000-0000-0000-0000-000000000000",前端可能误判为有效 ID,需配合业务层做空值过滤 - 数据库主键用
UNIQUEIDENTIFIER(SQL Server)或UUID(PostgreSQL),别用varchar(36)—— 索引效率差、空间浪费近一倍
真正难的不是生成,而是统一字符串格式、守住 TryParse 边界、以及在 EF Core 或 Dapper 映射时明确主键生成策略(ValueGenerated.Never 还是交由数据库)。这些地方一松懈,问题就藏在上线后第三个月的数据修复里。










