question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Allow per-connection, non-global plugin usage

See original GitHub issue

through 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:closed
  • Created 5 years ago
  • Reactions:2
  • Comments:30 (12 by maintainers)

github_iconTop GitHub Comments

11reactions
davidrothcommented, Nov 21, 2020

@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 of DateTime. If dapper would use the explicit IDataRecord.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:

  1. Copy the following class in your project:
// Needed because NodaTime Plugin overrides the default mappings and Dapper uses non-generic IDataReader.GetValue functions which return Instant instead of DateTime.
public class DapperDateTimeTypeHandler : SqlMapper.TypeHandler<DateTime>
{
    public override DateTime Parse(object value)
    {
        if (value is DateTime dateTime)
        {
            return dateTime;
        }
        else if (value is NodaTime.Instant i)
        {
            return i.ToDateTimeUtc();
        }
        throw new ArgumentException($"Invalid value of type '{value?.GetType().FullName}' given. DateTime or NodaTime.Instant values are supported.", nameof(value));
    }

    public void SetValue(IDbDataParameter parameter, object value)
        => parameter.Value = value;

    public override void SetValue(IDbDataParameter parameter, DateTime value)
        => parameter.Value = value;
}
  1. Add the following line somewhere in Startup.cs. I prefer adding it directly in the services.AddHangfire call:
services.AddHangfire(config =>
{
    config.UsePostgreSqlStorage(appSettings.ConnectionString);
    Dapper.SqlMapper.AddTypeHandler(new DapperDateTimeTypeHandler()); // <--- register the type handler
});
5reactions
davidrothcommented, Sep 12, 2020

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

Read more comments on GitHub >

github_iconTop Results From Across the Web

Allow per-connection, non-global plugin usage
Allow per-connection, non-global plugin usage ... Timestamp and routes it to the NodaTime type handler, instead of the built-in DateTime type ...
Read more >
How to Use Kafka Connect - Get Started
This document provides concepts and instructions for getting started with Kafka Connect.
Read more >
NRPE DOCUMENTATION
The NRPE addon is designed to allow you to execute Nagios plugins on remote ... The most straight forward use of the NRPE...
Read more >
Remote Development Tips and Tricks
Visual Studio Code Remote Development troubleshooting tips and tricks for SSH, Containers, and the Windows Subsystem for Linux (WSL)
Read more >
Lua Plugins Containing Local Variables
When in Lua you write a local statement, you are declaring that the following identifiers will denote local variables, whether or not those ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found