GUID Generation
Overview
RCommon abstracts GUID generation behind an IGuidGenerator interface for two reasons:
- Testability. Code that calls
Guid.NewGuid()directly cannot be controlled in unit tests. InjectingIGuidGeneratorlets tests substitute a predictable implementation. - Database performance. Standard random GUIDs cause index fragmentation in clustered indexes because their values are not monotonically increasing. Sequential GUIDs embed a timestamp in the byte layout so new rows are always appended at the end of the index.
RCommon ships two implementations out of the box: SimpleGuidGenerator and SequentialGuidGenerator. You pick one during application configuration; only one generator may be registered per application.
Installation
dotnet add package RCommon.CoreConfiguration
Configure GUID generation inside the AddRCommon() builder chain.
Simple (random) GUIDs
Uses Guid.NewGuid() internally. Suitable for non-database use cases or when the underlying database handles GUID ordering itself (e.g., NEWSEQUENTIALID() in SQL Server).
builder.Services.AddRCommon()
.WithSimpleGuidGenerator();
Sequential GUIDs
Generates GUIDs with an embedded timestamp so that successive calls produce values that sort in the correct order for a given database platform. The byte layout varies by platform; choose the appropriate SequentialGuidType for your database:
SequentialGuidType | Database |
|---|---|
SequentialAsString | MySQL, PostgreSQL |
SequentialAsBinary | Oracle |
SequentialAtEnd | SQL Server (default) |
builder.Services.AddRCommon()
.WithSequentialGuidGenerator(options =>
{
options.DefaultSequentialGuidType = SequentialGuidType.SequentialAtEnd;
});
When DefaultSequentialGuidType is left null, the generator defaults to SequentialAtEnd.
Usage
Inject IGuidGenerator wherever you need to create a new identifier:
using RCommon;
public class OrderService
{
private readonly IGuidGenerator _guidGenerator;
public OrderService(IGuidGenerator guidGenerator)
{
_guidGenerator = guidGenerator;
}
public Order CreateOrder(CustomerId customerId)
{
var orderId = _guidGenerator.Create();
return new Order(orderId, customerId);
}
}
Generating a specific sequential GUID type at runtime
SequentialGuidGenerator exposes an overload that accepts a SequentialGuidType directly, which is useful when a single application works with multiple databases:
// Only available when you have a reference to SequentialGuidGenerator directly
var generator = serviceProvider.GetRequiredService<IGuidGenerator>() as SequentialGuidGenerator;
var guid = generator?.Create(SequentialGuidType.SequentialAsString);
For most scenarios, rely on the default configured via WithSequentialGuidGenerator and inject IGuidGenerator — the concrete type is an implementation detail.
In unit tests
Substitute IGuidGenerator with a test double that returns a known value:
public class FakeGuidGenerator : IGuidGenerator
{
private readonly Guid _value;
public FakeGuidGenerator(Guid value)
{
_value = value;
}
public Guid Create() => _value;
}
// In a test
var knownId = Guid.Parse("00000000-0000-0000-0000-000000000001");
var service = new OrderService(new FakeGuidGenerator(knownId));
var order = service.CreateOrder(customerId);
Assert.Equal(knownId, order.Id);
API Summary
IGuidGenerator
| Member | Description |
|---|---|
Guid Create() | Creates and returns a new Guid. |