{"id":24578241,"url":"https://github.com/byteaether/ulid","last_synced_at":"2026-05-07T10:02:05.964Z","repository":{"id":255436229,"uuid":"849938176","full_name":"ByteAether/Ulid","owner":"ByteAether","description":"A .NET C# library for generating and working with Universally Unique Lexicographically Sortable Identifiers (ULIDs), designed to be globally unique, sortable, human-readable, and AoT compatible, making them ideal for use in distributed systems and databases.","archived":false,"fork":false,"pushed_at":"2026-05-06T11:39:11.000Z","size":295,"stargazers_count":83,"open_issues_count":0,"forks_count":4,"subscribers_count":4,"default_branch":"main","last_synced_at":"2026-05-06T13:38:13.716Z","etag":null,"topics":["csharp","dotnet","guid","id","primary-key","ulid","uuid"],"latest_commit_sha":null,"homepage":"https://byteaether.github.io/series/byteaether-ulid/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ByteAether.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-08-30T14:54:10.000Z","updated_at":"2026-05-06T11:39:16.000Z","dependencies_parsed_at":"2025-12-16T17:07:22.542Z","dependency_job_id":null,"html_url":"https://github.com/ByteAether/Ulid","commit_stats":null,"previous_names":["byteaether/ulid"],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/ByteAether/Ulid","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ByteAether%2FUlid","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ByteAether%2FUlid/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ByteAether%2FUlid/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ByteAether%2FUlid/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ByteAether","download_url":"https://codeload.github.com/ByteAether/Ulid/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ByteAether%2FUlid/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32732349,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-07T02:14:30.463Z","status":"ssl_error","status_checked_at":"2026-05-07T02:14:29.405Z","response_time":62,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","dotnet","guid","id","primary-key","ulid","uuid"],"created_at":"2025-01-23T23:58:21.148Z","updated_at":"2026-05-07T10:02:05.957Z","avatar_url":"https://github.com/ByteAether.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ![ULID from ByteAether](assets/header.png)\r\n\r\n[![License](https://img.shields.io/github/license/ByteAether/Ulid?logo=github\u0026label=License)](https://github.com/ByteAether/Ulid/blob/main/LICENSE)\r\n[![NuGet Version](https://img.shields.io/nuget/v/ByteAether.Ulid?logo=nuget\u0026label=Version)](https://www.nuget.org/packages/ByteAether.Ulid/)\r\n[![NuGet Downloads](https://img.shields.io/nuget/dt/ByteAether.Ulid?logo=nuget\u0026label=Downloads)](https://www.nuget.org/packages/ByteAether.Ulid/)\r\n[![GitHub Build Status](https://img.shields.io/github/actions/workflow/status/ByteAether/Ulid/build-and-test.yml?logo=github\u0026label=Build%20%26%20Test)](https://github.com/ByteAether/Ulid/actions/workflows/build-and-test.yml)\r\n[![GitHub Security](https://img.shields.io/github/actions/workflow/status/ByteAether/Ulid/codeql.yml?logo=github\u0026label=Security%20Validation)](https://github.com/ByteAether/Ulid/actions/workflows/codeql.yml)\r\n\r\nA high-performance, fully compliant .NET implementation of ULIDs (Universally Unique Lexicographically Sortable Identifiers), adhering to the [official ULID specification](https://github.com/ulid/spec).\r\n\r\n## Table of Contents\r\n\r\n- [Introduction](#introduction)\r\n- [Features](#features)\r\n- [Installation](#installation)\r\n- [Usage](#usage)\r\n- [API](#api)\r\n- [Integration with Other Libraries](#integration-with-other-libraries)\r\n- [Benchmarking](#benchmarking)\r\n- [Prior Art](#prior-art)\r\n- [Contributing](#contributing)\r\n- [License](#license)\r\n\r\n## Introduction\r\n\r\n\u003cimg align=\"right\" width=\"100px\" src=\"assets/logo.png\" /\u003e\r\n\r\nULIDs are universally unique, lexicographically sortable identifiers, ideal for distributed systems and time-ordered data due to their sortability and human-readability—advantages GUIDs lack. This library offers a robust, fully compliant .NET implementation, addressing limitations found in other ULID solutions.\r\n\r\nThis implementation addresses a potential `OverflowException` that can occur when generating multiple ULIDs within the same millisecond due to the \"random\" part overflowing. To ensure dependable, unique ULID generation, our solution increments the timestamp component upon random part overflow, eliminating such exceptions. This behavior aligns with discussions in [ULID specification issue #39](https://github.com/ulid/spec/issues/39#issuecomment-2252145597).\r\n\r\nThis library uniquely addresses the predictability of monotonic ULIDs generated within the same millisecond by allowing random increments to the random component. This mitigates enumeration attack vulnerabilities, as discussed in [ULID specification issue #105](https://github.com/ulid/spec/issues/105). You can configure the random increment with a random value ranging from 1-byte (1–256) to 4-bytes (1–4,294,967,296), enhancing randomness while preserving lexicographical sortability.\r\n\r\n### ULID vs UUIDv7\r\n\r\nIn the evolution of distributed identifiers, ULIDs represent the definitive successor to both legacy GUIDs and auto-incrementing integers. While modern standards like UUIDv7 attempt to address sortability, the [RFC 9562](https://www.rfc-editor.org/rfc/rfc9562#name-monotonicity-and-counters) makes monotonicity optional, allowing implementations ([such as the native .NET provider](https://github.com/dotnet/runtime/blob/571b044582ceb7fe426b7f143c703064aa9ea4db/src/libraries/System.Private.CoreLib/src/System/Guid.cs#L306)) to sacrifice strict ordering during sub-millisecond bursts. This _\"lazy\"_ approach reintroduces the very index fragmentation and out-of-order writes that sortable IDs were meant to solve.\r\n\r\nULID addresses this by design, mandating strict lexicographical sortability and monotonic increments. By enforcing these requirements at the specification level rather than leaving them to the implementor's discretion, ULID ensures consistent, high-performance behavior across all environments. This library provides a robust, compliant implementation that guarantees this order, enabling your application to scale without the performance trade-offs of non-deterministic identifiers.\r\n\r\n## Features\r\n\r\n![.NET AOT Ready](https://img.shields.io/badge/.NET-AOT_Ready-blue)\r\n![.NET 10.0](https://img.shields.io/badge/.NET-10.0-brightgreen)\r\n![.NET 9.0](https://img.shields.io/badge/.NET-9.0-brightgreen)\r\n![.NET 8.0](https://img.shields.io/badge/.NET-8.0-brightgreen)\r\n![.NET 7.0](https://img.shields.io/badge/.NET-7.0-green)\r\n![.NET 6.0](https://img.shields.io/badge/.NET-6.0-green)\r\n![.NET 5.0](https://img.shields.io/badge/.NET-5.0-yellow)\r\n![.NET Standard 2.1](https://img.shields.io/badge/.NET-Standard_2.1-yellow)\r\n![.NET Standard 2.0](https://img.shields.io/badge/.NET-Standard_2.0-green)\r\n\r\n- **Universally Unique**: Ensures global uniqueness across systems.\r\n- **Sortable**: Lexicographically ordered for time-based sorting.\r\n- **Lock-Free Synchronization**: Monotonic generation utilizes a high-performance, **lock-free compare-and-exchange (CAS)** approach.\r\n- **Specification-Compliant**: Fully adheres to the ULID specification.\r\n- **Interoperable**: Includes conversion methods to and from GUIDs, [Crockford's Base32](https://www.crockford.com/base32.html) strings, and byte arrays.\r\n- **Ahead-of-Time (AoT) Compilation Compatible**: Fully compatible with AoT compilation for improved startup performance and smaller binary sizes.\r\n- **Error-Free Generation**: Prevents `OverflowException` by incrementing the timestamp component when the random part overflows, ensuring continuous unique ULID generation.\r\n\r\nThese features collectively make **ByteAether.Ulid** a robust and efficient choice for managing unique identifiers in your .NET applications.\r\n\r\n## Installation\r\n\r\nInstall the latest stable package via NuGet:\r\n```sh\r\ndotnet add package ByteAether.Ulid\r\n```\r\nTo install a specific [preview version](https://www.nuget.org/packages/ByteAether.Ulid/absoluteLatest), use the `--version` option:\r\n```sh\r\ndotnet add package ByteAether.Ulid --version \u003cVERSION_NUMBER\u003e\r\n```\r\n\r\n## Usage\r\n\r\nHere is a basic example of how to use the ULID implementation:\r\n```csharp\r\nusing System;\r\nusing ByteAether.Ulid;\r\n\r\n// Create a new ULID\r\nvar ulid = Ulid.New();\r\n\r\n// Convert to byte array and back\r\nbyte[] byteArray = ulid.ToByteArray();\r\nvar ulidFromByteArray = Ulid.New(byteArray);\r\n\r\n// Convert to GUID and back\r\nGuid guid = ulid.ToGuid();\r\nvar ulidFromGuid = Ulid.New(guid);\r\n\r\n// Convert to string and back\r\nstring ulidString = ulid.ToString();\r\nvar ulidFromString = Ulid.Parse(ulidString);\r\n\r\nConsole.WriteLine($\"ULID: {ulid}, GUID: {guid}, String: {ulidString}\");\r\n```\r\n\r\n### Filtering by Time Range (LINQ)\r\n\r\nSince ULIDs are lexicographically sortable and contain a timestamp, you can use `Ulid.MinAt()` and `Ulid.MaxAt()` to generate boundary ULIDs for a specific time range. This allows EF Core to translate these into efficient range comparisons (e.g., `WHERE Id \u003e= @min AND Id \u003c= @max`) in your database.\r\n\r\n```csharp\r\npublic async Task\u003cList\u003cEntity\u003e\u003e GetEntitiesFromYesterday(MyDbContext context)\r\n{\r\n    var startOfYesterday = DateTimeOffset.UtcNow.AddDays(-1).Date;\r\n    var endOfYesterday = startOfYesterday.AddDays(1).AddTicks(-1);\r\n\r\n    // Create boundary ULIDs for the time range\r\n    var minUlid = Ulid.MinAt(startOfYesterday);\r\n    var maxUlid = Ulid.MaxAt(endOfYesterday);\r\n\r\n    // This query uses the primary key index for high performance\r\n    return await context.Entities\r\n        .Where(e =\u003e e.Id \u003e= minUlid \u0026\u0026 e.Id \u003c= maxUlid)\r\n        .ToListAsync();\r\n}\r\n```\r\n\r\n### Advanced Generation\r\n\r\nYou can customize ULID generation by providing `GenerationOptions`. This allows you to control monotonicity and the source of randomness.\r\n\r\n#### Example: Monotonic ULID with Random Increments\r\n\r\nTo generate ULIDs that are monotonically increasing with a random increment, you can specify the `Monotonicity` option.\r\n```csharp\r\nusing System;\r\nusing ByteAether.Ulid;\r\nusing static ByteAether.Ulid.Ulid.GenerationOptions;\r\n\r\n// Configure options for a 2-byte random increment\r\nvar options = new Ulid.GenerationOptions\r\n{\r\n\tMonotonicity = MonotonicityOptions.MonotonicRandom2Byte\r\n};\r\n\r\n// Generate a ULID with the specified options\r\nvar ulid = Ulid.New(options);\r\n\r\nConsole.WriteLine($\"ULID with random increment: {ulid}\");\r\n```\r\n#### Example: Setting Default Generation Options\r\n\r\nYou can set default generation options for the entire application. This is useful for consistently applying specific behaviors, such as prioritizing performance over cryptographic security.\r\n```csharp\r\nusing System;\r\nusing ByteAether.Ulid;\r\nusing static ByteAether.Ulid.Ulid.GenerationOptions;\r\n\r\n// Set default generation options for the entire application\r\nUlid.DefaultGenerationOptions = new()\r\n{\r\n\tMonotonicity = MonotonicityOptions.MonotonicIncrement,\r\n\tInitialRandomSource = new PseudoRandomProvider(),\r\n\tIncrementRandomSource = new PseudoRandomProvider()\r\n};\r\n\r\n// Now, any subsequent call to Ulid.New() will use these options\r\nvar ulid = Ulid.New();\r\n\r\nConsole.WriteLine($\"ULID from pseudo-random source: {ulid}\");\r\n```\r\n\r\n## API\r\n\r\nThe `Ulid` implementation provides the following properties and methods:\r\n\r\n### Creation\r\n\r\n- `Ulid.New(GenerationOptions? options = null)`\\\r\n  Generates a new ULID using default generation options. Accepts an optional `GenerationOptions` parameter to customize the generation behavior.\r\n- `Ulid.New(DateTimeOffset dateTimeOffset, GenerationOptions? options = null)`\\\r\n  Generates a new ULID using the specified `DateTimeOffset` and default generation options. Accepts an optional `GenerationOptions` parameter to customize the generation behavior.\r\n- `Ulid.New(long timestamp, GenerationOptions? options = null)`\\\r\n  Generates a new ULID using the specified Unix timestamp in milliseconds (`long`) and default generation options. Accepts an optional `GenerationOptions` parameter to customize the generation behavior.\r\n- `Ulid.New(DateTimeOffset dateTimeOffset, ReadOnlySpan\u003cbyte\u003e random)`\\\r\n  Generates a new ULID using the specified `DateTimeOffset` and a pre-existing random byte array.\r\n- `Ulid.New(long timestamp, ReadOnlySpan\u003cbyte\u003e random)`\\\r\n  Generates a new ULID using the specified Unix timestamp in milliseconds (`long`) and a pre-existing random byte array.\r\n- `Ulid.New(ReadOnlySpan\u003cbyte\u003e bytes)`\\\r\n  Creates a ULID from an existing byte array.\r\n- `Ulid.New(Guid guid)`\\\r\n  Create from existing `Guid`.\r\n- `Ulid.MinAt(DateTimeOffset datetime)`\\\r\n  Creates the minimum possible ULID value for the specified `DateTimeOffset`.\r\n- `Ulid.MinAt(long timestamp)`\\\r\n  Creates the minimum possible ULID value for the specified Unix timestamp in milliseconds (`long`).\r\n- `Ulid.MaxAt(DateTimeOffset datetime)`\\\r\n  Creates the maximum possible ULID value for the specified `DateTimeOffset`.\r\n- `Ulid.MaxAt(long timestamp)`\\\r\n  Creates the maximum possible ULID value for the specified Unix timestamp in milliseconds (`long`).\r\n\r\n### Checking Validity\r\n\r\n- `Ulid.IsValid(string ulidString)`\\\r\n  Validates if the given string is a valid ULID.\r\n- `Ulid.IsValid(ReadOnlySpan\u003cchar\u003e ulidString)`\\\r\n  Validates if the given span of characters is a valid ULID.\r\n- `Ulid.IsValid(ReadOnlySpan\u003cbyte\u003e ulidBytes)`\\\r\n  Validates if the given byte array represents a valid ULID.\r\n\r\n### Parsing\r\n\r\n- `Ulid.Parse(ReadOnlySpan\u003cchar\u003e chars, IFormatProvider? provider = null)`\\\r\n  Parses a ULID from a character span in canonical format. The `IFormatProvider` is ignored.\r\n- `Ulid.TryParse(ReadOnlySpan\u003cchar\u003e s, IFormatProvider? provider, out Ulid result)`\\\r\n  Tries to parse a ULID from a character span in canonical format. Returns `true` if successful.\r\n- `Ulid.Parse(string s, IFormatProvider? provider = null)`\\\r\n  Parses a ULID from a string in canonical format. The `IFormatProvider` is ignored.\r\n- `Ulid.TryParse(string? s, IFormatProvider? provider, out Ulid result)`\\\r\n  Tries to parse a ULID from a string in canonical format. Returns `true` if successful.\r\n\r\n### Properties\r\n\r\n- `Ulid.MinValue`\\\r\n  Represents an empty ULID, equivalent to `default(Ulid)` and `Ulid.New(new byte[16])`.\r\n- `Ulid.MaxValue`\\\r\n  Represents the maximum possible value for a ULID (all bytes set to `0xFF`).\r\n- `Ulid.Empty`\\\r\n  Alias for `Ulid.MinValue`.\r\n- `Ulid.DefaultGenerationOptions`\\\r\n  Default configuration for ULID generation when no options are provided by the `Ulid.New(...)` call.\r\n- `.Time`\\\r\n  Gets the timestamp component of the ULID as a `DateTimeOffset`.\r\n- `.TimeBytes`\\\r\n  Gets the time component of the ULID as a `ReadOnlySpan\u003cbyte\u003e`.\r\n- `.Random`\\\r\n  Gets the random component of the ULID as a `ReadOnlySpan\u003cbyte\u003e`.\r\n\r\n### Conversions \u0026 Interoperability\r\n\r\n- `.AsByteSpan()`\\\r\n  Provides a `ReadOnlySpan\u003cbyte\u003e` representing the contents of the ULID.\r\n- `.ToByteArray()`\\\r\n  Converts the ULID to a byte array.\r\n- `.ToGuid()`\\\r\n  Converts the ULID to a `Guid`.\r\n- `.ToString(string? format = null, IFormatProvider? formatProvider = null)`\\\r\n  Converts the ULID to a canonical string representation. Format arguments are ignored.\r\n- Provides implicit operators to and from `Guid` and `string`.\r\n\r\n### Comparison Operators \u0026 .NET Interfaces\r\n\r\n- Supports all comparison operators:\\\r\n  `==`, `!=`, `\u003c`, `\u003c=`, `\u003e`, `\u003e=`.\r\n- Implements standard comparison and equality methods:\\\r\n  `CompareTo`, `Equals`, `GetHashCode`.\r\n- Implements the following .NET standard interfaces:\\\r\n  `IMinMaxValue\u003cUlid\u003e`, `IEquatable\u003cUlid\u003e`, `IIEqualityComparer\u003cUlid\u003e`, `IComparable`, `IComparable\u003cUlid\u003e`, `IComparisonOperators\u003cUlid, Ulid, bool\u003e`, `IFormattable`, `IParsable\u003cUlid\u003e`, `ISpanFormattable`, `ISpanParsable\u003cUlid\u003e`, `IUtf8SpanFormattable`, `IUtf8SpanParsable\u003cUlid\u003e`.\r\n\r\n### GenerationOptions\r\n\r\nThe `GenerationOptions` class provides detailed configuration for ULID generation, with the following key properties:\r\n\r\n- `Monotonicity`\\\r\n  Controls the behavior of ULID generation when multiple identifiers are created within the same millisecond. It determines whether ULIDs are strictly increasing or allow for random ordering within that millisecond. Available options include: `NonMonotonic`, `MonotonicIncrement` (default), `MonotonicRandom1Byte`, `MonotonicRandom2Byte`, `MonotonicRandom3Byte`, `MonotonicRandom4Byte`.\r\n\r\n- `InitialRandomSource`\\\r\n  An `IRandomProvider` for generating the random bytes of a ULID. The default `CryptographicallySecureRandomProvider` ensures robust, unpredictable ULIDs using a cryptographically secure generator.\r\n\r\n- `IncrementRandomSource`\\\r\n  An `IRandomProvider` that provides randomness for monotonic random increments. The default `PseudoRandomProvider` is a faster, non-cryptographically secure source optimized for this specific purpose.\r\n\r\nThis library comes with two default `IRandomProvider` implementations:\r\n\r\n- **`CryptographicallySecureRandomProvider`**\\\r\n  Utilizes `System.Security.Cryptography.RandomNumberGenerator` for high-quality, cryptographically secure random data.\r\n- **`PseudoRandomProvider`**\\\r\n  A faster, non-cryptographically secure option based on `System.Random`, ideal for performance-critical scenarios where cryptographic security is not required for random increments.\r\n\r\nCustom `IRandomProvider` implementations can also be created.\r\n\r\n## Integration with Other Libraries\r\n\r\n### ASP.NET Core\r\n\r\nSupports seamless integration as a route or query parameter with built-in `TypeConverter`.\r\n\r\n### System.Text.Json (.NET 5.0+)\r\n\r\nIncludes a `JsonConverter` for easy serialization and deserialization.\r\n\r\n### EF Core Integration\r\n\r\nTo use ULIDs as primary keys or properties in Entity Framework Core, you can create a custom **ValueConverter** to handle the conversion between `Ulid` and `byte[]`. Here's how to do it:\r\n\r\n#### 1. Create a custom `ValueConverter` to convert `Ulid` to `byte[]` and vice versa:\r\n```csharp\r\npublic class UlidToBytesConverter : ValueConverter\u003cUlid, byte[]\u003e\r\n{\r\n\tprivate static readonly ConverterMappingHints _defaultHints = new(size: 16);\r\n\r\n\tpublic UlidToBytesConverter() : this(_defaultHints) { }\r\n\r\n\tpublic UlidToBytesConverter(ConverterMappingHints? mappingHints = null) : base(\r\n\t\tconvertToProviderExpression: x =\u003e x.ToByteArray(),\r\n\t\tconvertFromProviderExpression: x =\u003e Ulid.New(x),\r\n\t\tmappingHints: _defaultHints.With(mappingHints)\r\n\t)\r\n\t{ }\r\n}\r\n```\r\n#### 2. Register the Converter in ConfigureConventions\r\n\r\nOnce the converter is created, you need to register it in your `DbContext`'s `ConfigureConventions` virtual method to apply it to `Ulid` properties:\r\n```csharp\r\nprotected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)\r\n{\r\n\t// ...\r\n\tconfigurationBuilder\r\n\t\t.Properties\u003cUlid\u003e()\r\n\t\t.HaveConversion\u003cUlidToBytesConverter\u003e();\r\n\t// ...\r\n}\r\n```\r\n### Dapper Integration\r\nTo use ULIDs with Dapper, you can create a custom **TypeHandler** to convert between `Ulid` and `byte[]`. Here's how to set it up:\r\n\r\n#### 1. Create the ULID Type Handler\r\n```csharp\r\nusing Dapper;\r\nusing System.Data;\r\n\r\npublic class UlidTypeHandler : SqlMapper.TypeHandler\u003cUlid\u003e\r\n{\r\n\tpublic override void SetValue(IDbDataParameter parameter, Ulid value)\r\n\t{\r\n\t\tparameter.Value = value.ToByteArray();\r\n\t}\r\n\r\n\tpublic override Ulid Parse(object value)\r\n\t{\r\n\t\treturn Ulid.New((byte[])value);\r\n\t}\r\n}\r\n```\r\n#### 2. Register the Type Handler\r\nAfter creating the `UlidTypeHandler`, you need to register it with Dapper. You can do this during application startup (e.g., in the `Main` method or `ConfigureServices` for ASP.NET Core).\r\n```csharp\r\nDapper.SqlMapper.AddTypeHandler(new UlidTypeHandler());\r\n```\r\n### MessagePack Integration\r\nTo use ULIDs with **MessagePack**, you can create a custom **MessagePackResolver** to handle the serialization and deserialization of `Ulid` as `byte[]`. Here's how to set it up:\r\n\r\n#### 1. Create the Custom Formatter\r\n\r\nFirst, create a custom formatter for `Ulid` to handle its conversion to and from `byte[]`:\r\n```csharp\r\nusing MessagePack;\r\nusing MessagePack.Formatters;\r\n\r\npublic class UlidFormatter : IMessagePackFormatter\u003cUlid\u003e\r\n{\r\n\tpublic Ulid Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)\r\n\t{\r\n\t\tvar bytes = reader.ReadByteArray();\r\n\t\treturn Ulid.New(bytes);\r\n\t}\r\n\r\n\tpublic void Serialize(ref MessagePackWriter writer, Ulid value, MessagePackSerializerOptions options)\r\n\t{\r\n\t\twriter.Write(value.ToByteArray());\r\n\t}\r\n}\r\n```\r\n#### 2. Register the Formatter\r\n\r\nOnce the `UlidFormatter` is created, you need to register it with the `MessagePackSerializer` to handle the `Ulid` type.\r\n```csharp\r\nMessagePack.Resolvers.CompositeResolver.Register(\r\n\tnew IMessagePackFormatter[] { new UlidFormatter() },\r\n\tMessagePack.Resolvers.StandardResolver.GetFormatterWithVerify\u003cUlid\u003e()\r\n);\r\n```\r\nAlternatively, you can register the formatter globally when configuring MessagePack options:\r\n```csharp\r\nMessagePackSerializer.DefaultOptions = MessagePackSerializer.DefaultOptions\r\n\t.WithResolver(MessagePack.Resolvers.CompositeResolver.Create(\r\n\t\tnew IMessagePackFormatter[] { new UlidFormatter() },\r\n\t\tMessagePack.Resolvers.StandardResolver.Instance\r\n\t));\r\n```\r\n### Newtonsoft.Json Integration\r\n\r\nTo use ULIDs with **Newtonsoft.Json**, you need to create a custom **JsonConverter** to handle the serialization and deserialization of ULID values. Here's how to set it up:\r\n\r\n#### 1. Create the Custom JsonConverter\r\n\r\nFirst, create a custom `JsonConverter` for `Ulid` to serialize and deserialize it as a `string`:\r\n```csharp\r\nusing Newtonsoft.Json;\r\nusing System;\r\n\r\npublic class UlidJsonConverter : JsonConverter\u003cUlid\u003e\r\n{\r\n\tpublic override Ulid ReadJson(JsonReader reader, Type objectType, Ulid existingValue, bool hasExistingValue, JsonSerializer serializer)\r\n\t{\r\n\t\tvar value = (string)reader.Value;\r\n\t\treturn Ulid.Parse(value);\r\n\t}\r\n\r\n\tpublic override void WriteJson(JsonWriter writer, Ulid value, JsonSerializer serializer)\r\n\t{\r\n\t\twriter.WriteValue(value.ToString());\r\n\t}\r\n}\r\n```\r\n#### 2. Register the JsonConverter\r\n\r\nOnce the `UlidJsonConverter` is created, you need to register it with **Newtonsoft.Json** to handle `Ulid` serialization and deserialization. You can register the converter globally when configuring your JSON settings:\r\n```csharp\r\nusing Newtonsoft.Json;\r\nusing System.Collections.Generic;\r\n\r\nJsonConvert.DefaultSettings = () =\u003e new JsonSerializerSettings\r\n{\r\n\tConverters = new List\u003cJsonConverter\u003e { new UlidJsonConverter() }\r\n};\r\n```\r\nAlternatively, you can specify the converter explicitly in individual serialization or deserialization calls:\r\n```csharp\r\nvar settings = new JsonSerializerSettings();\r\nsettings.Converters.Add(new UlidJsonConverter());\r\n\r\nvar json = JsonConvert.SerializeObject(myObject, settings);\r\nvar deserializedObject = JsonConvert.DeserializeObject\u003cMyObject\u003e(json, settings);\r\n```\r\n## Benchmarking\r\n\r\nBenchmarking was performed using [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) to demonstrate the performance and efficiency of this ULID implementation. Comparisons include [NetUlid](https://github.com/ultimicro/netulid) 2.1.0, [Ulid](https://github.com/Cysharp/Ulid) 1.4.1, [NUlid](https://github.com/RobThree/NUlid) 1.7.3, and `Guid` for overlapping functionalities like creation, parsing, and byte conversions.\r\n\r\nBenchmark scenarios also include comparisons against `Guid`, where functionality overlaps, such as creation, parsing, and byte conversions.\r\n\r\n*Note:*\r\n* `ByteAetherUlidR1Bc` \u0026 `ByteAetherUlidR4Bc` are configured to use a cryptographically secure random increment of 1 byte and 4 bytes, respectively, during monotonic ULID generation.\r\n* `ByteAetherUlidR1Bp` \u0026 `ByteAetherUlidR4Bp` are configured to use a pseudo-random increment of 1 byte and 4 bytes, respectively, during monotonic ULID generation.\r\n* `ByteAetherUlidP` is configured to use a pseudo-random source for the random component during non-monotonic ULID generation.\r\n\r\nThe following benchmarks were performed:\r\n```\r\nBenchmarkDotNet v0.15.8, Windows 10 (10.0.19044.7184/21H2/November2021Update)\r\nAMD Ryzen 7 3700X 3.60GHz, 1 CPU, 12 logical and 6 physical cores\r\n.NET SDK 10.0.202\r\n  [Host]     : .NET 10.0.6 (10.0.6, 10.0.626.17701), X64 RyuJIT x86-64-v3\r\n  DefaultJob : .NET 10.0.6 (10.0.6, 10.0.626.17701), X64 RyuJIT x86-64-v3\r\n\r\nJob=DefaultJob\r\n\r\n| Type            | Method             | Mean        | Error     | Gen0   | Allocated |\r\n|---------------- |------------------- |------------:|----------:|-------:|----------:|\r\n| Generate        | ByteAetherUlid     |  41.5749 ns | 0.1226 ns |      - |         - |\r\n| Generate        | ByteAetherUlidR1Bp |  47.8382 ns | 0.1300 ns |      - |         - |\r\n| Generate        | ByteAetherUlidR4Bp |  51.8018 ns | 0.1633 ns |      - |         - |\r\n| Generate        | ByteAetherUlidR1Bc |  89.3543 ns | 0.3834 ns |      - |         - |\r\n| Generate        | ByteAetherUlidR4Bc |  97.0720 ns | 0.5817 ns |      - |         - |\r\n| Generate        | NetUlid *(1)       | 161.8118 ns | 0.5833 ns | 0.0095 |      80 B |\r\n| Generate        | NUlid *(2)         |  49.6472 ns | 0.0899 ns |      - |         - |\r\n\r\n| GenerateNonMono | ByteAetherUlid     |  90.3967 ns | 0.2756 ns |      - |         - |\r\n| GenerateNonMono | ByteAetherUlidP    |  42.4365 ns | 0.1973 ns |      - |         - |\r\n| GenerateNonMono | Ulid *(3,4)        |  39.7697 ns | 0.1061 ns |      - |         - |\r\n| GenerateNonMono | NUlid              |  92.7844 ns | 0.2120 ns |      - |         - |\r\n| GenerateNonMono | Guid *(5)          |  48.5736 ns | 0.1559 ns |      - |         - |\r\n| GenerateNonMono | GuidV7 *(3,5)      |  78.4207 ns | 0.5717 ns |      - |         - |\r\n\r\n| FromByteArray   | ByteAetherUlid     |   0.2572 ns | 0.0033 ns |      - |         - |\r\n| FromByteArray   | NetUlid            |   0.6415 ns | 0.0093 ns |      - |         - |\r\n| FromByteArray   | Ulid               |   0.2726 ns | 0.0125 ns |      - |         - |\r\n| FromByteArray   | NUlid              |   0.0325 ns | 0.0028 ns |      - |         - |\r\n| FromByteArray   | Guid               |   0.0232 ns | 0.0036 ns |      - |         - |\r\n\r\n| FromGuid        | ByteAetherUlid     |   0.0000 ns | 0.0000 ns |      - |         - |\r\n| FromGuid        | NetUlid            |   1.2034 ns | 0.0798 ns |      - |         - |\r\n| FromGuid        | Ulid               |   1.4057 ns | 0.0437 ns |      - |         - |\r\n| FromGuid        | NUlid              |   0.1920 ns | 0.0089 ns |      - |         - |\r\n\r\n| FromString      | ByteAetherUlid     |  13.6761 ns | 0.0780 ns |      - |         - |\r\n| FromString      | NetUlid            |  27.1021 ns | 0.2000 ns |      - |         - |\r\n| FromString      | Ulid               |  14.9363 ns | 0.0178 ns |      - |         - |\r\n| FromString      | NUlid              |  47.7679 ns | 0.1855 ns | 0.0086 |      72 B |\r\n| FromString      | Guid               |  20.6854 ns | 0.2167 ns |      - |         - |\r\n\r\n| ToByteArray     | ByteAetherUlid     |   4.3934 ns | 0.1295 ns | 0.0048 |      40 B |\r\n| ToByteArray     | AsByteSpan *(6)    |   0.0000 ns | 0.0000 ns |      - |         - |\r\n| ToByteArray     | NetUlid            |  10.7272 ns | 0.1604 ns | 0.0048 |      40 B |\r\n| ToByteArray     | Ulid               |   4.1402 ns | 0.1311 ns | 0.0048 |      40 B |\r\n| ToByteArray     | NUlid              |   4.5557 ns | 0.1312 ns | 0.0048 |      40 B |\r\n\r\n| ToGuid          | ByteAetherUlid     |   0.0151 ns | 0.0031 ns |      - |         - |\r\n| ToGuid          | NetUlid            |   9.9122 ns | 0.0858 ns |      - |         - |\r\n| ToGuid          | Ulid               |   0.5244 ns | 0.0163 ns |      - |         - |\r\n| ToGuid          | NUlid              |   0.1479 ns | 0.0042 ns |      - |         - |\r\n\r\n| ToString        | ByteAetherUlid     |  12.4213 ns | 0.2890 ns | 0.0096 |      80 B |\r\n| ToString        | NetUlid            |  24.3216 ns | 0.5226 ns | 0.0095 |      80 B |\r\n| ToString        | Ulid               |  12.8387 ns | 0.2766 ns | 0.0095 |      80 B |\r\n| ToString        | NUlid              |  30.1114 ns | 0.1880 ns | 0.0095 |      80 B |\r\n| ToString        | Guid               |   9.0335 ns | 0.1220 ns | 0.0115 |      96 B |\r\n\r\n| CompareTo       | ByteAetherUlid     |   0.0024 ns | 0.0046 ns |      - |         - |\r\n| CompareTo       | NetUlid            |   3.4035 ns | 0.0304 ns |      - |         - |\r\n| CompareTo       | Ulid               |   0.0002 ns | 0.0005 ns |      - |         - |\r\n| CompareTo       | NUlid              |   0.4194 ns | 0.0083 ns |      - |         - |\r\n\r\n| Equals          | ByteAetherUlid     |   0.0021 ns | 0.0023 ns |      - |         - |\r\n| Equals          | NetUlid            |   1.0259 ns | 0.0086 ns |      - |         - |\r\n| Equals          | Ulid               |   0.0047 ns | 0.0085 ns |      - |         - |\r\n| Equals          | NUlid              |   0.0036 ns | 0.0037 ns |      - |         - |\r\n| Equals          | Guid               |   0.0001 ns | 0.0004 ns |      - |         - |\r\n\r\n| GetHashCode     | ByteAetherUlid     |   0.0012 ns | 0.0024 ns |      - |         - |\r\n| GetHashCode     | NetUlid            |   9.9326 ns | 0.0403 ns |      - |         - |\r\n| GetHashCode     | Ulid               |   0.0000 ns | 0.0000 ns |      - |         - |\r\n| GetHashCode     | NUlid              |   6.0461 ns | 0.0258 ns |      - |         - |\r\n| GetHashCode     | Guid               |   0.0000 ns | 0.0000 ns |      - |         - |\r\n```\r\n\r\nExisting competitive libraries exhibit various deviations from the official ULID specification or present drawbacks:\r\n  1. `NetUlid`: Only supports monotonicity within a single thread.\r\n  2. `NUlid`: Requires custom wrappers and state management for monotonic generation.\r\n  3. `Ulid` \u0026 `GuidV7`: Do not implement monotonicity.\r\n  4. `Ulid`: Utilizes a cryptographically non-secure `XOR-Shift` for random value generation, with only the initial seed being cryptographically secure.\r\n  5. `Guid` \u0026 `GuidV7`: [The Guid documentation explicitly states](https://learn.microsoft.com/en-us/dotnet/api/system.guid.newguid?view=net-9.0#remarks) that its random component may not be generated using a cryptographically secure random number generator (RNG), and that `Guid` values should not be used for cryptographic purposes.\r\n  6. `AsByteSpan`: ByteAether.Ulid provides a `AsByteSpan()` method to read the underlying byte array as a `ReadOnlySpan\u003cbyte\u003e`.\r\n\r\nFurthermore, both `NetUlid` and `NUlid`, despite offering monotonicity, are susceptible to `OverflowException` due to random-part overflow.\r\n\r\nThis implementation demonstrates performance comparable to or exceeding its closest competitors. Crucially, it provides the most complete adherence to the official ULID specification, ensuring superior reliability and robustness for your applications compared to other libraries.\r\n\r\n## Prior Art\r\n\r\nMuch of this implementation is either based on or inspired by existing works. This library is standing on the shoulders of giants.\r\n\r\n  * [NetUlid](https://github.com/ultimicro/netulid)\r\n  * [Ulid](https://github.com/Cysharp/Ulid)\r\n  * [NUlid](https://github.com/RobThree/NUlid)\r\n  * [Official ULID specification](https://github.com/ulid/spec)\r\n  * [Crockford's Base32](https://www.crockford.com/base32.html)\r\n\r\n## Contributing\r\n\r\nWe welcome all contributions! You can:\r\n\r\n * **Open a Pull Request:** Fork the repository, create a branch, make your changes, and submit a pull request to the `main` branch.\r\n * **Report Issues:** Found a bug or have a suggestion? [Open an issue](https://github.com/ByteAether/Ulid/issues) with details.\r\n\r\nThank you for helping improve the project!\r\n\r\n## License\r\n\r\nThis project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyteaether%2Fulid","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbyteaether%2Fulid","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyteaether%2Fulid/lists"}