dotnet / dotnet/EntityFramework.Docs

Document the things needed to add an extension with type mappings

Open
#3,500 1 comment 0 reactions 0 assignees View on GitHub
area-change-tracking area-value-conversions
Dominant language
Mermaid
Stars
1.7k
Forks
2k
Avg merge
7d 23h
Merged PRs (30d)
16

Description

Based on the excellent work of Shay Rojanski and Steve Rasmusmen to integrate NodaTime, I tried to do the same with Ulid.

The wip repo is here https://github.com/linoafr/UlidIntegration with unit tests if this can help.

Ulid api works almost like Guid api

```csharp
Ulid.NewUlid() // Produce 01FJ407WVXP0N8JNEM763SW753
Ulid.Empty // Produce 00000000000000000000000000
Ulid.MinValue // Produce 00000000000000000000000000
Ulid.MaxValue // Produce 76EZ91ZPZZZZZZZZZZZZZZZZZZ
```

Ulid can be stored in a database as char(26) or binary(16).

I think the relevant code is here for char(26).

```csharp
internal class UlidToStringValueConverter : ValueConverter
{
private static readonly ConverterMappingHints DefaultHints = new(size: 26);

public UlidToStringValueConverter() : this(null)
{
}

internal UlidToStringValueConverter(ConverterMappingHints? mappingHints = null)
: base(
convertToProviderExpression: x => x.ToString(),
convertFromProviderExpression: x => Ulid.Parse(x),
mappingHints: DefaultHints.With(mappingHints))
{
}
}

internal class UlidToStringTypeMapping : RelationalTypeMapping
{
private static readonly UlidToStringValueConverter ValueConverter = new();

public UlidToStringTypeMapping()
: base(new RelationalTypeMappingParameters(new CoreTypeMappingParameters(
typeof(Ulid),
ValueConverter),
"char",
StoreTypePostfix.Size,
System.Data.DbType.AnsiStringFixedLength,
unicode: false,
size: 26,
fixedLength: true))
{
}

protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters)
=> new UlidToStringTypeMapping();
}

internal class UlidToStringTypeMappingSourcePlugin : IRelationalTypeMappingSourcePlugin
{
public RelationalTypeMapping? FindMapping(in RelationalTypeMappingInfo mappingInfo)
{
if (mappingInfo.ClrType == typeof(Ulid))
{
return new UlidToStringTypeMapping();
}

return null;
}
}

```

## This query is working (SqlServer and Npgsql)

```csharp
var myId = Ulid.NewUlid();
var e = db.MyEntity.SingleOrDefaultAsync(p => p.Id == myId);
```

## This queries are not working (SqlServer and Npgsql)

### Ulid.NewUlid()

```csharp
var e = db.MyEntity.SingleOrDefaultAsync(p => p.Id == Ulid.NewUlid());
```

```
Message: 
Npgsql.PostgresException : 42601: syntax error at or near "FJ2B2J3BMN1AVKT6SBA15D8R"

POSITION: 64

Stack Trace: 
NpgsqlConnector.g__ReadMessageLong|215_0(NpgsqlConnector connector, Boolean async, DataRowLoadingMode dataRowLoadingMode, Boolean readingNotifications, Boolean isReadingPrependedMessage)
NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
NpgsqlCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken)
NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
AsyncEnumerator.MoveNextAsync()
ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
UlidTests.Query_with_ulid_new_method() line 45
--- End of stack trace from previous location ---
```

### Ulid.MinValue (Unit test is green for SqlServer but I have a project with this error message "Error converting data type varchar to numeric")

```csharp
var e = db.MyEntity.SingleOrDefaultAsync(p => p.Id == Ulid.MinValue);
```

```
Message: 
Npgsql.PostgresException : 42883: operator does not exist: character = integer

POSITION: 60

Stack Trace: 
NpgsqlConnector.g__ReadMessageLong|215_0(NpgsqlConnector connector, Boolean async, DataRowLoadingMode dataRowLoadingMode, Boolean readingNotifications, Boolean isReadingPrependedMessage)
NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
NpgsqlCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken)
NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
AsyncEnumerator.MoveNextAsync()
ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
UlidTests.Query_with_ulid_minvalue() line 52
--- End of stack trace from previous location ---
```

### Ulid.MaxValue

```csharp
var e = db.MyEntity.SingleOrDefaultAsync(p => p.Id == Ulid.MaxValue);
```

```
Message: 
Npgsql.PostgresException : 42601: syntax error at or near "EZ91ZPZZZZZZZZZZZZZZZZZZ"

POSITION: 64

Stack Trace: 
NpgsqlConnector.g__ReadMessageLong|215_0(NpgsqlConnector connector, Boolean async, DataRowLoadingMode dataRowLoadingMode, Boolean readingNotifications, Boolean isReadingPrependedMessage)
NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
NpgsqlCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken)
NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
AsyncEnumerator.MoveNextAsync()
ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
UlidTests.Query_with_ulid_maxvalue() line 59
--- End of stack trace from previous location ---
```

### Include provider and version information

EF Core version: 6.0.0-rc.2.21480.5
Database provider: SqlServer & Npgsql
Target framework: 6.0.0-rc.2.21480.5
Operating system: Windows 10
IDE: Visual Studio 2022 17.0.0 Preview 5.0

Thanks in advance for your help

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.