C# / .NET Guid
On this page
In .NET, UUIDs are called Guids and are represented by System.Guid — a 128-bit value type. The string form is identical to UUIDs from any other platform; the binary serialization order is the one place they differ. See UUID vs GUID.
Generate a v4 Guid
Guid id = Guid.NewGuid();
// 0e6f1b8c-2c33-4f1f-9c0b-2a3d4e5f6a7b
Guid.NewGuid() produces a v4 (random) Guid using the platform’s CSPRNG. Available since .NET Framework 1.0; works identically on .NET Core, .NET 5/6/7/8/9, and Mono.
Generate a v7 Guid (.NET 9+)
// .NET 9.0 (November 2024) and later
Guid id = Guid.CreateVersion7();
// 01928a47-3b30-7c5e-9d1a-f0b8c4a7e923
Guid id2 = Guid.CreateVersion7(timestamp: DateTimeOffset.UtcNow);
CreateVersion7 is the right default for new database primary keys — see UUID as primary key for why. For .NET 8 and earlier, install UUIDNext:
// dotnet add package UUIDNext
using UUIDNext;
Guid id = Uuid.NewSequential(); // v7
v5 Guid (.NET 9+)
Guid ns = Guid.Parse("6ba7b811-9d1d-11d1-80b4-00c04fd430c8"); // URL ns
Guid id = Guid.CreateVersion5(ns, Encoding.UTF8.GetBytes("https://example.com"));
For older .NET versions, use the UUIDNext or Faithlife.Utility.UuidUtility packages.
Parse from string
Guid id = Guid.Parse("0e6f1b8c-2c33-4f1f-9c0b-2a3d4e5f6a7b");
// Specific format (faster for hot paths)
Guid id2 = Guid.ParseExact("0e6f1b8c2c334f1f9c0b2a3d4e5f6a7b", "N");
// Try-parse for user input
if (Guid.TryParse(input, out Guid parsed)) { /* use parsed */ }
Guid.Parse accepts all five formats (N, D, B, P, X). Use ParseExact if you know the input is one specific shape — it’s faster and rejects the others.
Format specifiers
Guid.ToString() accepts a format character:
| Format | Example | When to use |
|---|---|---|
D (default) | 0e6f1b8c-2c33-4f1f-9c0b-2a3d4e5f6a7b | Logs, JSON, URLs |
N | 0e6f1b8c2c334f1f9c0b2a3d4e5f6a7b | Compact URLs, no hyphens |
B | {0e6f1b8c-2c33-4f1f-9c0b-2a3d4e5f6a7b} | Windows registry, COM |
P | (0e6f1b8c-2c33-4f1f-9c0b-2a3d4e5f6a7b) | Rarely used |
X | {0xe6f1b8c,0x2c33,0x4f1f,{0x9c,0x0b,...}} | C/C++ struct literal |
Default since .NET 5 is lowercase. .NET Framework historically produced uppercase. Use g.ToString().ToLowerInvariant() if you need stable lowercase output across older runtimes.
Bytes — the byte-order gotcha
Guid g = Guid.Parse("00112233-4455-6677-8899-aabbccddeeff");
byte[] bytes = g.ToByteArray();
// 33 22 11 00 55 44 77 66 88 99 aa bb cc dd ee ff
The first three groups are stored little-endian in the byte array. This is “Microsoft order” — it’s the historical .NET / COM convention.
If you’re sending Guids over the wire to a system that expects RFC 4122 byte order (almost everything else), use ToByteArray(bigEndian: true) (.NET 8+) or do the byte-swap yourself:
// .NET 8+
byte[] rfcBytes = g.ToByteArray(bigEndian: true);
// Older
byte[] b = g.ToByteArray();
Array.Reverse(b, 0, 4); // time_low
Array.Reverse(b, 4, 2); // time_mid
Array.Reverse(b, 6, 2); // time_hi_and_version
// b is now RFC byte order
For string round-trips (the canonical form) you don’t need to think about this — it’s purely a binary-serialization concern.
SQL Server
-- table
CREATE TABLE [User] (
Id uniqueidentifier PRIMARY KEY DEFAULT NEWID(),
Email nvarchar(256)
);
-- v4 (random) — bad for clustered index, fine for non-clustered
SELECT NEWID();
-- "sequential" — better for clustered index, but reveals partial timestamp
SELECT NEWSEQUENTIALID();
NEWSEQUENTIALID is not a UUID v7 — it’s a Microsoft-specific monotonic-within-a-machine variant. For cross-platform v7, generate in C# with Guid.CreateVersion7() and pass the value in.
Entity Framework Core
public class User
{
public Guid Id { get; set; } = Guid.CreateVersion7(); // .NET 9+
public string Email { get; set; }
}
// Or in OnModelCreating:
modelBuilder.Entity<User>().Property(u => u.Id)
.HasDefaultValueSql("NEWSEQUENTIALID()"); // SQL Server-side
EF Core maps Guid ↔ uniqueidentifier automatically.
ASP.NET Core minimal APIs
app.MapGet("/users/{id:guid}", (Guid id) => GetUser(id));
The :guid route constraint validates the parameter is a parseable Guid before your handler runs. 404 on invalid input — no manual validation needed.
Empty Guid (nil)
Guid.Empty // 00000000-0000-0000-0000-000000000000
Guid.Empty == Guid.Parse("00000000-0000-0000-0000-000000000000") // true
Useful as a sentinel. Don’t store it as a real entity ID; it tends to confuse libraries that treat it specially.
Common pitfalls
Guid.NewGuid()for clustered primary keys is bad on SQL Server — random Guids fragment the B-tree. UseGuid.CreateVersion7()(.NET 9+) orNEWSEQUENTIALID()for the clustered index.Guid.ToByteArray()is not RFC byte order. Always specifybigEndian: true(.NET 8+) for cross-platform interop.- Don’t store Guids as
nvarchar(36).uniqueidentifieris half the storage and indexed faster. Guid.Parse("")throwsFormatException. UseTryParsefor user input.
Cheat sheet
| Goal | Code |
|---|---|
| Random Guid | Guid.NewGuid() |
| Time-ordered (DB key, .NET 9+) | Guid.CreateVersion7() |
| Deterministic from name (.NET 9+) | Guid.CreateVersion5(ns, bytes) |
| Parse | Guid.Parse(s) or Guid.TryParse(s, out g) |
| Compact (no hyphens) | g.ToString("N") |
| RFC byte order | g.ToByteArray(bigEndian: true) |
| Empty | Guid.Empty |
Try the tools
- UUID generator — works for both UUIDs and Guids (canonical hex)
- UUID v7 generator — for new SQL Server primary keys
- UUID vs GUID — the byte-order story in detail
- UUID validator — paste a Guid, get version + timestamp