I tried to call my user defined function in pgresql from C# code,
my function creation script is as follows,
CREATE OR REPLACE FUNCTION public."GetUserDailyData"(
cid integer,
hid integer,
currday integer)
RETURNS character varying AS
$BODY$
DECLARE
returndata varchar = '';
BEGIN
SELECT data->20+currday into returndata FROM pops
WHERE hybid = hid and cropid = cid;
return returndata;
END
$BODY$
LANGUAGE plpgsql
COST 100;
My method to call this function is as follows,
public static object ExecuteScalar(string conString, string spName, NpgsqlParameter[] param)
{
using (var conn = new NpgsqlConnection(conString))
{
conn.Open();
using (var tran = conn.BeginTransaction())
using (var command = conn.CreateCommand())
{
command.CommandText = spName;
command.CommandType = CommandType.StoredProcedure;
for (var i = 0; i < param.Length; i++)
{
command.Parameters.Add(new NpgsqlParameter());
command.Parameters[i] = param[i];
}
var result = command.ExecuteScalar();
return result;
}
}
}
I tried everything even checked the existence of this function in pg_proc using
select * from pg_proc where proname = 'GetUserDailyData'
and it reflected the function details row.
But every time it is giving the same error.
Any kind of suggestion would be highly appreciated. Thanks.
Adding objects with case sensitive names in PostgreSQL can lead to these complications; in this case you need to specify the name of the stored procedure between quotes, however it would be advisable to simply not create any objects that rely on case sensitivity, use underscores instead, or when create/refer to objects using CamelCase without the quotes (which creates/refers to the objects in low-caps). In any case, you may also need to specify the whole interface (not just the name) as the CommandText, and specify the data types of the parameters (see this).
...
command.CommandText = "\"" + spName + "\"";
...
Related
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 have the following table:
And I have the following trigger:
CREATE TRIGGER check_insertion_to_pushes_table
BEFORE INSERT
ON "Pushes"
FOR EACH ROW
EXECUTE PROCEDURE trg_insert_failed_push();
CREATE or replace FUNCTION trg_insert_failed_push()
RETURNS trigger AS
$func$
BEGIN
IF (NEW."Sent" = false) THEN
IF EXISTS(
SELECT *
FROM "Pushes"
where "Sent" = false
and "CustomerId" = NEW."CustomerId"
and "PushTemplateId" = NEW."PushTemplateId"
)
THEN
RETURN NULL;
END IF;
RETURN NEW;
ELSE
RETURN NEW;
end if;
END
$func$ LANGUAGE plpgsql;
If there is row in the DB where CustomerId and PushTemplateId and Sent are equal to new row and Sent is false I would like to pass insertion.
And I have the following test to check how it works:
public class Tests
{
private IPushRepository _pushRepository;
[NUnit.Framework.SetUp]
public void Setup()
{
var confBuilder = new ConfigurationBuilder();
var configuration = confBuilder.AddJsonFile("/home/aleksej/projects/makeapppushernet/TestProject/appsettings.LocalToProdDb.json").Build();
_pushRepository = new PushRepository(new ApplicationDbContext(configuration));
}
[Test]
public async Task Test1()
{
var push = new Push
{
CustomerId = 69164,
Sent = false,
PackageId = "com.kek.lol",
Category = "betting",
Advertiser = "Advertiser",
TemplateType = "opened_and_not_registration",
IntervalType = "minutes",
BottomDateTimeBorder = 90,
TopDateTimeBorder = 60,
ClientStartDateTime = DateTime.Now,
FCMResponse = "hello",
CreatedAt = DateTime.Now,
LangCode = "En",
PushBody = "Hello",
PushTitle = "Hello",
PushTemplateId = 15
};
var pushesList = new List<Push>
{
push
};
await _pushRepository.SaveAsync(pushesList);
Assert.Pass();
}
}
If I set false for Sent in the test I have the following exception:
Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
If I set true I have nothing. It just passes insertion.
Update
Ok, with the help of Shay Rojansky's answer I have the following trigger code:
CREATE TRIGGER check_insertion_to_failed_pushes_table
BEFORE INSERT
ON "FailedPushes"
FOR EACH ROW
EXECUTE PROCEDURE trg_insert_failed_push();
CREATE or replace FUNCTION trg_insert_failed_push()
RETURNS trigger AS
$func$
DECLARE
push "FailedPushes"%ROWTYPE;
old_push_id numeric;
BEGIN
old_push_id = (SELECT "FailedPushId"
FROM "FailedPushes"
where "CustomerId" = NEW."CustomerId"
and "PushTemplateId" = NEW."PushTemplateId");
push := new;
IF (old_push_id != 0)
THEN
push."FailedPushId" = old_push_id;
DELETE
FROM "FailedPushes"
where "CustomerId" = NEW."CustomerId"
and "PushTemplateId" = NEW."PushTemplateId";
return push;
END IF;
push."FailedPushId" = (SELECT count(*) FROM "FailedPushes")::numeric + 1;
return push;
END
$func$ LANGUAGE plpgsql;
Maybe not very elegant but it works.
You are in effect configuring PostgreSQL to ignore the INSERT under certain conditions, but EF Core isn't aware of this in any way. When you tell EF Core to add a new row, it expects for that to actually happen in the database. If the entity has any database-generated columns (identity, serial), EF Core also expects to receive the their values for the newly-inserted row (and will populate them back into the entity's CLR instance).
So AFAIK you can't just tell the database to ignore the INSERT and expect everything to work...
See this issue on EF Core upsert support which is somewhat related.
Maybe your model parsing to Action in the controller might not have accurate values.
I have a insert sp
create procedure sp_insert_services
#Service_Type nvarchar(50),
#Frequency nvarchar(50),
as
insert INTO tbl_fl_Master_List
(Service_Type,Frequency)
values
(#Service_Type,
#Frequency)
now how i write this through lambda expression,linq to sql and EF?
i tried this but this show error
var insert = insertt.tbl_fl_Master_List();
if(insertt!=null)
{
insertt.tbl_fl_Master_List.Add();
}
insertt.SaveChanges();
Error 8 Non-invocable member 'chart_project.Track_Data.tbl_fl_Master_List' cannot be used like a method.
Error 9 No overload for method 'Add' takes 0 arguments
and also i try this
public static string Insert_master_services(string Service_Type,string Frequency)
{
Data insertt=new Data();
tbl_fl_Master_List t = new tbl_fl_Master_List();
t.Service_Type = Service_Type;
t.Frequency = Frequency;
insertt.SaveChanges();
}
I have a simple function that just inserts the parameter values provided to it into columns in a table.
When I run the function via the ExecuteNonQuery() method on the command object I always get -1, even if the insert took place.
If i run the same query as a Text command it gives be the correct result of 1.
I'm new to postgresql/npgsql. Is there trick to making the function feed back the number of rows affected? Something like "set nocount off" in SQL Server?
EDIT:
The code I am using: (with npgsql 2.0.11)
var connStr = #"Server=127.0.0.1;Port=5432;User Id=postgres;Password=***;Database=Test;";
using (var conn = new NpgsqlConnection(connStr)) {
conn.Open();
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "insert_something";
cmd.CommandType = CommandType.StoredProcedure;
NpgsqlCommandBuilder.DeriveParameters(cmd);
cmd.Parameters["_id"].Value = 1;
cmd.Parameters["_val"].Value = 2;
var rowsAffected = cmd.ExecuteNonQuery();
Console.WriteLine(rowsAffected);
}
}
I haven't used Npgsql, but the documentation has an example with
the following code:
NpgsqlCommand command = new NpgsqlCommand("insert into table1 values(1, 1)", conn);
Int32 rowsaffected;
try
{
rowsaffected = command.ExecuteNonQuery();
}
If you are talking about some PostgreSQL function like this:
CREATE FUNCTION myinsert(integer) RETURNS void
LANGUAGE 'sql' AS 'INSERT INTO mytable VALUES ($1)';
and you are doing something like SELECT myinsert(1);, you can't get the number of affected rows, because you are running a SELECT and what the function does internally is opaque to you.
For ExecuteNonQuery() to get the number of rows affected, your postgresql function should return just that. An example would be:
CREATE OR REPLACE FUNCTION insert_something(_id int, _val int)
RETURNS int as $$
DECLARE count int;
BEGIN
INSERT INTO some_table (id, value) VALUES(_id, _val);
GET DIAGNOSTICS count = ROW_COUNT;
RETURN count;
END;
$$ language plpgsql;
Now if you call ExecuteNonQuery() from your code, you should get the inserted rows count.
Here's how I have implemented and its working for me without any issues:
Add an OUT Parameter to your function.
CREATE OR REPLACE FUNCTION schema.UpdateSomeValue(
IN par_updateId text,
OUT par_returnvalue INT4)
BEGIN
UPDATE schema.TableToUpdate
SET status = 1
WHERE ID = par_updateId;
GET DIAGNOSTICS par_returnvalue = ROW_COUNT;
END;
Now on the C# side
private int UpdateSomeValue()
{
var connStr = #"Server=127.0.0.1;Port=5432;UserId=postgres;Password=***;Database=Test;";
int result = -1;
using (var conn = new NpgsqlConnection(connStr)) {
conn.Open();
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "shema.UpdateSomeValue";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("par_updateId",NpgsqlTypes.NpgsqlDbType.INT, 1);
NpgsqlParameter outParm = new NpgsqlParameter("par_returnvalue",
NpgsqlTypes.NpgsqlDbType.Integer)
{
Direction = ParameterDirection.Output
};
cmd.ExecuteNonQuery();
result = Convert.ToInt32(outParm.Value);
}
}
return result;
}
PS : I tried the just ExecuteNonQuery() approach but it was always returning -1 for me (as if no rows has been affected NpgsqlDocumentation) Using above way I was able to capture the Number of rows affected from the PostgreSQL function and with OUT parameter it was easy to catch the affected rows on the client side.
Is it possible to implement batching of multiple stored procedure calls (doing updates/deletes) in ADO.NET without resorting to DataAdapters?
You could try using System.Data.SqlClient.SqlCommandSet. It's actually internal, but Ayende made a wrapper to make it public.
Code is currently hosted in sourceforge.
You're SQL text can contain multiple commands. If you return multiple result sets, then you can use a DataReader and use the NextResult function. What I often do is store the SQL to execute as an Embedded Resource, then load that text. If it contains parameters, then set the parameters just like you would normally.
For example, I have a file:
UPDATE dbo.QuotePricedLineItem
SET fkQuoteVendorLineSet = NULL
FROM dbo.QuotePricedLineItem qpli
INNER JOIN dbo.QuoteLineItem qli ON qpli.Id = qli.Id
WHERE qli.fkQuote = #quoteId AND qpli.fkQuoteVendorLineSet = #ciscoConfigId
DELETE CiscoQuoteLineItem
FROM CiscoQuoteLineItem cqli
INNER JOIN QuoteLineItem qli ON cqli.Id = qli.Id
WHERE qli.fkQuote = #quoteId AND cqli.fkCiscoQuoteVendorLineSet = #ciscoConfigId
that I execute as such:
using (SqlConnection conn = DbUtils.CreateConnection() as SqlConnection)
{
conn.Open();
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = MvcApplication.GetResource("SQL.DemoteCiscoQuoteLineItems.sql");
cmd.Parameters.AddWithValue("#quoteId", q.Id);
cmd.Parameters.AddWithValue("#ciscoConfigId", configSetId);
cmd.ExecuteNonQuery();
}
Note that MvcApplication.GetResource is not a built in function - it's one you have to write... here's mine:
public static string GetResource(string p)
{
Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream("CortexQuoting.Res." + p);
if (s == null) return null;
StreamReader sw = new StreamReader(s);
string ss = sw.ReadToEnd();
sw.Close();
return ss;
}