Allow per-connection, non-global plugin usage
See original GitHub issuethrough this https://github.com/npgsql/Npgsql.EntityFrameworkCore.PostgreSQL/issues/568#issuecomment-412329701 and https://github.com/npgsql/Npgsql.EntityFrameworkCore.PostgreSQL/issues/648#issuecomment-423617436 , seems right now, the BCL date/time types are not supported when the NodaTime plugin is configured.
, and from this Long story short, when you set up the ADO NodaTime plugin, it "hijacks" NpgsqlDbType.Timestamp and routes it to the NodaTime type handler, instead of the built-in DateTime type handler.
and http://www.npgsql.org/doc/types/nodatime.html#setup shows :
// Place this at the beginning of your program to use NodaTime everywhere (recommended)
NpgsqlConnection.GlobalTypeMapper.UseNodaTime();
// Or to temporarily use NodaTime on a single connection only:
conn.TypeMapper.UseNodaTime();
seems that, npgsql supports using plugins in connection level, while the extension method UseNodaTime
shows :
// TODO: Global-only setup at the ADO.NET level for now, optionally allow per-connection?
NpgsqlConnection.GlobalTypeMapper.UseNodaTime();
I am wondering that if this can be implemented in per-connection way or is there any workaround here for now?
My situation here is , we have not the whole control with code we use, e.g: use aspnet.identity
, hangfire.postgresql
, these libs may use ef.core
, may use dapper
or raw npgsql
. While our own dbcontext
use the nodatime plugin style, if some dbcontext config using nodatime plugin, it will break all BCL ways to use datetime, even in raw npgsql.
Does the way check plugins and add corresponding type-mappers in NpgsqlRelationalConnection solve the problem ?
right now, this works for me, does this a reasonable way to fix situation here, do you have any suggestion ?
// ensure conn in di is request scoped
services.AddScoped<DbConnection>(provider =>
{
var conn = new NpgsqlConnection("conn str");
conn.Open();
// register typp mapper on connection level, not global level
conn.TypeMapper.UseNodaTime();
return conn;
})
.AddHangfire(o =>
{
// since nodatime type mapper region on connection level, it will not affect BCL datetime on hangfire use dapper with raw npgsql
o.UsePostgreSqlStorage("conn str");
})
.AddEntityFrameworkNpgsql()
.AddDbContext<XXXDbContext>((p, options) =>
{
// since conn is registered with nodatime type mapper on connection level before, ef.core will work correctly
var conn = p.GetRequiredService<DbConnection>();
options.UseNpgsql(conn,b =>
{
// DON'T USE `UseNodaTime()` extension method, since it will register nodatime on global type mapper, and it will affect BCL datetime on the application level
// UsePlugin here to register plugin on ef.core plugin model
b.UsePlugin(new NodaTimePlugin());
});
});
Issue Analytics
- State:
- Created 5 years ago
- Reactions:2
- Comments:30 (12 by maintainers)
@fireflamemm
Yes there is a workaround 😊 Let me first explain why you are seeing the exception, although the NodaTime plugin now supports BCL types.
Dapper maps properties by using the non-generic IDataRecord indexer. You can check that if you look at dappers SqlMapper code on line 3492 and line 2962.
The NodaTime plugin overrides the default type mapping and therefore it will try to read
Instant
instead ofDateTime
. If dapper would use the explicitIDataRecord.GetDateTime(int i)
method, the exception wouldn’t occur because in that case, the plugin would correctly return the expected DateTime value.Fortunately, dapper supports registering custom TypeHandler instances which will solve this issue. So to fix this issue you need to:
Good news for everybody waiting for a solution to this issue: The npgsql noda time plugin has recently been modified to support BCL types besides nodatime types. See MR https://github.com/npgsql/npgsql/pull/3124 for details. This should resolve the specific pain points described in this thread as now BCL datetime types can co-exist with Nodatime types, without any workarounds or performance issues.
Fix has been merged and is targeted for https://github.com/npgsql/npgsql/milestone/70