Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Di AddNpgsql<Context>not add NodaTime and NetTopologySuite extensions #5542

Closed
Millarex opened this issue Jan 23, 2024 · 4 comments
Closed

Comments

@Millarex
Copy link

Millarex commented Jan 23, 2024

The issue

For Npgsql 7.0 I use DI init:

var appDsRaw = new NpgsqlDataSourceBuilder(configuration.GetConnectionString("RouteDb"));
appDsRaw.UseNodaTime();
appDsRaw.UseNetTopologySuite();
var appDs = appDsRaw.Build();
services.AddDbContext<ApplicationContext>(options => options.UseNpgsql(appDs,
    x =>
    {
        x.UseNodaTime();
        x.UseNetTopologySuite();
        x.MigrationsHistoryTable("__EFMigrationsHistory", "public");
        x.MigrationsAssembly("Migrations");
    })
);

For Npgsql 8.0 I try use DI init:

services.AddNpgsql<ApplicationContext>(configuration.GetConnectionString("RouteDb"), x =>
        {
            x.UseNodaTime();
            x.UseNetTopologySuite();
            x.MigrationsHistoryTable("__EFMigrationsHistory", "public");
            x.MigrationsAssembly("Migrations");
        });

But UseNodaTime and UseNetTopologySuite not work and i had NpgsqlDbType error.
Add services.AddEntityFrameworkNpgsqlNodaTime() and services.AddEntityFrameworkNpgsqlNetTopologySuite() not resolve problem.
What I do wrong and how fix this problem?
The initialization from Npgsql 7.0 continues to work correctly but I would like to understand.

Exception message: 
Writing is not supported for parameters having NpgsqlDbType 'Geometry'
Stack trace:
Microsoft.EntityFrameworkCore.DbUpdateException : An error occurred while saving the entity changes. See the inner exception for details.
  ----> System.InvalidCastException : Writing is not supported for parameters having NpgsqlDbType 'Geometry'.
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)

Further technical details:

Npgsql version: 8.0
EfCore version: 8.0
Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite version: 8.0
Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime version: 8.0
EfCore version: 8.0
PostgreSQL version: 14
Operating system: linux\Windows
AspNet Core Web Api
Dot net 8.0

@roji
Copy link
Member

roji commented Jan 23, 2024

The above shows you switching from using NpgsqlDataSource (which is the better, newer way) to using a connection string directly, without a data source (which is now discouraged). Consider continuing using NpgsqlDataSourceBuilder and NpgsqlDataSource.

In any case, note that your 7.0 configures NodaTime/NTS twice - once at the lower Npgsql level (on NpgsqlDataSourcebuilder), and once at the upper EF level (in EF's UseNpgsql). To do the same without a data source, you'll need to use the global type mapper (docs).

I'll go ahead and close the issue as everything is behaving as expected and the question has been answered, but feel free to post further questions here if you need to.

@roji roji closed this as not planned Won't fix, can't repro, duplicate, stale Jan 23, 2024
@Millarex
Copy link
Author

Thanks for answering.
Is it planned to implement the functionality like?

 //ServiceCollection Extension
public static IServiceCollection AddNpgsqlWithDataSource<TContext>(
        this IServiceCollection serviceCollection,
        string? connectionString,
        Action<NpgsqlDbContextOptionsBuilder>? npgsqlOptionsAction = null,
        //New ParameterBuilder for NpgsqlDataSourceBuilder
        Action<NpgsqlDataSourceOptionsBuilder>? buildAction= null,
        Action<DbContextOptionsBuilder>? optionsAction = null)
        where TContext : DbContext
    {
        Check.NotNull(serviceCollection, nameof(serviceCollection));

        // Create DataSource With Action Parameters here like       
        var dataSourceRaw = new NpgsqlDataSourceBuilder(connectionString, buildAction?.Invoke(buildOptions));
        var dataSource = dataSourceRaw.Build();

        //Set ef core context With NpgsqlDataSource type
        return serviceCollection.AddDbContext<TContext>((_, options) =>
        {
            optionsAction?.Invoke(options);
            options.UseNpgsql(dataSource, npgsqlOptionsAction);
        });
    }

    private static void AddDbContexts(IServiceCollection services, IConfiguration configuration)
    {
	//Short Ef core initialisation with DS
        services.AddNpgsqlWithDataSource<ApplicationContext>(configuration.GetConnectionString("RouteDb"),
            npgsqlOptions =>
            {
                npgsqlOptions.UseNodaTime();
                npgsqlOptions.UseNetTopologySuite();
                npgsqlOptions.MigrationsHistoryTable("__EFMigrationsHistory", "public");
                npgsqlOptions.MigrationsAssembly("Migrations");
            },
	    buildOptions =>
            {
                buildOptions.UseNodaTime();
                buildOptions.UseNetTopologySuite();
            });
    }

Slightly confusing 2nd setup

 npgsqlOptions.UseNodaTime();
 npgsqlOptions.UseNetTopologySuite();

and

 buildOptions.UseNodaTime();
 buildOptions.UseNetTopologySuite();

@roji
Copy link
Member

roji commented Jan 24, 2024

@Millarex you probably want to take a look at npgsql/efcore.pg#2542, which explores an approach where configuration would only need to be specified once at the EF level, and EFCore.PG would automatically also configure the necessary things at the Npgsql level (that's a bit different from what you propose above, where the same EF method accepts to build lambdas, one for EF and one for Npgsql).

I agree that the current situation isn't ideal, but for now you'll have to configure the two layers independently.

@Millarex
Copy link
Author

Thank you very much for your answer.
I will join in thinking about a convenient way to implement it in the issues you specified.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants