Getting stored procedure results in net core - c#

This is my stored procedure:
ALTER PROCEDURE [dbo].[GetAllOrdersHistoryForNofshonit]
#MemberId VARCHAR(50)
AS
BEGIN
SET NOCOUNT ON;
SELECT *
INTO #Temptable
FROM AttractionsOrdersHistory
UNION ALL
SELECT *
FROM MoviesOrdersHistory
UNION ALL
SELECT *
FROM SpaOrdersHistory
UNION ALL
SELECT *
FROM TzimersOrdersHistory
WHERE MemberId = #MemberId
END
I have this procedure that returns results from 4 views into a temp table.
How can I see the result from temp table?
How can I get the results into my existing code?
public async Task<bool> MemberOrderDateEXE(string memberId)
{
if (memberId != null)
{
using (var context = ContextManager.ClubContext())
{
var connectionString = ContextManager.ClubContext()._connectionString;
var connection = new SqlConnection(connectionString);
var command = new SqlCommand($"GetAllOrdersHistoryForNofshonit '{memberId}'", connection);
connection.Open();
var reader = command.ExecuteReader();
reader.Read();
var retValue = reader.GetValue(0);
if(retValue != null)
....
}
}

Related

Read select from stored procedures using LINQ

In this way, I retrieve data using the stored procedure:
var returnParameter = new SqlParameter("#RV", SqlDbType.Int) { Direction = ParameterDirection.Output };
var xmlParameter = new SqlParameter("#XML", XML) { DbType = DbType.Xml };
var familySentParameter = new SqlParameter("#FamilySent", SqlDbType.Int) { Direction = ParameterDirection.Output };
//var resultParameter = new SqlParameter("Result", SqlDbType.Text) { Direction = ParameterDirection.ReturnValue };
var sql = "exec #RV = uspConsumeSomething #XML, #FamilySent OUT";
var result = myContext.Database.ExecuteSqlCommand(sql, returnParameter, xmlParameter, familySentParameter);
RV = (int)returnParameter.Value;
Below the fragment of the stored procedure (termination of the procedure):
BEGIN CATCH
SELECT ERROR_MESSAGE();
IF ##TRANCOUNT > 0 ROLLBACK TRAN ENROLL;
SELECT * FROM #tblResult;
RETURN -400
END CATCH
SELECT Result FROM #tblResult;
RETURN 0 --ALL OK
END
How to obtain in C# data obtained from:
SELECT * FROM #tblResult;
SELECT Result FROM #tblResult;
They are not needed for the operation of the procedure but contain information about the type of error that arises in the stored procedure.
The use of Return in a stored procedure is supported by #RV
EDIT:
I'm trying a different approach but I still do not know how to get the values from SELECT here
DbConnection connection = myContext.Database.GetDbConnection();
using (DbCommand cmd = connection.CreateCommand())
{
cmd.CommandText = sql;
cmd.Parameters.AddRange(new[] { xmlParameter, returnParameter, familySentParameter });
if (connection.State.Equals(ConnectionState.Closed)) connection.Open();
using (var reader = cmd.ExecuteReader())
{
var entities = new List<string>();
while (reader.Read())
{
Debug.WriteLine("Reader: " + reader.GetValue(0));
}
}
}
As a result, there are 3 SELECT. How do you get access to 2 and 3?

How to get two tables returned by stored procedure [duplicate]

This question already has answers here:
Retrieve data from stored procedure which has multiple result sets
(10 answers)
Closed 5 years ago.
My method:
[Function(Name = "get_values")]
[ResultType(typeof(Values_Result))]
public IMultipleResults getvaluesresult([Parameter(DbType = "Int")] System.Nullable<int> Id1, [Parameter(DbType = "VarChar(100)")] string Id2)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), Id1, Id2);
return ((IMultipleResults)(result.ReturnValue));
}
Method call:
var resultValues = cont1.getvaluesresult(Convert.ToInt32(clsSession.id1), "101");
var resultValuesList = resultValues.GetResult<Values_Result>().ToList<Values_Result>();
if (resultValuesList != null && resultValuesList.Count > 0)
{
var dataTable = resultValuesList.ToDataTable();
// performing some tasks
}
My stored procedure looks like this:
CREATE PROCEDURE get_values
(#Id2 VARCHAR(50),
#Id1 INT)
AS
BEGIN
DECLARE #temp TABLE (id INT)
INSERT INTO #temp
(SELECT id FROM table1)
SELECT * FROM table2
SELECT * FROM #temp
END
I want to know how to get the two returned tables from the stored procedure.
I don't want to join the tables in the stored procedure as one
Using ADO.NET:
SqlConnection con = new SqlConnection("YourConnection String");
SqlCommand cmd = new SqlCommand();
SqlDataAdapter da = new SqlDataAdapter();
DataSet ds = new DataSet();
cmd = new SqlCommand("get_values", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#Id2", id); // if you have parameters.
da = new SqlDataAdapter(cmd);
da.Fill(ds);
con.Close();
// ds.Tables[0] your first select statement
// ds.Tables[1] your second select statement
Using Entity Framework:
// Create a SQL command to execute the sproc
var cmd = db.Database.Connection.CreateCommand(); // db is your data context
cmd.CommandText = "[dbo].[get_values]";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#Id2", id); // if you have parameters.
db.Database.Connection.Open();
var reader = cmd.ExecuteReader();
// Read Table2 from the first result set
var table2 = ((IObjectContextAdapter)db)
.ObjectContext
.Translate<Table2Model>(reader, "table2", MergeOption.AppendOnly);
// Move to second result set and read #temp
reader.NextResult();
var temp = ((IObjectContextAdapter)db)
.ObjectContext
.Translate<TempModel>(reader, "temp", MergeOption.AppendOnly);
// I forgot if you need to use #temp or temp only
You need to call GetResult function again for the second table.
If you have multiple table result as a result of the stored procedure, you should call GetResult function again for retrieving the next table result.
var resultValues = cont1.getvaluesresult(Convert.ToInt32(clsSession.id1), "101");
// For table2
var resultValuesList1 = resultValues.GetResult<Values_Result>().ToList<Values_Result>();
if (resultValuesList1 != null && resultValuesList1.Count > 0)
{
var dataTable = resultValuesList1.ToDataTable();
// performing some tasks
}
// For temp table
var resultValuesList2 = resultValues.GetResult<Values_Result>().ToList<Values_Result>();
if (resultValuesList2 != null && resultValuesList2.Count > 0)
{
var dataTable = resultValuesList2.ToDataTable();
// performing some tasks
}
var resultValues = cont1.getvaluesresult(Convert.ToInt32(clsSession.id1),
"101");
var resultValuesList = resultValues.GetResult<Values_Result>
().ToList<Values_Result>();
if (resultValuesList != null && resultValuesList.Count > 0)
{
DataSet ds = resultValuesList.ToDataTable();
// performing some tasks
}
this data set can store multiple table and you can work with it

Pattern for using Dapper typed QueryAsync with stored procedure return codes

I'm running into a situation with Dapper calling a stored procedure where it does some validation logic, updates a row, then returns the results of the row as a strongly-typed POCO.
The problem lies in the validation logic, as there are multiple exit conditions where I want to return an error code for a given situation - and when Dapper QueryAsync does not receive a result set that matches the given POCO then it throws an InvalidOperationException.
(Before anybody comments on mixing logic in with the stored procedure, I understand the concern here but at this point it is unavoidable due to strict performance constraints. The design is not the issue to address.)
How can Dapper handle a stored procedure that can return either a POCO or an error code?
I haven't seen anything in Dapper tutorials about reading stored procedure return codes when using QueryAsync<T>.
So my initial thought is to do something like this (pseudocode below):
Stored procedure: make it always return POCO columns as the first result set (SELECT TOP 0 FROM PocoTable)
Stored procedure: then SELECT -1 AS ErrorCode as the final result set
C# repository: instead of doing conn.QueryAsync<Poco>() do conn.QueryMultiple(), then ReadAsync<Poco>, and if that returns null then ReadAsync<int> for the error code
Is there a better way to handle this?
Example code here:
(note that this is pseudocode and only meant to convey the idea, not actually be an executable example - please don't comment on correcting syntax or usage)
Stored procedure:
CREATE PROCEDURE dbo.SwapData
#ID1 INT,
#ID2 INT
AS
BEGIN
DECLARE #Count INT
DECLARE #Swap Table
(
[ID] INT Identity(1,1),
[PocoID] INT,
[MyData] [nvarchar](255)
)
INSERT INTO #Swap
SELECT PocoID, MyData
FROM PocoTable
WHERE PocoID IN (#ID1, #ID2)
SELECT #Count = COUNT(1) FROM #Swap
IF #Count <> 2
BEGIN
SELECT TOP 0 NULL FROM PocoTable
SELECT -1 AS ErrorCode
RETURN -1 -- never used in C# just returning non-0 for convention
END
UPDATE ORIG
SET ORIG.MyData = swap.MyData
FROM PocoTable AS ORIG
INNER JOIN #Swap AS swap ON (ORIG.PocoID = #ID1 AND swap.ID = 2)
OR (ORIG.PocoID = #ID2 AND swap.ID = 1)
SELECT PocoID, MyData
FROM PocoTable
WHERE PocoID = #ID1
END
Repository:
public class Repo
{
public Task<Poco> SwapData(int targetId, int sourceId)
{
using (var conn = GetSqlConnection())
{
await conn.OpenAsync();
var mrs = await conn.QueryMultipleAsync(
"dbo.SwapData",
new { ID1 = targetId, ID2 = sourceId },
commandType: CommandType.StoredProcedure);
var result = await mrs.ReadFirstOrDefaultAsync<TType>();
if (result != null) return result;
var errorCode = await mrs.ReadFirstAsync<int>();
throw new PocoRepositorySwapException(errorCode);
}
}
}
UPDATE
I've discovered a nice solution that works synchronously but NOT asynchronously!
Stored procedure w/ simple return:
CREATE PROCEDURE dbo.SwapData
#ID1 INT,
#ID2 INT
AS
BEGIN
DECLARE #Count INT
DECLARE #Swap Table
(
[ID] INT Identity(1,1),
[PocoID] INT,
[MyData] [nvarchar](255)
)
INSERT INTO #Swap
SELECT PocoID, MyData
FROM PocoTable
WHERE PocoID IN (#ID1, #ID2)
SELECT #Count = COUNT(1) FROM #Swap
IF #Count <> 2
BEGIN
RETURN -1
END
UPDATE ORIG
SET ORIG.MyData = swap.MyData
FROM PocoTable AS ORIG
INNER JOIN #Swap AS swap ON (ORIG.PocoID = #ID1 AND swap.ID = 2)
OR (ORIG.PocoID = #ID2 AND swap.ID = 1)
SELECT PocoID, MyData
FROM PocoTable
WHERE PocoID = #ID1
END
Repository (synchronous, works):
public class Repo
{
public Task<Poco> SwapData(int targetId, int sourceId)
{
using (var conn = GetSqlConnection())
{
var p = new { ID1 = targetId, ID2 = sourceId };
var pWithReturnValue = new DynamicParameters(p);
p.Add("return", null, DbType.Int32, ParameterDirection.ReturnValue);
// no exception when synchronous
var result = conn.Query<Poco>("dbo.SwapData", p, commandType: CommandType.StoredProcedure);
var errorCode = p.Get<int>("return");
if (errorCode != 0)
throw new SwapRepositoryException(errorCode);
return result.FirstOrDefault();
}
}
}
Repository (asynchronous, Dapper exception):
public class Repo
{
public Task<Poco> SwapData(int targetId, int sourceId)
{
using (var conn = GetSqlConnection())
{
var p = new { ID1 = targetId, ID2 = sourceId };
var pWithReturnValue = new DynamicParameters(p);
p.Add("return", null, DbType.Int32, ParameterDirection.ReturnValue);
// "InvalidOperationException: No columns were selected" happens here when async
var result = await conn.QueryAsync<Poco>("dbo.SwapData", p, commandType: CommandType.StoredProcedure);
var errorCode = p.Get<int>("return");
if (errorCode != 0)
throw new SwapRepositoryException(errorCode);
return result.FirstOrDefault();
}
}
}
The only change is the Query to QueryAsync, and that results in an InvalidOperationException.

How to pass parameter for sproc via C#

I have a MVC application that runs the following sproc named sp_GetEmployeeByID:
#ID int = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT #ID, *
from tblEmployee
where ID = #ID
and the method that calls this needs to pass the int parameter however I cant seem to figure this out, here is what i have so far:
public Employee GetSingleEmployee(int ID)
{
string connectionString = ConfigurationManager.ConnectionStrings["KVKDb"].ConnectionString;
Employee emp = new Employee();
using (SqlConnection connect = new SqlConnection(connectionString))
{
SqlCommand sprocCmd = new SqlCommand("sp_GetEmployeeByID " + ID, connect); sprocCmd.CommandType = System.Data.CommandType.StoredProcedure;
connect.Open();
SqlDataReader rdr = sprocCmd.ExecuteReader();
while (rdr.Read() == true)
{
Employee employee = new Employee();
employee.ID = Convert.ToInt32(rdr["ID"]);
employee.City = rdr["City"].ToString();
employee.DateOfBirth = Convert.ToDateTime(rdr["DateOfBirth"]);
employee.Gender = rdr["Gender"].ToString();
employee.Name = rdr["Name"].ToString();
emp = employee;
}
}
return emp;
}
The obvious issue is that there is no sproc named sp_GetEmployeeByID int ID. I want to know how to call that sproc and pass a parameter for the sprocs #ID parameter.
Add a Parameter to the command:
SqlCommand sprocCmd = new SqlCommand("sp_GetEmployeeByID");
sprocCmd.CommandType = System.Data.CommandType.StoredProcedure;
sprocCmd.Parameters.AddWithValue("#ID", ID)

stored procedure returns many records but datareader sees only one

I created a SQL Server stored procedure in which I use a cursor and I created an ASP.NET method to execute that procedure. I'm using a SqlDataReader and while(reader.Read()) to read the values. The problem is that the cursor in the stored procedure returns many rows, but the method reads the first record only. Anyone can help?
stored procedure:
create procedure [dbo].[GetMenusUserGroupCanView]
(
#UserGroupID int
,#LanguageID int
) as begin
declare #MenuID int
declare #Title varchar(255)
declare db_cursor cursor for
(
select MenuID
from TrioGate.dbo.Sys_UserGroupMenus
where UserGroupID=#UserGroupID and ViewFlag='true'
)
open db_cursor
fetch next from db_cursor into #MenuID
while ##fetch_status = 0 begin
select
Sys_Menus.MenuID
,Sys_Menus.ParentMenuID
,Sys_Menus.DescriptionLabelID
,Sys_Menus.TitleLabelID
,Sys_Menus.TooltipLabelID
,Sys_Menus.[Icon]
,Sys_Menus.[MenuName]
,Sys_Menus.[MenuTypeID] as MenuType
,[dbo].[Get_ParentMenu_Name](Sys_Menus.ParentMenuID) as ParentMenuName
,[dbo].[Get_Label_Description](Sys_Menus.TitleLabelID,1) as Title
,[dbo].[Get_Label_Description](Sys_Menus.TooltipLabelID,1) as Tooltip
,[dbo].[Get_Label_Description](Sys_Menus.DescriptionLabelID,1) as [Description]
,Sys_Menus.[MainTableName] as [Table]
,Sys_Menus.[Seq],Sys_Menus.[MenuPath],Sys_Menus.ActivateLog,Sys_Menus.MenuID
from Sys_Menus
left join Sys_LabelDetails
on Sys_Menus[TitleLabelID] = Sys_LabelDetails[LabelID]
where Sys_LabelDetails.LanguageID = #LanguageID
and MenuID = #MenuID
fetch next from db_cursor into #MenuID
end
close db_cursor
deallocate db_cursor
end
Method:
public List<Menu> GetMenusUserGroupCanView(int UserGroupID, int LanguageID)
{
List<Menu> list = new List<Menu>();
SqlCommand cmd = new SqlCommand("GetMenusUserGroupCanView ", Connection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#UserGroupID", UserGroupID);
cmd.Parameters.AddWithValue("#LanguageID", LanguageID);
try
{
Connection.Open();
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
Menu entry = new Menu();
entry.MenuID = (int)reader["MenuID"];
entry.ParentMenuID = (int)reader["ParentMenuID"];
entry.ActivateLog = (bool)reader["ActivateLog"];
entry.Description = reader["Description"].ToString();
entry.DescriptionLabelID = (int)reader["DescriptionLabelID"];
entry.Icon = (byte[])reader["Icon"];
entry.MainTableName = reader["Table"].ToString();
entry.MenuName = reader["MenuName"].ToString();
entry.MenuPath = reader["MenuPath"].ToString();
entry.MenuTypeID = reader["MenuType"].ToString();
entry.ParentMenuName = reader["ParentMenuName"].ToString();
entry.Seq = (int)reader["Seq"];
entry.Title = reader["Title"].ToString();
entry.TitleLabelID = (int)reader["TitleLabelID"];
entry.Tooltip = reader["Tooltip"].ToString();
entry.ToolTipLabelID = (int)reader["ToolTipLabelID"];
list.Add(entry);
}
}
catch { }
finally
{
Connection.Close();
}
return list;
}
Each iteration of your loop performs a select. So, instead of one set of multiple records, you're getting multiple sets.
Choose your destiny:
A. (treat the symptom): advance your reader between resultsets with IDataReader.NextResult()
or B. (fix the problem): There's no need for that cursor; use a join instead:
select
* /* dump your columns back in here */
from Sys_UserGroupMenus as g
join Sys_Menus as m
join Sys_LabelDetails as d
on d.LabelID = m.TitleLabelID
and d.LanguageID = #LanguageID
on m.MenuID = g.MenuID
where g.UserGroupID = #UserGroupID
and g.ViewFlag = 'true'

Categories

Resources