Postgres function not working while we upgraded our .net core and other packages to latest version. Earlier, the same code was working perfectly fine.
We are using function instead of stored procedure.
DbTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
try
{
using (DbCommand command = connection.CreateCommand())
{
command.Transaction = transaction;
command.CommandType = CommandType.StoredProcedure;
command.CommandText = Constants.StoredProcedure.SPNAME;
command.Parameters.Add(new NpgsqlParameter("Param1", NpgsqlTypes.NpgsqlDbType.Integer)
{ Value = val1 });
command.Parameters.Add(new NpgsqlParameter("Param2", NpgsqlTypes.NpgsqlDbType.Varchar)
{ Value = val2 });
command.Parameters.Add(new NpgsqlParameter("Param3", NpgsqlTypes.NpgsqlDbType.Varchar)
{ Value = val3 });
var res = command.ExecuteScalar();
transaction.Commit();
}}
Error - Npgsql.PostgresException: '42809: public.NotableEventUserModeratorJoinOrder(Param1 => integer, Param2 => character varying, Param3 => character varying) is not a procedure
We have commented line command.CommandType = CommandType.StoredProcedure; and then receving error - PostgreSQL, Npgsql returning 42601: syntax error at
It is a documented breaking change in npgsql 7.
The doc on the stored procedures / functions also says:
Warning
Starting with Npgsql 7.0, CommandType.StoredProcedure now invokes
stored procedures, and not function as before. See the release notes
for more information and how to opt out of this change.
And the two options are
disable the new feature
When NpgsqlCommand.CommandType is set to CommandType.StoredProcedure,
Npgsql now generates SQL for invoking a PostgreSQL stored procedure,
and not a function, as before. To opt out of this breaking change and
continue to invoke functions as before, enable the
Npgsql.EnableStoredProcedureCompatMode AppContext switch as follows:
AppContext.SetSwitch("Npgsql.EnableStoredProcedureCompatMode", true);
or
call your function with a regular select
using var cmd = new NpgsqlCommand("SELECT my_func(1, 2)", conn);
Related
I am replacing a very long sql statement in C# with a stored procedure. The sql was called by the FromSqlRaw function. What piece of EF functionality can be implemented to achieve the same result. The following returns an exception error near b6f42 or similar each time I run the program. The procedure results called from SSMS are satisfactory.
var wrk = DbCtx.WRK.FromSqlRaw($"Execute GetCustomersAsync
{Id} {LastName}").ToObservableCollection();
You could use SqlQuery for that:
var param1 = new SqlParameter()
{
ParameterName = "#Date",
SqlDbType = SqlDbType.DateTime,
Value = validityEnd
};
var parameters = new SqlParameter[] { param1 };
return context
.Set<TEntity>()
.SqlQuery<ReturnType>(
"[schema].[spName] #Date",
parameters);
Requirement:
How to get back the multiple refcursor data from a postgresql 11 procedure (not a function) without using the fetch statement using npgsql 4.0 from ado.net.
Here is the sample which i have tried:
Postgresql Procedure:
CREATE OR REPLACE PROCEDURE public.GetMultipleResultSets(
INOUT ref1 refcursor,
INOUT ref2 refcursor)
LANGUAGE 'plpgsql'
AS $BODY$
begin
open ref1 for
select * from public."tblTestTable1";
open ref2 for
select * from public."tblTestTable2";
end;
$BODY$;
C# Code using Npgsql 4.0:
public DataSet ReturnAsDataSet(string procedureName)
{
this.dataSet = new DataSet();
OpenConnection();
NpgsqlTransaction objTransaction = this.Connection.BeginTransaction();
NpgsqlDataAdapter adapter = new NpgsqlDataAdapter();
NpgsqlCommand command = this.Connection.CreateCommand();
try
{
NpgsqlParameter refCursorParam1 = new NpgsqlParameter("#ref1", NpgsqlTypes.NpgsqlDbType.Refcursor);
refCursorParam1.Direction = ParameterDirection.InputOutput;
refCursorParam1.Value = "ref1";
command.Parameters.Add(refCursorParam1);
refCursorParam2 = new NpgsqlParameter("#ref2", NpgsqlTypes.NpgsqlDbType.Refcursor);
refCursorParam2.Direction = ParameterDirection.InputOutput;
refCursorParam2.Value = "ref2";
command.Parameters.Add(refCursorParam2);
command.CommandText = "call " + procedureName + "(#ref1, #ref2)";
command.Transaction = objTransaction;
adapter.SelectCommand = command;
adapter.Fill(dataSet);
objTransaction.Commit();
}
catch (NpgsqlException ex)
{
if (objTransaction != null)
objTransaction.Rollback();
throw new Exception(ex.Message);
}
finally
{
CloseConnection();
command.Dispose();
objTransaction.Dispose();
}
return this.dataSet;
}
This code will return a table having the "ref1", "ref2" as the columns and "ref1" and "ref2" as the values inside it as follows:
enter image description here
But I need the actual result sets returned from the procedure.
How can I achieve it without manually fetching those refcursor data.
I mean without using "fetch all ref" statement how can we retrieve the data by executing either ExecuteReader() or adapter.Fill() methods as above.
Is there any automatic cursor dereferencing available in npgsql?
Please provide the answer if anyone knows.
Thanks for your help in advance.
This is currently not done for you by Npgsql, this issue tracks it. You can see this long discussions on the pros and cons of this. At the moment you'll have to call FETCH on the cursors yourself.
I am using PostgreSQL pgadmin4 (4.16v) with ASP.NET application. I have created a procedure as defined below:
CREATE OR REPLACE PROCEDURE public.usp_bind(
)
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
select district_id,district_name from district_master order by district_name;
END;
$BODY$;
From asp.net application I have called above procedure, code as below:
NpgsqlConnection conn = new NpgsqlConnection();
NpgsqlDataAdapter da = new NpgsqlDataAdapter();
NpgsqlCommand cmd = new NpgsqlCommand();
DataSet ds = new DataSet();
public string dbconstr = dbConnectstr.Connectionstring();
public DataSet getDatafunction(string procedure_, [Optional] string flag_)
{
using (conn = new NpgsqlConnection(dbconstr))
{
//conn.Open();
using (da = new NpgsqlDataAdapter())
{
da.SelectCommand.CommandType = CommandType.StoredProcedure;
da.SelectCommand.CommandText = "CALL usp_bind";
da.SelectCommand.Connection = conn;
using (ds = new DataSet())
{
da.Fill(ds);
}
}
//conn.Close();
}
return ds;
}
It's giving me an error as - 42809: 'usp_bind' is a procedure.
I would have called it using a CALL method too but did't worked. What is the exact way to call a procedure from ASP.NET application?
Don't set CommandType.StoredProcedure on your command.
Unfortunately, stored procedures are new, and CommandType.StoredProcedure was already used to invoke functions, and changing that would be a major breaking change at this point.
I am using NpgSQL with PostgreSQL and ADO.NET. Forgive the simplicity of the question as I just started using PostgreSQL and NpgSQL this week.
Something like this works fine:
[Test]
public void InsertNoParameters()
{
NpgsqlConnection conn = new NpgsqlConnection("Host=localhost; Database=postgres; User ID=postgres; Password=password");
conn.Open();
IDbCommand command = conn.CreateCommand();
string sql = "INSERT INTO Customers (FirstName,LastName) VALUES ('Test','Tube')";
command.CommandText = sql;
command.ExecuteNonQuery();
conn.Close();
}
When I put in parameters I get the error message:
Npgsql.NpgsqlException : ERROR: 42703: column "_firstname" does not exist
[Test]
public void InsertWithParameters()
{
NpgsqlConnection conn = new NpgsqlConnection("Host=localhost; Database=postgres; User ID=postgres; Password=password");
conn.Open();
IDbCommand command = conn.CreateCommand();
string sql = "INSERT INTO Customers (FirstName,LastName) VALUES (_FirstName,_LastName)";
command.CommandText = sql;
var parameter = command.CreateParameter();
parameter.ParameterName = "_FirstName";
parameter.Value = "Test";
command.Parameters.Add(parameter);
parameter = command.CreateParameter();
parameter.ParameterName = "_LastName";
parameter.Value = "Tube";
command.Parameters.Add(parameter);
command.ExecuteNonQuery();
conn.Close();
}
The responses in the comments are correct:
Npgsql doesn't support _ as a parameter placeholder notation. You should be using # or : (so #FirstName or :FirstName, not _FirstName).
PostgreSQL will automatically lower-case your table and column names unless they are double-quoted. Either use lower-case names for everything (simpler) or quote identifiers in your SQL queries.
So your code should look more or less like this:
IDbCommand command = conn.CreateCommand();
string sql = "INSERT INTO Customers (first_name, last_name) VALUES (#FirstName,#LastName)";
command.CommandText = sql;
var parameter = command.CreateParameter();
parameter.ParameterName = "FirstName";
parameter.Value = "Test";
command.Parameters.Add(parameter);
So here's the deal. In our database, we wrap most of our reads (i.e. select statements) in table valued functions for purposes of security and modularity. So I've got a TVF which defines one or more optional parameters.
I believe having a TVF with defaulted parameters mandates the use of the keyword default when calling the TVF like so:
select * from fn_SampleTVF(123, DEFAULT, DEFAULT)
That's fine, everything works in the query analyzer, but when it comes time to actually make this request from ADO.NET, I'm not sure how to create a sql parameter that actually puts the word default into the rendered sql.
I have something roughly like this now:
String qry = "select * from fn_SampleTVF(#requiredParam, #optionalParam)";
DbCommand command = this.CreateStoreCommand(qry, CommandType.Text);
SqlParameter someRequiredParam = new SqlParameter("#requiredParam", SqlDbType.Int);
someRequiredParam.Value = 123;
command.Parameters.Add(someRequiredParam);
SqlParameter optionalParam = new SqlParameter("#optionalParam", SqlDbType.Int);
optionalParam.Value = >>>> WTF? <<<<
command.Parameters.Add(optionalParam);
So, anybody got any ideas how to pass default to the TVF?
SqlParameter optionalParam = new SqlParameter("#optionalParam", SqlDbType.Int);
optionalParam.Value = >>>> WTF? <<<<
command.Parameters.Add(optionalParam);
You don't have to add above code (The optional parameter) for default. SQL Server will use the default as defined in your UDF. However if you would like to pass different value then you can pass:
SqlParameter optionalParam = new SqlParameter("#optionalParam", SqlDbType.Int);
optionalParam.Value = newValue;
command.Parameters.Add(optionalParam);
I would have done so:
public void YourMethod(int rparam, int? oparam = null)
{
String qry = string.Format("select * from fn_SampleTVF(#requiredParam, {0})"
, !oparam.HasValue ? "default" : "#optionalParam");
SqlParameter someRequiredParam = new SqlParameter("#requiredParam", SqlDbType.Int);
someRequiredParam.Value = rparam;
command.Parameters.Add(someRequiredParam);
if (oparam.HasValue)
{
SqlParameter optionalParam = new SqlParameter("#optionalParam", SqlDbType.Int);
optionalParam.Value = oparam.Value;
command.Parameters.Add(optionalParam);
}
}
You can pass Null as the parameter value.
This article shows examples: http://support.microsoft.com/kb/321902