How to convert this stored procedure to simple query - c#

I write this stored procedure but I am returning all sql queries to site code and I have a little problem to convert this query. I insert data in one table then insert it in another table with key that is generated in first table. I don't know what is the best way to write this from site code. To make three methods or what?
#m_UserId uniqueidentifier,
#m_WispTypeId int,
#m_CreatedOnDate datetime,
#m_PrivacyTypeId int,
#m_WispText nvarchar(200)
AS
SET XACT_ABORT, NOCOUNT ON
DECLARE #starttrancount int
BEGIN TRY
SELECT #starttrancount = ##TRANCOUNT
IF #starttrancount = 0
BEGIN TRANSACTION
DECLARE #wispId int
INSERT INTO dbo.tbl_Wisps
(UserId,WispTypeId,CreatedOnDate,PrivacyTypeId,WispText)
VALUES
(#m_UserId,#m_WispTypeId,#m_CreatedOnDate,#m_PrivacyTypeId,#m_WispText)
SELECT #wispId = SCOPE_IDENTITY()
INSERT INTO dbo.tbl_CommentableEntity
(ItemId)
VALUES
(#wispId)
DECLARE #ceid int
select #ceid = SCOPE_IDENTITY()
UPDATE dbo.tbl_Wisps SET CommentableEntityId = #ceid WHERE WispId = #wispId
IF #starttrancount = 0
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0 AND #starttrancount = 0
ROLLBACK TRANSACTION
RAISERROR ('Error in adding new wisp', 16, 1)
END CATCH

There are a number of ways to use the OUTPUT clause. The following pattern may work for you:
insert dbo.tbl_CommentableEntity (ItemId)
select wispId from (
insert dbo.tbl_Wisps (UserId,WispTypeId,CreatedOnDate,PrivacyTypeId,WispText)
output inserted.wispId
values (#m_UserId, #m_WispTypeId, #m_CreatedOnDate, #m_PrivacyTypeId, #m_WispText)
) as ins

If you want to do it in code, I would split it into several methods and have the save methods return the identity. Then, you can just create a method that encompasses all three queries, and which emulates through code the same logic that is in the stored procedure
public int SaveThis()
{
return -1 //return identity
}
public int SaveThat(int thisID)
{
return -2 //return identity
}
public void SaveThisAndThat()
{
int thisID = this.SaveThis();
int thatID = this.SaveThat(thisID);
//so on and so forth
}

Related

Can't call stored procedure

I'm a beginner at C#. I can't call a stored procedure.
My stored procedure is this:
CREATE PROCEDURE USP_login
#us VARCHAR(20),
#pwd VARCHAR(20)
AS
BEGIN TRAN
BEGIN TRY
SELECT *
FROM dbo.KhachHang
WHERE tenDangNhap = #us AND matKhau = #pwd
END TRY
BEGIN CATCH
ROLLBACK TRAN
RETURN 0
END CATCH
COMMIT TRAN
RETURN 1
GO
In my C# code, I use this function to call the USP_login stored procedure but it doesn't work:
public bool loginStored(string us, string pwd)
{
object[] sqlParams =
{
new SqlParameter ("#userName", us),
new SqlParameter ("#passWord", pwd),
};
var rs = db.Database.SqlQuery<bool>("USP_login #userName, #passWord", sqlParams).SingleOrDefault();
return rs;
}
Error message in screenshot:
Looks like SELECT * ... is returning more than just a single bool. (Based on the query, clearly the table has at least two fields, tenDangNhap and matKhau.) But that's what you told the code to expect:
db.Database.SqlQuery<bool>(/.../)
Either select only the column you want:
SELECT SomeBooleanValue FROM dbo.KhachHang WHERE tenDangNhap=#us AND matKhau=#pwd
Or specify the correct type that can be expected for each record (which may be a custom class that you need to define):
db.Database.SqlQuery<SomeObjectType>(/.../)

oracle stored procedure return resultset

Can I define the stored procedure without using the RefCursor ? (like "return refcursor")
I do not want to use OracleDbType.RefCursor because it is not sent as dbparameter in other databases.
Also DbParameter.DbType = OracleDbType.RefCursor; does not supported
I do not want to define "retval IN OUT SYS_REFCURSOR" in the code below. Is there another way?
CREATE OR REPLACE procedure SYSTEM.customer_select_row(
p_email IN CUSTOMER.Email%TYPE,
p_password IN CUSTOMER."Password"%TYPE,
retval IN OUT SYS_REFCURSOR
)
IS
BEGIN
OPEN retval FOR
SELECT CustomerId, FirstName, LastName FROM CUSTOMER
WHERE Email = p_email AND "Password" = p_password
END customer_select_row;
You could use a pipeline Function,
It is a function that works exacltly as a table
you can call it this way
SELECT *
FROM TABLE(TEST_PIPELINE.STOCKPIVOT(10));
the TEST_PIPELINE.STOCKPIVOT(10) is a function
you can build it this way:
create or replace PACKAGE TEST_PIPELINE AS
-- here you declare a type record
type t_record is record
(
field_1 VARCHAR2(100),
field_2 VARCHAR2(100));
-- declare a table type from your previously created type
TYPE t_collection IS TABLE OF t_record;
-- declare that the function will return the collection pipelined
FUNCTION StockPivot(P_LINES NUMBER) RETURN t_collection PIPELINED;
END;
/
create or replace PACKAGE BODY TEST_PIPELINE IS
FUNCTION StockPivot(P_LINES NUMBER) RETURN t_collection PIPELINED IS
-- declare here a type of the record
T_LINE T_RECORD;
BEGIN
-- here is a loop example for insert some lines on pipeline
FOR I IN 1..P_LINES LOOP
-- inser data on your line this way
T_LINE.field_1 := 'LINE - ' || I;
T_LINE.field_2 := 'LINE - ' || I;
-- then insert insert the line for result (this kind of functions should not have a return statement)
PIPE ROW (T_LINE );
END LOOP;
END;
END;

SQL return 2 values to C# web service

I have a SQL Stored Procedure that works like this: it updates the Confirmed column in a PaymentRecords table, which has a default value of 0, to be either 1 (approved) or 2 (rejected). To ensure concurrency control, it prevents updates from occurring if the Confirmed value is already 1 or 2.
When the update is successful, it returns a string 'SUCCESS', where as if it fails, it returns the chequeNumber to display a message saying that particular cheque has already been updated. Both of these are returned to a C# web service.
SQL:
ALTER PROCEDURE [dbo].[UpdateUnverifiedChequeConfirmed]
-- Add the parameters for the stored procedure here
#id uniqueidentifier,
#confirmed int,
#rejectReason nvarchar(MAX),
#bankName nvarchar(MAX)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #existingConfirmed int;
-- Insert statements for procedure here
SELECT TOP 1 #existingConfirmed = [PaymentRecords].[confirmed]
FROM [dbo].[PaymentRecords]
INNER JOIN [dbo].[ChequePayments]
ON [ChequePayments].[paymentRecordID] = [PaymentRecords].[id]
WHERE [ChequePayments].[id] = #id
IF #existingConfirmed = '1' OR #existingConfirmed = '2'
BEGIN
PRINT 'Cheque already updated by another user, further changes not allowed.';
--SELECT 0 AS FAIL
SELECT [ChequePayments].[chequeNo] AS chequeNo
FROM [ChequePayments]
WHERE [ChequePayments].[id] = #id
END
ELSE
BEGIN
UPDATE [dbo].[PaymentRecords]
SET confirmed = #confirmed, rejectReason = #rejectReason, [timestamp] = GETDATE()
FROM [dbo].[PaymentRecords]
INNER JOIN [dbo].[ChequePayments]
ON [ChequePayments].[paymentRecordID] = [PaymentRecords].[id]
WHERE [ChequePayments].[id] = #id
UPDATE [dbo].[ChequePayments]
SET bankName = #bankName, [timestamp] = GETDATE()
WHERE [ChequePayments].[id] = #id
--SELECT 1 AS SUCCESS
SELECT 'SUCCESS' AS SUCCESS
END
END
C#:
unverifiedChequeNumberResponse ICommon.updateUnverifiedCheque(unverifiedChequeRequest request, string id)
{
string result, rejectReason;
Guid newID;
if (request.bankName == "Select a Bank")
{
return new unverifiedChequeNumberResponse("Error: No bank selected.");
}
if (request.rejectReason == "")
{
rejectReason = null;
}
else
{
rejectReason = request.rejectReason;
}
if (Guid.TryParse(id, out newID))
{
try
{
result = dbkl.UpdateUnverifiedChequeConfirmed(newID, request.confirmed, rejectReason, request.bankName).FirstOrDefault();
}
catch (Exception ex)
{
if (isDebug() == true)
{
return new unverifiedChequeNumberResponse(ex.Message);
}
else
{
return new unverifiedChequeNumberResponse("Error: Database inaccessible");
}
}
if (result == "SUCCESS")
{
return new unverifiedChequeNumberResponse();
}
else
{
unverifiedChequeNumberResponse response = new unverifiedChequeNumberResponse();
response.chequeNo = result;
return new unverifiedChequeNumberResponse("Error: Cheque " + response.chequeNo + " already updated by another user.");
}
}
else
{
return new unverifiedChequeNumberResponse("Error: Invalid ID.");
}
}
Although all of this is working properly, I need to make a change so that the SP returns a 1 when successful and 0 when unsuccessful. The two issues I am facing are:
How to make the unsuccessful update path in the SP return 2 values, namely 0 and the chequeNumber? If I write it as:
SELECT 0 AS FAIL, [ChequePayments].[chequeNo] AS chequeNo
FROM [ChequePayments]
WHERE [ChequePayments].[id] = #id
it ends up returning a null value and throws an exception.
If the chequeNumber has a value of 1 (unlikely in practice but still worth covering) and the update is denied, the web service will still return the success message. Is there a way to change it so that the service can differentiate between 1 as a chequeNumber and 1 as a Success value?
You could look at using SQL Output parameters. For example;
ALTER PROCEDURE [dbo].[UpdateUnverifiedChequeConfirmed]
-- Add the parameters for the stored procedure here
#id uniqueidentifier,
#confirmed int,
#rejectReason nvarchar(MAX),
#bankName nvarchar(MAX),
#result1 int output,
#result2 varchar(50) output
AS
BEGIN
-- do stuff...
set #result1 = 123
set #result2 = 'success'
END
You would have to update your .Net code to define the output paramaters then read their values after the call.
You might also want to consider using transactions for concurrency control - that's what they're for, and they will provide a more robust solution than you have here.

EF6 stored procedure must declare scalar variable

I am trying to call a stored procedure using C# EF6 to bring back data. I have tried to run the stored procedure in SQL management studio and it seems to work fine, however when I try to run it in my application I get an error saying "Must declare the scalar variable "#devID"
Here is part of my method in my application calling the stored procedure
public IHttpActionResult GetMetrics(int deviceID, string attribute, string startDate)
{
if (deviceID == 0)
{
return NotFound();
}
var metrics = db.Database.SqlQuery<Metrics>("GetMetrics #devID, #MetricType, #startTime", deviceID, attribute, startDate).ToList();
and here is my stored procedure:
ALTER PROCEDURE [dbo].[GetMetrics]
-- Add the parameters for the stored procedure here
#devID int,
#MetricType nvarchar(20),
#startTime nvarchar(50)
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 *
FROM dbMetrics
WHERE deviceID = #devID and MetricType = #MetricType and timeStamp >= #startTime
ORDER BY timeStamp
END
As per the documentation, if you want to use named parameters, you need to pass SqlParameter objects like this:
var metrics = db.Database.SqlQuery<Metrics>("GetMetrics #devID, #MetricType, #startTime",
new SqlParameter("devID", deviceID),
new SqlParameter("MetricType", attribute),
new SqlParameter("startTime", startDate)
).ToList();

Access stored procedure value with LINQ. Entity Framework 5, DB first

My stored procedure is working correctly. However, I am not able to retrieve it.
My current function to retrieve the value from the stored procedure is:
public static int GetCsStatus()
{
using (Entities db = new Entities())
{
System.Data.Objects.ObjectParameter s = new System.Data.Objects.ObjectParameter("Status", typeof(int));
int r = db.proc_CsStatus(120, s);//.ToString());
return r;
}
}
I don't mind if this is changed or not used at all. I am currently getting a "r" value of -1 when I am expecting a 0 or 1.
Here is my stored procedure:
USE [DATABASE_CS]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[proc_CsStatus]
-- Add the parameters for the stored procedure here
#TimeLimit Int,
#Status Int OUTPUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON
-- Declare variables.
DECLARE #LastUpdate Int
-- Calculate the LastUpdate.
SELECT #LastUpdate = DATEDIFF(second, Timestamp, CURRENT_TIMESTAMP)
FROM Heartbeat
WHERE Id=1
-- Compare it to the TimeLimit.
IF #LastUpdate > #TimeLimit SELECT #Status = 0
ELSE SELECT #Status = 1
END
GO
Any input is much appreciated!!!
After executing your procedure, your the ObjectParameter s will contain the value. Your procedure call will not return it. The value you are looking for should be able to be found in s.Value.
Try the following:
public static int GetCsStatus()
{
using (Entities db = new Entities())
{
System.Data.Objects.ObjectParameter s = new System.Data.Objects.ObjectParameter("Status", typeof(int));
int r = db.proc_CsStatus(120, s);
return (int)s.Value;
}
}
The value which you are returning(r) is the number of rows affected by your procedure.
More Info:
Behind the scenes, your procedure is doing something along the lines of the following:
return base.ExecuteFunction("proc_CsStatus", input, output);
ObjectContext.ExecuteFunction() returns the number of rows affected by the call.

Categories