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

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

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

| Specifier | Typical use |
|---|---|
N (32 digits) | URLs, file names |
D (default) | JSON, logs, SQL |
B / P | Registry, COM, legacy tools |
X | C/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

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

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:
| Setup | Key generation |
|---|---|
| EF Core + SQL Server | Let EF Core generate them (sequential GUIDs by default) |
| Plain SQL Server | NEWSEQUENTIALID() default |
| PostgreSQL or sorting in .NET | Guid.CreateVersion7() |
| Small, quiet tables | Guid.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?
| Task | My pick |
|---|---|
| New ID in code | Guid.NewGuid() |
| IDs that sort by time | Guid.CreateVersion7() |
| Text from users or APIs | Guid.TryParse |
| One GUID for a config file | Tools > Create GUID |
Things to Keep in Mind
new Guid()is all zeros. UseGuid.NewGuid().CreateVersion7needs .NET 9+. It won’t compile on .NET 8.- GUIDs aren’t secrets. Use
RandomNumberGeneratorfor 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:
- Upgrade a project from .NET 8 to .NET 10
- New C# 14 features worth using
- Check that a string isn’t null or empty
- Generate random numbers in a range without repeats
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.
Bijay Kumar is a renowned software engineer, accomplished author, and distinguished Microsoft Most Valuable Professional (MVP) specializing in SharePoint. With a rich professional background spanning over 15 years, Bijay has established himself as an authority in the field of information technology. He possesses unparalleled expertise in multiple programming languages and technologies such as ASP.NET, ASP.NET MVC, C#.NET, and SharePoint, which has enabled him to develop innovative and cutting-edge solutions for clients across the globe. Read more…