I am writing an app using .net Web Api, Entity Framework and PostgreSql and I am having trouble calling a stored procedure. I've tried to go off of this question: using stored procedure in entity framework but it doesn't seem to help so I'll ask for my specific case and hope I can get help.
I have written a stored procedure in postgres and all I want to do is call it from my repository. What is the best way to go about doing this? If I want to call it though a DbContext object do I need to tell the DbContext that the stored procedure exists before I call it? Would it just be better to use ado.Net to use sql directly? This is what I tried and it gives me a syntax error:
var serverName = new PgSqlParameter("server_name", server);
var dt = new PgSqlParameter("data_tag", dataTag);
var timestamp = new PgSqlParameter("ts", DateTime.Now);
var val = new PgSqlParameter("val", value);
if (context.Database.Connection.State == ConnectionState.Closed)
context.Database.Connection.Open();
try
{
context.Database.ExecuteSqlCommand(#"EXEC [dbo].[historical_tag_values_insert]", serverName, dt, timestamp, val);
}
finally
{
if (context.Database.Connection.State == ConnectionState.Open)
context.Database.Connection.Close();
}
Related
I have an Oracle stored procedure (in a package) with the following signature:
procedure My_StoredProcedure
(p_start_date IN DATE,
p_end_date IN DATE,
p_result_cursor OUT out_cursor,
p_message OUT VARCHAR2
);
Where out_cursor is defined in the package as type out_cursor is ref cursor;
As this is a stored procedure that aggregates 3 different tables I did not have a single entity generated (as database first) for the stored procedure results, after scaffolding using Entity Framework Core for Oracle.
The C# code for the method, which calls this stored procedure is:
var parameters = new[] {
new OracleParameter("p_start_date", OracleDbType.Date, startDate, ParameterDirection.Input),
new OracleParameter("p_end_date", OracleDbType.Date, endDate, ParameterDirection.Input),
new OracleParameter("p_result_cursor", OracleDbType.RefCursor, ParameterDirection.Output),
new OracleParameter("p_message", OracleDbType.Varchar2, ParameterDirection.Output)
};
var sql = "BEGIN MY_PKG.My_StoredProcedure(:p_start_date, :p_end_date, :p_result_cursor, :p_message); END;";
int rowsAffected = _dbContext.Database.ExecuteSqlRaw(storedProc, parameters);
This is not working as I would expect, although the rowsAffected variable contains -1, and there is no error being returned, I see nothing being returned in the parameters[2] or parameters[3] of the parameters variable.
I suspect that the out_cursor definition may be tripping me up. I do not have control over the database or the creation of the stored procedure.
I am also open to a better way of doing this but any help would be gratefully received.
I have managed to resolve this issue myself by bringing together a few different strands to provide a complete solution.
Firstly, the use of the EF Core database first scaffolding to create the DbContext file & associated entities will NOT create the stored procedure as would be the case when using the EF wizard in the .Net Framework.
Therefore, once the scaffolding process is complete the entity model class, which will be used to stored the result from the store procedure needs to be created by hand and added to the DbContext generated file.
...
public virtual DbSet<SpResult> SpResults { get; set; } = null!;
Additionally, since the results will not have a primary key this also needs to be specified in the generated DbContext file, along with any handling for mapping column names to entity model properties or value conversions:
modelBuilder.Entity<TicketSale>(entity =>
{
entity.HasNoKey();
entity.Property(e => e.Price)
.HasColumnName("PRIICE");
entity.Property(e => e.Date)
.HasConversion(val => val.ToShortDateString(), val => DateTime.Parse(val));
For the type out_cursor defined in the package, it is necessary to described this as a user defined type in the Oracle parameter property UdtTypeName, which is used by the stored procedure:
var parameters = new[] {
...,
new OracleParameter()
{
ParameterName = "p_result_cursor",
OracleDbType = OracleDbType.RefCursor,
Direction = ParameterDirection.Output,
UdtTypeName = "MY_PKG.out_cursor"
},
...
};
Finally, the stored procedure can now be called as follows:
var storedProc = "BEGIN MY_PKG.My_StoredProcedure(:p_start_date, :p_end_date, :p_result_cursor, :p_message); END;";
return _dbContext.SpResults.FromSqlRaw(storedProc, parameters).ToList();
Additonally, it is possible to do this using the ExecuteSqlRaw via a OracleCommand, OracleDataAdapter, DataSet and selecting the contents of the DataTable with a LINQ expression and creating a list of the stored procedure result class, however when using EF Core I do think this is the tidier solution.
I'm posting this for two reasons
I'm new to PostgreSQL and it took a while to piece this information together, so I thought someone else would find this helpful and
to ask if there is another way to call a PostgreSQL stored procedure that doesn't require all of the parameters to be included in the stored procedure name/signature.
The following uses Dapper and Npgsql to make a call to a PostgreSQL stored procedure that inserts (null id_inout passed in) or updates a record (id_inout has a value).
I'd like to understand why PostgreSQL requires the entire stored procedure signature when making the call.
public static int? PO_Save(PurchaseOrder po)
{
int? recordId = null;
using (var cn = new NpgsqlConnection(AppSettings.ConnectionString))
{
if (cn.State != ConnectionState.Open)
cn.Open();
var procName = "CALL po_save(#in_ponumber,#in_deliverydate,#in_bldnum," +
"#in_facname,#in_facnumber,#in_facaddress1,#in_facaddress2,#in_city," +
"#in_state,#in_zip,#in_theme,#id_inout)";
var p = new Dapper.DynamicParameters();
p.Add("#in_ponumber", po.PONumber);
p.Add("#in_deliverydate", po.DeliveryDate);
p.Add("#in_bldnum", po.BldNum);
p.Add("#in_facname", po.FacName);
p.Add("#in_facnumber", po.FacNumber);
p.Add("#in_facaddress1", po.FacAddress1);
p.Add("#in_facaddress2", po.FacAddress2);
p.Add("#in_city", po.City);
p.Add("#in_state", po.State);
p.Add("#in_zip", po.Zip);
p.Add("#in_theme", po.Theme);
p.Add("#id_out", po.POID, null, ParameterDirection.InputOutput);
var res = cn.Execute(procName, p);
recordId = p.Get<int>("#id_inout");
}
return recordId;
}
You should be able to pass commandType: CommandType.StoredProcedure to Execute, e.g:
var res = cn.Execute(
"po_save",
new {
in_ponumber = po.PONumber,
in_deliverydate = po.DeliveryDate,
// etc...
},
commandType: CommandType.StoredProcedure,
);
Here's the docs with such an example: https://github.com/StackExchange/Dapper/blob/main/Readme.md#stored-procedures
I wanted to find an answer to this myself, and beginning with Npgsql 7.0, CommandType.StoredProcedure will now invoke stored procedures and not functions as before.
I have an entity, lets just call it "Entity", that I want to delete with a stored procedure. The "Entity" entity is relatively complex with a lot of related entities - Hence why, I want to use a stored procedure to delete the Entity.
CREATE PROCEDURE dbo.spDeleteEntity
#EntityId int,
#ServiceResult bit output
AS
BEGIN
.... Delete logic here ...
IF ##ERROR = 0
SET #ServiceResult = 1
ELSE SET #ServiceResult = 0
END
As you can see, the stored procedure takes in an EntityId for the entity, performs my delete logic, and returns a bit - Which in this case is my ServiceResult. Here the ServiceResult is "True"/1 if no errors occur while executing the query, and "False"/0 if errors occur. The problem is now, that I want to be able to execute this stored procedure from .NET Core. My Initial idea was to do something like this
public bool DeleteEntity(Entity Entity)
{
return _context.Entity.FromSqlRaw<bool>("spDeleteEntity {0}", Entity.Id);
}
I believe this doesn't work, because Entity Framework Core does not know what datatype it should expect. From what I can read, Entity Framework Core only accepts types of TEntity. So my question really is, how do I call a stored procedure with Entity Framework Core, so that I can pass an Id and get a bool value returned.
While in your case, you could simply RAISERROR in your procedure to indicate failure.;
try{
_context.Database.ExecuteSqlInterpolatedAsync($"spDeleteEntity {Entity.Id}");
return true;
}catch(...){
return false;
}
There is a way to pass sql parameters in / out of raw sql commands using EF Core with something like;
var entityId = new SqlParameter("#entityId", Entity.Id);
var result = new SqlParameter("#result", SqlDbType.Bit)
{ Direction = ParameterDirection.Output };
_context.Database.ExecuteSqlRaw("EXEC #result = spDeleteEntity #entityId", entityId, result);
return (bool)result.Value;
Call your stored procedure in the try catch and add your SqlParameter that you want to pass to sp like this :
try
{
using(var context = new SampleContext())
{
//Declare storedprocedure parameter
var Idp = new SqlParameter("#IdParam", "Idp");
//Call stored procedure(For async call use ExecuteSqlCommandAsync method)
context.Database.ExecuteSqlCommand("EXEC spName #IdParam", Idp);
}
Return true:
}
catch
{
Return false;
}
In this way if execution of sp occurred with error, it goes to catch and return false and if not it's return true.
Note: if you declare raiseerror in your storedprocedure, you can generates an error message and send it to your application try catch.
More details about raiseerror :
https://learn.microsoft.com/en-us/sql/t-sql/language-elements/raiserror-transact-sql?view=sql-server-ver15
I've recently started working with EF6 So my knowledge on it isn't very great. I am currently working on test web project which I have created using VS Express 2013. EF has created the required tables. Now I've added my own table which has basic information such as:
FirstName
Surname
DOB
What I'm trying to gather is would I write a SQL query in my function to get the data from the database so something like
using (SqlConnection connection = new SqlConnection(myConnectionString))
{
using (SqlCommand cmd = new SqlCommand("select * from my table", connection))
{
connection.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
//Read Data
}
}
}
Or is there a separate way to write it in EF? Because I couldn't find how and where the SQL syntax queries are used in EF or maybe I'm just missing something? Thanks in advance for your help & support
Entity Framework (EF) is an object-relational mapper that enables .NET
developers to work with relational data using domain-specific objects.
It eliminates the need for most of the data-access code that
developers usually need to write.
You need to review more there http://msdn.microsoft.com/en-gb/data/ef.aspx and at least read some intros to EF or some video tutorials.
var context = new EntitiesContext();//DbContext Object
var list = ent.Customers; // will return all customers.
The code you have posted is using ADO.Net. You can perform raw sql in entity framework like this
Entities ent = new Entities();//DbContext Object
var list = ent.tablename.SqlQuery("select * from my table");
And using Entity framework to get data from db
Entities ent = new Entities();//DbContext Object
var data = ent.tableName;
The point of EF is that you don't have to write SQL queries. You have an object model you use to access the database:
using (var context = new YourEntities())
{
var records = context.Table; // .Where(t => t.Name == "foo")
foreach (var record in records)
{
// ...
}
}
Please check out the Quickstart example from MSDN
http://msdn.microsoft.com/en-us/library/vstudio/bb399182%28v=vs.100%29.aspx
I am using the following code to run a stored procedure:
MyContext db = new MyContext();
var a = db.Database.SqlQuery<string>("dbo.getQuestionUIdsByTopicId #TopicId",
new SqlParameter { ParameterName = "TopicId", Value = testTopicId });
This works good but now I will have a stored procedure that does not return any data other than a return code.
How can I execute a stored procedure with a parameter using my context db.Database and have the stored procedure return only a return code? If someone could give an example of a 3-4 line SP and how it returns a return code that would also be a great help.
You can use ExecuteSqlCommand to send non-query commands to the database.
int result = db.Database.ExecuteSqlCommand("exec sproc...");
See this link for more info.