Reputation: 1009
I have a stored procedure that checks for cards that will expire within the next 90, 60, 30, 21, 14 and 7 days. When I execute the stored procedure in SQL Server, I get all the rows, but when I invoke the same stored procedure from code, it only returns the first row and does not loop the entire resultset.
This is how I'm retrieving the results:
CREATE PROCEDURE [dbo].[GetExpiringCards]
AS
DECLARE @today datetime2 = CAST(GETDATE() AS date)
DECLARE @INTERVALS TABLE (intval int)
INSERT INTO @INTERVALS (intval) VALUES (90)
INSERT INTO @INTERVALS (intval) VALUES (60)
INSERT INTO @INTERVALS (intval) VALUES (30)
INSERT INTO @INTERVALS (intval) VALUES (21)
INSERT INTO @INTERVALS (intval) VALUES (7)
INSERT INTO @INTERVALS (intval) VALUES (14)
DECLARE @value int
DECLARE db_cursor CURSOR FOR
SELECT intval FROM @INTERVALS
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO @value
WHILE @@FETCH_STATUS = 0
BEGIN
SET NOCOUNT ON;
SELECT
@value AS Interval, DATEADD(DD, @value, @today) AS ExpiresOn,
CustomerId, IssueBranch
FROM
Cards
WHERE
ExpiryDate = CONVERT(DATE, DATEADD(DAY, @value, GETDATE()))
FETCH NEXT FROM db_cursor INTO @value
END
CLOSE db_cursor
DEALLOCATE db_cursor
GO
In the code I am invoking as:
using (SqlConnection conn = new SqlConnection(DatabaseConnect2))
{
SqlCommand cmd = new SqlCommand("GetExpiringCards", conn);
cmd.CommandType = CommandType.StoredProcedure;
conn.Open();
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
Customer customer = new Customer ();
customer.Interval = Convert.ToInt32(dr["Interval"]);
customer.ExpiresOn = Convert.ToDateTime(dr["ExpiresOn"]);
customer.CustomerId = Convert.ToString(dr["CustomerId"]);
customer.IssueBranch = Convert.ToString(dr["IssueBranch"]);
customer.Add(upload);
}
conn.Close();
}
When I execute the stored procedure in SSMS I get the following:
I have tried changing the logic as below but the flow immediately loops out once it fetches the first row:
using (SqlConnection conn = new SqlConnection(DatabaseConnect2))
{
SqlCommand cmd = new SqlCommand("GetExpiringCards", conn);
cmd.CommandType = CommandType.StoredProcedure;
conn.Open();
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read() && dr.HasRows)
{
Customer customer = new Customer ();
customer.Interval = Convert.ToInt32(dr["Interval"]);
customer.ExpiresOn = Convert.ToDateTime(dr["ExpiresOn"]);
customer.CustomerId = Convert.ToString(dr["CustomerId"]);
customer.IssueBranch = Convert.ToString(dr["IssueBranch"]);
customer.Add(upload);
}
conn.Close();
}
What am I missing?
Upvotes: 0
Views: 1229
Reputation: 46203
Each FETCH
returns one row as a result set back to the application. Although you could change the C# code to handle that (i.e. dr.NextResult()
), it would be better to return all the rows in a single query. Below is a single-statement example that also uses a table value constructor instead of table variable.
CREATE PROCEDURE [dbo].[GetExpiringCards]
AS
SET NOCOUNT ON;
SELECT
i.value AS Interval
, DATEADD(DD,value,GETDATE()) AS ExpiresOn
, CustomerId, IssueBranch
FROM Cards AS c
CROSS JOIN (SELECT value FROM (VALUES(90),(60),(30),(21),(7),(14)) AS intervals(value)) AS i
WHERE c.ExpiryDate=CONVERT(DATE, DATEADD(DAY, i.value, GETDATE()));
Upvotes: 3