EF Provider 8.2.2 does not allow me to override ValueConverter on nullable types

I probably understand why - 8.2.2 introduced automatic ValueConverters for nullable types. But it would still be good if i could override those when needed.

For example i have this converter for DateTime:

private static readonly ValueConverter<DateTime?, DateTime?> _nullableDateTimeConverter = new(
        v => v.HasValue ? v.Value.ToUniversalTime() : v,
        v => v.HasValue ? DateTime.SpecifyKind(v.Value, DateTimeKind.Utc) : v);

But the provider is shouting at me with error

2025-02-06T14:15:12       System.NotSupportedException: Unsupported ValueConverter for Nullable<DateTime> encountered. Null conversion must be left to EF Core. If using HasConversion with conversion expressions directly move them to constructor arguments of a ValueConverter instead. For example: mb.Entity<AccountStatusDefinition>().Property(e => e.UpdatedAt).HasConversion(x => x, y => y) becomes .HasConversion(new ValueConverter(x => x, y => y));
2025-02-06T14:15:12          at MongoDB.EntityFrameworkCore.Serializers.BsonSerializerFactory.CreateValueConverterSerializer(ValueConverter converter, IReadOnlyProperty property)
2025-02-06T14:15:12          at MongoDB.EntityFrameworkCore.Serializers.BsonSerializerFactory.CreateTypeSerializer(IReadOnlyProperty property)
2025-02-06T14:15:12          at MongoDB.EntityFrameworkCore.Serializers.BsonSerializerFactory.GetPropertySerializationInfo(IReadOnlyProperty property)
2025-02-06T14:15:12          at MongoDB.EntityFrameworkCore.Storage.MongoUpdate.WriteProperty(IBsonWriter writer, Object value, IProperty property)
2025-02-06T14:15:12          at MongoDB.EntityFrameworkCore.Storage.MongoUpdate.WriteNonKeyProperties(IBsonWriter writer, IUpdateEntry entry, Func`2 propertyFilter)
2025-02-06T14:15:12          at MongoDB.EntityFrameworkCore.Storage.MongoUpdate.WriteEntity(IBsonWriter writer, IUpdateEntry entry, Func`2 propertyFilter)
2025-02-06T14:15:12          at MongoDB.EntityFrameworkCore.Storage.MongoUpdate.ConvertAdded(IUpdateEntry entry)
2025-02-06T14:15:12          at MongoDB.EntityFrameworkCore.Storage.MongoUpdate.Create(IUpdateEntry entry)
2025-02-06T14:15:12          at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
2025-02-06T14:15:12          at System.Linq.Enumerable.OfTypeIterator[TResult](IEnumerable source)+MoveNext()
2025-02-06T14:15:12          at MongoDB.EntityFrameworkCore.Storage.MongoUpdateBatch.CreateBatches(IEnumerable`1 updates)+MoveNext()
2025-02-06T14:15:12          at MongoDB.EntityFrameworkCore.Storage.MongoDatabaseWrapper.WriteBatchesAsync(IEnumerable`1 updates, IClientSessionHandle session, CancellationToken cancellationToken)
2025-02-06T14:15:12          at MongoDB.EntityFrameworkCore.Storage.MongoDatabaseWrapper.SaveChangesAsync(IList`1 entries, CancellationToken cancellationToken)
2025-02-06T14:15:12          at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
2025-02-06T14:15:12          at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
2025-02-06T14:15:12          at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
2025-02-06T14:15:12          at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)

Is there a way to work around that?

Hi Jacob.

This is unfortunately a limitation of EF Core itself.

You can track their progress on it at Allow HasConversion/ValueConverters to convert nulls · Issue #13850 · dotnet/efcore · GitHub but I don’t know if it’s even prioritised for EF 10.

In your specific example you should be able to just have the value converter deal values and not nulls and let EF do what you are currently doing (propogating the null). e.g.

private static readonly ValueConverter<DateTime, DateTime> _dateTimeConverter = new(
        v => v.ToUniversalTime(),
        v => DateTime.SpecifyKind(v.Value, DateTimeKind.Utc));
`

Does that not work for you?

I had both, for nullable and non-nullable DateTime. But i removed the nullable one and all integration tests passed. So i think it wasn’t really needed :slight_smile:

1 Like

Hi Jakub, glad that worked out for you.

Do you have any recommendations as to improvements for the exception message? I tried to make it quite verbose but I suspect it still wasn’t as clear as it could be.

No, i think it’s pretty clear. I just hit me since i’ve had this value converter before and postgres provide didn’t care at any point. That’s why the “Null conversion must be left to EF Core.” wasn’t that clear to me, since it worked before and it only throws in the Mongo EF provider

Does it actually work in the PostgreSQL provider or is it being ignored and the non-nullable one being used instead?

That’s a great question i do not have answer for. It was introduced by somebody else and as far as I can tell it wasn’t really tested. So it probably is just ignored :grin:

@Damien_Guard Integration tests did not detect that, but there was a reason to use the nullable DateTime converter. This was a fix to enforce all DateTime to be stored as UtcKind. Source: c# - Entity Framework DateTime and UTC - Stack Overflow

I think in Mongo EF provider the same thing is to use the SetDateTimeKind ? So i just branch in Configuration and set that for Mongo and leave the nullable converter for the rest.

The usecase is that we have a microservice system and we are sharing a lot of EF configuration in shared libraries. Some microservices are using postgres and others mongodb. Hence the branching needed here

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.