How to Create a GUID in C# (.NET 10 Examples + Video)

To create a GUID in C#, call Guid.NewGuid(). It gives you a random 128-bit value in the familiar 8-4-4-4-12 hex layout, so your code can make an order ID or database key without asking a server for the next number.

On .NET 9 and later you also get Guid.CreateVersion7(), which puts a timestamp up front so new IDs sort by creation time. I ran both on .NET 10, plus formatting, parsing and the all-zeros GUID that trips people up.

The Sample: GUID Order IDs for an Online Store

Every example gives IDs to orders at Summit Trail Outfitters, a Denver outdoor-gear store. Jenna Morales orders on the website and Tyler Brooks on the mobile app, with no shared counter between the apps.

Each example is a single .cs file you run with dotnet run; if you haven’t set that up, see creating a console app in VS Code.

Method 1: Create a GUID with Guid.NewGuid() (Recommended)

Use NewGuid() for almost everything: order IDs, log correlation IDs, temp file names. Here are IDs for the store’s next two orders:

// Order IDs for Summit Trail Outfitters (Denver, CO)
Guid orderId = Guid.NewGuid();
Guid nextOrderId = Guid.NewGuid();

Console.WriteLine($"Order 1:  {orderId}");
Console.WriteLine($"Order 2:  {nextOrderId}");
Console.WriteLine($"Same ID?  {orderId == nextOrderId}");
Console.WriteLine($"Version:  {orderId.Version}");
Console.WriteLine($"Length:   {orderId.ToString().Length} characters");

Output:

Order 1:  6fd19a90-21c7-4ae0-a359-6f85b7d05d67
Order 2:  c26beaf9-cb58-44d4-b4b5-0a637e69c52f
Same ID?  False
Version:  4
Length:   36 characters
Guid.NewGuid() in C# creating two different order IDs, Version 4 and a 36-character string, in VS Code on .NET 10
Two calls, two different GUIDs; both are version 4

The two IDs differ, Version is 4, and the text is 36 characters. You’ll get new values on every run.

How does this code work?

NewGuid() builds a version 4 (random) GUID with 122 random bits from the operating system, and it never returns all zeros. Printing it calls ToString in C#, which uses the lowercase “D” layout.

Important: The Version property needs .NET 9+, and a GUID isn’t a password or security token; Microsoft’s docs say NewGuid isn’t for cryptography.

Method 2: Create a Time-Ordered GUID with Guid.CreateVersion7() (.NET 9+)

Random GUIDs land all over a sorted index. CreateVersion7() starts the value with the Unix time in milliseconds instead. Same store orders, now created 10 ms apart:

Guid first = Guid.CreateVersion7();
Thread.Sleep(10);                  // next order, 10 ms later
Guid second = Guid.CreateVersion7();

Console.WriteLine($"Order 1: {first}");
Console.WriteLine($"Order 2: {second}");
Console.WriteLine($"Version: {first.Version}");
Console.WriteLine($"Sorted by time: {first.CompareTo(second) < 0}");

// The first 12 hex digits are the Unix time in milliseconds
long ms = Convert.ToInt64(first.ToString("N")[..12], 16);
Console.WriteLine(DateTimeOffset.FromUnixTimeMilliseconds(ms).ToString("u"));

var placed = new DateTimeOffset(2026, 11, 2, 14, 30, 0, TimeSpan.Zero);
Console.WriteLine(Guid.CreateVersion7(placed));

Output:

Order 1: 01a191da-5b2f-7f2c-b2fd-f1921fc705f3
Order 2: 01a191da-5b41-786e-bcf9-0e26164bcb18
Version: 7
Sorted by time: True
2026-10-31 12:06:08Z
01a19caa-c640-79b3-97ec-4b8e3fa9eebd
Guid.CreateVersion7() in C# creating two time-ordered order IDs with the same timestamp prefix, Version 7 and Sorted by time True
Version 7 GUIDs share a time prefix, so later orders sort after earlier ones

Both IDs share their leading digits, Version is 7, and the second order sorts after the first. The last line is stamped November 2, 2026, 2:30 PM UTC.

How does this code work?

The first 12 hex digits are the timestamp, so I converted them back to a date. The DateTimeOffset overload lets you stamp a known time, like an imported order.

The rest is random, so two GUIDs from the same millisecond can come out of order; that’s why the demo waits 10 ms. It’s one of the newer runtime APIs; what changed in .NET 10 covers more.

Method 3: Format a GUID with ToString(“N”), “D”, “B”, “P” and “X”

A URL wants no hyphens and the registry wants braces. ToString(format) handles both. Same kind of order ID, printed five ways:

Guid orderId = Guid.NewGuid();

foreach (string format in new[] { "N", "D", "B", "P", "X" })
    Console.WriteLine($"{format}: {orderId.ToString(format)}");

Console.WriteLine($"Default:   {orderId}");
Console.WriteLine($"Uppercase: {orderId.ToString("D").ToUpperInvariant()}");
Console.WriteLine($"N length:  {orderId.ToString("N").Length}");

Output:

N: 372302ed1e3a4c508f7ae7882f96f027
D: 372302ed-1e3a-4c50-8f7a-e7882f96f027
B: {372302ed-1e3a-4c50-8f7a-e7882f96f027}
P: (372302ed-1e3a-4c50-8f7a-e7882f96f027)
X: {0x372302ed,0x1e3a,0x4c50,{0x8f,0x7a,0xe7,0x88,0x2f,0x96,0xf0,0x27}}
Default:   372302ed-1e3a-4c50-8f7a-e7882f96f027
Uppercase: 372302ED-1E3A-4C50-8F7A-E7882F96F027
N length:  32
C# Guid ToString format specifiers N, D, B, P and X printing the same GUID, plus uppercase and the 32-character N length
One GUID, five format specifiers
SpecifierTypical use
N (32 digits)URLs, file names
D (default)JSON, logs, SQL
B / PRegistry, COM, legacy tools
XC/C++ source code

How does this code work?

The specifier only changes the text, never the 16 bytes, and any other letter throws FormatException. Output is always lowercase hex, so add ToUpperInvariant() when you need uppercase, as when you format an int as a hex string.

Method 4: Convert a String to a GUID with Guid.Parse and TryParse

IDs come back as text from APIs and CSV files. Same order ID, as a partner API sends it:

string fromApi = "{3F2A9C1E-7B4D-4E8A-9C21-5D6E7F809A1B}";
Guid orderId = Guid.Parse(fromApi);       // braces + uppercase are fine
Console.WriteLine(orderId);

string[] inputs = ["3f2a9c1e7b4d4e8a9c215d6e7f809a1b", "ORDER-1042", ""];
foreach (string text in inputs)
{
    bool ok = Guid.TryParse(text, out Guid id);
    Console.WriteLine($"'{text}' -> {(ok ? id.ToString() : "not a GUID")}");
}

Console.WriteLine($"Exact D: {Guid.TryParseExact(fromApi, "D", out _)}");
Console.WriteLine($"Exact B: {Guid.TryParseExact(fromApi, "B", out _)}");

try { Guid.Parse("ORDER-1042"); }
catch (FormatException) { Console.WriteLine("Parse threw FormatException"); }

Output:

3f2a9c1e-7b4d-4e8a-9c21-5d6e7f809a1b
'3f2a9c1e7b4d4e8a9c215d6e7f809a1b' -> 3f2a9c1e-7b4d-4e8a-9c21-5d6e7f809a1b
'ORDER-1042' -> not a GUID
'' -> not a GUID
Exact D: False
Exact B: True
Parse threw FormatException
C# Guid.Parse with braces and uppercase, Guid.TryParse returning not a GUID for ORDER-1042, and TryParseExact D vs B results
Parse accepts any standard format; TryParseExact insists on one

The braced and the 32-digit strings parse to the same GUID, the bad inputs print “not a GUID”, and TryParseExact only passes with "B".

How does this code work?

Parse and TryParse accept all five formats in any case. Use TryParse for user input instead of a try-catch block, and ParseExact when a contract allows one format only.

How Do You Check for an Empty GUID in C#?

If every ID in your table is all zeros, your code almost certainly says new Guid(). Same store, a customer ID that was never created:

Guid customerId = new Guid();             // the classic mistake
Console.WriteLine(customerId);
Console.WriteLine(customerId == Guid.Empty);
Console.WriteLine(default(Guid) == Guid.Empty);

Guid? couponId = null;
Console.WriteLine($"customerId missing? {IsMissing(customerId)}");
Console.WriteLine($"couponId missing?   {IsMissing(couponId)}");
Console.WriteLine($"NewGuid missing?    {IsMissing(Guid.NewGuid())}");

static bool IsMissing(Guid? id) => id is null || id == Guid.Empty;

Output:

00000000-0000-0000-0000-000000000000
True
True
customerId missing? True
couponId missing?   True
NewGuid missing?    False
C# new Guid() printing the all-zeros Guid.Empty value and an IsMissing helper checking Guid? null and Guid.Empty
new Guid() and default(Guid) both equal Guid.Empty

It prints all zeros, both comparisons are True, and the helper flags the zero ID and the null coupon.

How does this code work?

Guid is a struct in C#, so new Guid() just zeroes its 16 bytes, the same as default and Guid.Empty. Use NewGuid() instead, and compare with Guid.Empty to catch unset IDs.

Create a GUID from a String (Same Input, Same GUID)

Want a stable ID per customer email? Hash the text and pass the 16 bytes to the Guid constructor:

using System.Security.Cryptography;
using System.Text;

Console.WriteLine(FromText("jenna.morales@example.com"));
Console.WriteLine(FromText("jenna.morales@example.com"));
Console.WriteLine(FromText("tyler.brooks@example.com"));

// Need a short random code? Don't cut a GUID down
Console.WriteLine(RandomNumberGenerator.GetHexString(12));

// Same text in, same GUID out (an ID, not a password hash)
static Guid FromText(string text) =>
    new Guid(MD5.HashData(Encoding.UTF8.GetBytes(text)));

Output:

ed30bac7-efc4-6cd9-94e7-854e4e888714
ed30bac7-efc4-6cd9-94e7-854e4e888714
22c3da74-609b-94aa-207c-8b2a777deaaf
122392F85533

Jenna’s email gives the same GUID twice; Tyler’s differs. The last line is a random 12-character code.

How does this code work?

MD5.HashData returns exactly 16 bytes for new Guid(byte[]) (converting a string to a byte array explains the UTF-8 step). The version digit is just hash output, 6 here.

For a short random code, skip NewGuid().ToString("N").Substring(0, 15). RandomNumberGenerator.GetHexString(12) (.NET 8+) is secure, like a one-time password in ASP.NET.

Should You Use a GUID as a Primary Key in SQL Server?

You can, but random version 4 keys insert all over a clustered index and cause page splits. Here’s what I’d do for the store’s Orders table:

SetupKey generation
EF Core + SQL ServerLet EF Core generate them (sequential GUIDs by default)
Plain SQL ServerNEWSEQUENTIALID() default
PostgreSQL or sorting in .NETGuid.CreateVersion7()
Small, quiet tablesGuid.NewGuid() is fine

Note: SQL Server doesn’t sort uniqueidentifier by its bit pattern, so version 7’s time prefix doesn’t match its index order. Compare key sizes in data types in C#.

Create a GUID in Visual Studio Without Code (Tools > Create GUID)

Need one GUID for a config file? In Visual Studio, pick Tools > Create GUID, choose a format, click New GUID, then Copy. If the item is missing, add guidgen.exe from the Common7\Tools folder under Tools > External Tools.

My older video shows the dialog:

Which GUID Method Should You Use?

TaskMy pick
New ID in codeGuid.NewGuid()
IDs that sort by timeGuid.CreateVersion7()
Text from users or APIsGuid.TryParse
One GUID for a config fileTools > Create GUID

Things to Keep in Mind

  • new Guid() is all zeros. Use Guid.NewGuid().
  • CreateVersion7 needs .NET 9+. It won’t compile on .NET 8.
  • GUIDs aren’t secrets. Use RandomNumberGenerator for tokens.
  • Compare GUIDs, not strings. Parse first; uppercase and lowercase text are the same value.

If you’re moving an older app forward, these pair well with this one:

Frequently Asked Questions

How do I generate a new GUID in C#?

Call Guid.NewGuid(). Each call returns a new random version 4 GUID.

Can Guid.NewGuid() create a duplicate?

In theory, yes. With 122 random bits, the odds are too small to plan for.

What’s the difference between Guid.NewGuid() and Guid.CreateVersion7()?

NewGuid is fully random; CreateVersion7 (.NET 9+) starts with a timestamp, so values sort by time. See the System.Guid reference on Microsoft Learn.

How do I create a GUID without hyphens in C#?

Use Guid.NewGuid().ToString("N") for 32 hex digits.

Why is my GUID all zeros?

You used new Guid() or default. Call Guid.NewGuid() instead.