JoinRpg.Common.PrimitiveTypes.SourceGenerator
2026.9.533
dotnet add package JoinRpg.Common.PrimitiveTypes.SourceGenerator --version 2026.9.533
NuGet\Install-Package JoinRpg.Common.PrimitiveTypes.SourceGenerator -Version 2026.9.533
<PackageReference Include="JoinRpg.Common.PrimitiveTypes.SourceGenerator" Version="2026.9.533" />
<PackageVersion Include="JoinRpg.Common.PrimitiveTypes.SourceGenerator" Version="2026.9.533" />
<PackageReference Include="JoinRpg.Common.PrimitiveTypes.SourceGenerator" />
paket add JoinRpg.Common.PrimitiveTypes.SourceGenerator --version 2026.9.533
#r "nuget: JoinRpg.Common.PrimitiveTypes.SourceGenerator, 2026.9.533"
#:package JoinRpg.Common.PrimitiveTypes.SourceGenerator@2026.9.533
#addin nuget:?package=JoinRpg.Common.PrimitiveTypes.SourceGenerator&version=2026.9.533
#tool nuget:?package=JoinRpg.Common.PrimitiveTypes.SourceGenerator&version=2026.9.533
JoinRpg.Common.PrimitiveTypes.SourceGenerator
Генерирует код для своих типизированных Id и строковых значений: пишете partial record с
атрибутом — получаете разбор, ToString, сравнение; для Id — ещё JSON-конвертер и TypeConverter,
для строк — проверку длины. Для готовых типов (UserIdentification, Email) он не нужен — хватит
JoinRpg.Common.PrimitiveTypes.
dotnet add package JoinRpg.Common.PrimitiveTypes
dotnet add package JoinRpg.Common.PrimitiveTypes.SourceGenerator
У генератора в .csproj допишите PrivateAssets="all". Версии обоих пакетов держите
одинаковыми: они выходят вместе, и сгенерированный код зовёт хелперы из PrimitiveTypes той же
версии. Нужен net10.0.
[TypedEntityId]
[TypedEntityId]
public partial record GameIdentification(int Value);
[TypedEntityId]
public partial record GamePlayerIdentification(GameIdentification GameId, int PlayerId);
Генерируется:
ToString()—GameId(5), для составного —GamePlayerId(5-12);ISpanParsable<T>:Parse/TryParseпринимаютGameId(5),Game(5),GameIdentification(5)и просто5; числа составного Id разделяются-или,;Id— последнее число;IComparable<T>— сравнивает только последнее число (у составногоGameIdне учитывается); не генерируется, еслиCompareToнаписан руками;FromOptional(...)с nullable-параметрами —nullна входе даётnull;[JsonConverter]— в JSON Id пишется строкой, тем же форматом, чтоToString(); работает и для ключей словаря;[TypeConverter]— Id можно держать в опциях конфигурации;- для Id из одного числа — неявное приведение к
int/longиIEntityId<T, TValue>(по нему пакетJoinRpg.Common.EntityFrameworkCoreмаппит колонку EF Core); - для составного — конструктор из плоских чисел (
new GamePlayerIdentification(5, 12)) иFromList(IEnumerable<int>, GameIdentification); если первый параметр сам составной Id — ещё свойства его вложенных Id.
Руками можно написать только CompareTo; свои ToString, Parse и FromOptional дадут CS0111.
Знак проверяет только Parse и только у Id из одного числа: Parse("0") не пройдёт,
new GameIdentification(0) пройдёт.
| Параметр | По умолчанию | Зачем |
|---|---|---|
ShortName |
для XxxIdentification — XxxId, иначе имя типа |
префикс в ToString и Parse |
AdditionalPrefixes |
— | старые префиксы, которые Parse тоже примет |
AllowNonPositive |
false |
разрешить в Parse 0 и отрицательные; только для Id из одного long |
[TypedStringValue]
[TypedStringValue(MaxLength = 100)]
public partial record GameTitle(string Value);
Генерируется: константы MinLength/MaxLength, неявные приведения из string и в string,
FromOptional(string?) (пустая строка или пробелы дают null), Equals(string?), сравнение,
ToString() вида GameTitle(...) (для вывода — .Value), Parse/TryParse.
Значение проверяют приведение из string, FromOptional и Parse: обрезают пробелы и
проверяют длину, при ошибке — ArgumentException (TryParse возвращает false). Конструктор
new GameTitle(...) и десериализация из JSON не проверяют ничего. Своего JSON-конвертера у
строк нет: в JSON это объект {"Value": "..."}.
| Параметр | По умолчанию | Зачем |
|---|---|---|
MinLength |
1 | минимальная длина после обрезки |
MaxLength |
999 | int.MaxValue — без проверки длины |
Trim |
true |
обрезать пробелы по краям |
Своя проверка или нормализация — статический CustomValidateAndCanonicalize(string) → string в
том же типе. Вызывается после обрезки и проверки длины, результат становится Value. Отказ —
ArgumentException: другое исключение TryParse пробросит.
internal static string CustomValidateAndCanonicalize(string value)
{
if (!value.Contains('@'))
{
throw new ArgumentException("Incorrect email", nameof(value));
}
return value;
}
Генератор пропускает приведения из string и в string, если они написаны руками; Parse и
TryParse — если в типе есть любой статический Parse или TryParse; CompareTo и
Equals(string?) — если тип сам объявляет IComparable<T> или IEquatable<string>. Остальное,
написанное руками, даст CS0111: ToString, FromOptional(string?), а также
CompareTo/Equals(string?) без объявленного интерфейса.
Когда генератор молчит
Тип — public partial record (иначе сразу CS0260 или CS0262). Генератор ничего не создаёт и
ничего не сообщает, если:
- нет позиционных параметров;
- у Id есть параметр не
int, неlongи не[TypedEntityId]-тип из того же проекта (тип из другой сборки, напримерUserIdentificationиз пакета, не разворачивается); - у Id нет ни одного параметра
int/long(Id из одних вложенных Id не генерируется); - у строки не ровно один параметр
string.
Ошибки тогда появятся дальше: нет Parse или приведения из string. Кроме того, long
допустим только единственным параметром, а чисел в Id — не больше четырёх.
Как устроен и как правится сам генератор — docs/source-generator.md.
Learn more about Target Frameworks and .NET Standard.
-
.NETStandard 2.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on JoinRpg.Common.PrimitiveTypes.SourceGenerator:
| Package | Downloads |
|---|---|
|
JoinRpg.Common.PrimitiveTypes
Common primitive types for JoinRpg (emails, user IDs, etc.) |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2026.9.533 | 84 | 10/29/2026 |
| 2026.9.438 | 443 | 10/27/2026 |
| 2026.9.363 | 347 | 10/22/2026 |
| 2026.9.217 | 90 | 10/16/2026 |
| 2026.8.421 | 99 | 10/4/2026 |
| 2026.8.383 | 97 | 10/4/2026 |
| 2026.8.256 | 1,504 | 9/29/2026 |
| 2026.8.12 | 792 | 9/6/2026 |
| 2026.8.0 | 590 | 9/2/2026 |
| 2026.7.34 | 249 | 8/26/2026 |
| 2026.7.32 | 294 | 8/26/2026 |
| 2026.7.14 | 155 | 8/24/2026 |
| 2026.7.10 | 128 | 8/23/2026 |
| 2026.5.26 | 472 | 8/4/2026 |
| 2026.3.87 | 1,034 | 5/12/2026 |
| 2026.3.77 | 176 | 5/11/2026 |
| 2026.3.76 | 125 | 5/11/2026 |
| 2026.3.72 | 166 | 5/8/2026 |