Skip to content

Commit 40cee34

Browse files
committed
Document extensions to EF/Dapper and Ulid
1 parent d85aa5f commit 40cee34

File tree

2 files changed

+64
-1
lines changed

2 files changed

+64
-1
lines changed

readme.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ Alternatively, you can also invoke that method in the `OnConfiguring` method of
8888
protected override void OnConfiguring(DbContextOptionsBuilder builder) => builder.UseStructId();
8989
```
9090

91+
In addition to the natively supported primitive types, any other types that implement `IParsable<T>`
92+
and `IFormattable` get automatic support by persisting as strings. This means that you can,
93+
for example, use [Ulid](https://github.com/Cysharp/Ulid) out of the box without any further
94+
configuration or customization (since it implements both interfaces).
95+
96+
Additionally, the `UseStructId` will pick up any custom [ValueConverter<TModel,TProvider>](https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.storage.valueconversion.valueconverter-2?view=efcore-9.0)
97+
you may add to your project and register them too for convenience.
98+
9199
## Dapper
92100

93101
If you are using Dapper, the package will automatically generate required `SqlMapper.TypeHandler<T>`
@@ -107,6 +115,29 @@ as strings). This means that you can, for example, use [Ulid](https://github.com
107115
out of the box without any further configuration or customization (since it implements
108116
both interfaces).
109117

118+
Additionally, the `UseStructId` will pick up any custom `Dapper.SqlMapper.TypeHandler<T>`
119+
you may add to your project and register them too for convenience.
120+
121+
## Ulid
122+
123+
Since [Ulid](https://github.com/Cysharp/Ulid) implements `IParsable<T>` and `IFormattable`,
124+
it is supported out of the box without any further configuration or customization with both
125+
Dapper and EF Core.
126+
127+
In addition to the necessary converter/handler registration, the package will also generate
128+
a `New()` (parameterless) factory method for struct ids using `Ulid` as the value type, which
129+
in turn will use `Ulid.NewUlid()` to generate a new value. This mirrors the behavior
130+
generated for `Guid`-based struct ids.
131+
132+
```csharp
133+
public readonly partial record struct ProductId : IStructId<Ulid>;
134+
135+
public record Product(ProductId Id, string Name);
136+
137+
// Create a new product with a new Ulid-based id
138+
var product = new Product(ProductId.New(), "Product Name");
139+
```
140+
110141
## Customization via Templates
111142

112143
Virtually all the built-in interfaces and implementations are generated using the same compiled
@@ -190,6 +221,39 @@ Things to note at template expansion time:
190221
by anoother generator.
191222

192223

224+
You can also customize code generation for the `TValue`s used in your struct ids.
225+
For example, the support for automatically registering a generic `Dapper.SqlMapper.TypeHandler<T>`
226+
when a `TValue` implements both `IParsable<T>` and `IFormattable` is implemented as a
227+
so-called `TValue` template:
228+
229+
```csharp
230+
[TValue]
231+
file class TValue_TypeHandler : Dapper.SqlMapper.TypeHandler<TValue>
232+
{
233+
public override TValue Parse(object value) => TValue.Parse((string)value, null);
234+
235+
public override void SetValue(IDbDataParameter parameter, TValue value)
236+
{
237+
parameter.DbType = DbType.String;
238+
parameter.Value = value.ToString(null, null);
239+
}
240+
}
241+
242+
file partial struct TValue : IParsable<TValue>, IFormattable
243+
{
244+
public static TValue Parse(string s, IFormatProvider? provider) => throw new NotImplementedException();
245+
public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out TValue result) => throw new NotImplementedException();
246+
public string ToString(string? format, IFormatProvider? formatProvider) => throw new NotImplementedException();
247+
}
248+
```
249+
250+
If you use use [Ulid](https://github.com/Cysharp/Ulid) as a value type in your struct ids,
251+
for example, the template will be applied since `Ulid` implements both `IParsable<T>` and
252+
`IFormattable`. The generated code will look like this:
253+
254+
```csharp
255+
256+
193257
<!-- #content -->
194258
<!-- #ci -->
195259

src/StructId/Templates/EntityFrameworkValueConverter.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
77
using StructId;
88

9-
// TODO: pending making it conditionally included at compile-time
109
[TValue]
1110
file class TValue_ValueConverter : ValueConverter<TValue, string>
1211
{

0 commit comments

Comments
 (0)