npgsql: Prepared statement incompatible with NpgsqlDataSource
For Npgsql 7.0, it is recommended to use both NpgsqlDataSource and Prepared statements.
https://www.npgsql.org/doc/basic-usage.html https://www.npgsql.org/doc/prepare.html
However when we combine them together, the server error is An unhandled exception occurred while processing the request. NotSupportedException: Connection and transaction access is not supported on commands created from DbDataSource. Npgsql.NpgsqlDataSourceCommand.PrepareAsync(CancellationToken cancellationToken)
Steps to reproduce
var connectionString = "Host=myserver;Username=mylogin;Password=mypass;Database=mydatabase";
await using var dataSource = NpgsqlDataSource.Create(connectionString);
await using (var cmd = dataSource.CreateCommand("Select * from table where col1=$1 and col2=$2")
{
cmd.Parameters.AddWithValue("value1");
cmd.Parameters.AddWithValue("value2");
await cmd.PrepareAsync();
await using (var reader = await cmd.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
// Do something
}
}
};
The issue
If I comment out the line “await cmd.PrepareAsync();”, then it is running correctly. So a combination of NpgsqlDataSource and Prepared statement gives error.
If you are seeing an exception, include the full exceptions details (message and stack trace).
Exception message:
Stack trace:
NotSupportedException: Connection and transaction access is not supported on commands created from DbDataSource.
Npgsql.NpgsqlDataSourceCommand.PrepareAsync(CancellationToken cancellationToken)
Campus.Pages.handler.CustomListModel.GetData_FeedAsync()
Campus.Pages.handler.CustomListModel.GetData_FeedAsync()
Campus.Pages.handler.CustomListModel.GetData_FeedAsync()
Campus.Pages.handler.CustomListModel.OnGetAsync(string t)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory+NonGenericTaskHandlerMethod.Execute(object receiver, object[] arguments)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeHandlerMethodAsync()
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeNextPageFilterAsync()
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Rethrow(PageHandlerExecutedContext context)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeInnerFilterAsync()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
Further technical details
Dotnet version: 7.0 Npgsql version: 7.0 PostgreSQL version: 16.0 Operating system: Windows 10
About this issue
- Original URL
- State: closed
- Created 8 months ago
- Comments: 15 (9 by maintainers)
@BillJones50 the things inside the BatchCommands list are NpgsqlBatchCommands, which have a Parameters property just like NpgsqlCommand. So you can do the following:
For usage questions such as the above, please open questions on e.g. stackoverflow; github issues are generally more for when you believe you’ve hit a bug, have a feature request, etc.
If your calculations take any time, I’d recommend closing the connection while they’re done; this frees up the connection for use by other threads, rather than keeping it “locked” and idle as the calculations are done.
Otherwise there’s a small perf overhead to closing and then reopening the connection, but Npgsql’s pooling implementation is very optimized, so that’s unlikely to be significant for most applications.
@BillJones50 your questions have nothing to do with this issue - please avoid thread hijacking.
From our docs on batching:
So just add parameters to the NpgsqlBatchCommands without the batch, just like you’d do with an NpgsqlCommand.
Since UPDATE doesn’t return a resultset (unless you use the RETURNING clause), no - you don’t call NextResultAsync().
@john2014 your code looks OK. In most applications it makes sense to just turn on auto-prepare rather than using explicit preparation (with PrepareAsync), but other than nothing else to say.
We may not have a single snippet containing that, but our getting started page does seem to cover all of this. Note that there’s nothing paricularly high-performance here - it’s just regular Npgsql code; the partial exception to this is PrepareAsync, which once again I wouldn’t necessarily recommend for the general case (as opposed to auto-preparation).
There’s nothing inherently wrong with sync - it’s a fully supported option. But you’re right that this point our samples should generally show only async - I’m modifying those code samples in #311.
@john2014 you probably want to give this doc page a good read.
It’s the same for all practical purposes.
(For completeness, Npgsql has an experimental mode called “multiplexing” which will - in the future - only work with dataSource.CreateCommand, but that’s not really relevant for the normal case.)
No; as explained in the docs, auto preparation and explicit preparation are two different techniques. You can still combine them (i.e. if you want to make sure some SQL is always prepared regardless of how often it’s used), but that’s a niche scenario. Read the docs to understand more about this.
Yes, prepared statements cost e.g. memory resources in PostgreSQL - it’s up to you to benchmark and determine the right number. Also, it’s a bit extreme to have 500 different SQLs in your application - this may be a sign that you’re doing dynamic SQL generation, which is bad for performance.
That’s a PostgreSQL question that’s unrelated to Npgsql - I suggest researching that in PostgreSQL resources. But overall they are stored in-memory, and must be re-prepared after server restart.
Yes, this is pretty clearly spelled out in the docs.
It will, that’s called explicit preparation.
There’s no difference in the common case; NpgsqlDbType is usually useful in cases where the CLR type doesn’t get you the PostgreSQL type you want; for example, a string parameter gets send as PostgreSQL
textby default, but you can send it asjsonbby specifying NpgsqlDbType.Technically there may be a very small performance benefit in specifying NpgsqlDbType, but that’s likely to be negligible for almost all regular scenarios. Benchmark to see the difference rather than just using NpgsqlDbType everywhere.
I’m going to go ahead and close this as the behavior is by-design and all questions have been answered. I suggest reading both Npgsql and PostgreSQL docs to better understand prepared statements (the information is out there). If you still have outstanding questions after that, feel free to post here again and I’ll do my best to help.