I'm trying to create a stored procedure to add or update a collection of object executed in c#, but not sure why it isn't executing correctly in c# while it works fine in sql.
I'm not sure how generate some useful error message to help with debugging either.
Any tip or pointer to guide me to the right direction is greatly appreciated.
Here's the sample code:
SQL script:
CREATE TYPE CodeList
AS TABLE
(
Code varchar(255) NOT NULL UNIQUE,
Name varchar(max) NOT NULL,
GeneratedDate date NULL
);
GO
PRINT 'Created CodeList Type';
GO
CREATE PROCEDURE AddOrUpdateCodes
#List AS CodeList READONLY
AS
BEGIN
SET NOCOUNT ON;
MERGE dbo.Code AS tgt
USING #List AS src
ON tgt.Code = src.Code
WHEN MATCHED THEN
UPDATE SET Name = src.Name, GeneratedDate = src.GeneratedDate
WHEN NOT MATCHED THEN
INSERT (Code, Name, GeneratedDate) VALUES (src.Code, src.Name, src.GeneratedDate);
END
GO
PRINT 'Created AddOrUpdateCodes Stored Procedure';
GO
DECLARE #List AS CodeList;
INSERT INTO #List (Code, Name, GeneratedDate) VALUES ('SQLTEST', 'SQLTEST', '2018-07-30')
EXEC AddOrUpdateCodes #List
GO
SELECT * FROM Symbol;
C# Code:
public int AddOrUpdateCodes(List<Code> codes)
{
using (var entity = new CodeEntities())
{
var dataTable = new DataTable("CodeList");
using (var reader = ObjectReader.Create(codes, "Code", "Name", "GeneratedDate"))
{
dataTable.Load(reader);
}
var sqlParameter = new SqlParameter("#List", dataTable);
sqlParameter.SqlDbType = SqlDbType.Structured;
sqlParameter.TypeName = "dbo.CodeList";
var result = entity.Database.ExecuteSqlCommand("AddOrUpdateCodes",
sqlParameter);
entity.SaveChanges();
return result;
}
}
The DbContext.Database.ExecuteSqlCommand() method helps to executes the given DDL/DML command against the database. Try using this line passing SqlParameters:
var result = entity.Database.ExecuteSqlCommand("AddOrUpdateCodes #List",
sqlParameter);
entity.SaveChanges();
return result;
Related
I'm new to Dapper - please help me. How can I get the inserted record value after a successful insert?
Stored procedure:
ALTER PROCEDURE Sp_InsertTestData
#Name varchar(50),
#gender int,
#refres int OUTPUT
AS
BEGIN
INSERT INTO Test_Master (Name, Gender)
VALUES (#Name, #gender);
SELECT #refres = SCOPE_IDENTITY()
SELECT #refres as M_SID
END
When I execute this stored procedure in SQL like this:
DECLARE #refres INT
EXEC Sp_InsertTestData 'test12',1,#refres
I'm getting an output showing the last inserted row's value.
But when this stored procedure is executed from C# code, every time I'm getting a value of 1:
using (SqlConnection con = new SqlConnection(_configuration.GetConnectionString("DatabaseConnection")))
{
con.Open();
SqlTransaction sqltrans = con.BeginTransaction();
var param = new DynamicParameters();
param.Add("#Name", Bindtestmaster.Name);
param.Add("#gender", Bindtestmaster.Gender);
param.Add("#refres");
res = con.Execute("Sp_InsertTestData", param, sqltrans, 0, CommandType.StoredProcedure);
}
That's because you are getting the result of the stored procedure call, which tells you the number of rows inserted (which is 1).
You want to read the output parameter #refres (and add it to your DynamicParameters as an output parameter)
/* ... */
param.Add("#refres", dbType: DbType.Int32, direction: ParameterDirection.Output);
con.Execute("Sp_InsertTestData", param, sqltrans,0,CommandType.StoredProcedure);
var yourId = param.Get<int>("#refres");
Btw, on your stored procedure instead of:
select #refres=SCOPE_IDENTITY()
You might want to prefer this:
SET #refres = SCOPE_IDENTITY() AS INT
And I'm not sure what that last SELECT is there for
Or directly output the inserted ID (using the OUTPUT SQL clause on the INSERT) and then you could read the result, and not have an output parameter at all.
Since your stored procedure also selects the output:
select #refres as M_SID
An easier way of doing this might be:
var id = con.ExecuteScalar<int>("Sp_InsertTestData", new {
Name = Bindtestmaster.Name,
gender = Bindtestmaster.Gender
}, sqltrans, 0, CommandType.StoredProcedure);
and forget DynamicParameters etc. You could also consider using the OUTPUT clause in the SQL, to simplify it:
ALTER PROCEDURE Sp_InsertTestData
#Name varchar(50), #gender int
AS
BEGIN
INSERT INTO Test_Master(Name, Gender)
OUTPUT INSERTED.Id -- whatever the column is here
VALUES (#Name, #gender);
END
I have a solution that bulk loads data via a datatable from c#, like this :
private void LoadRemittanceLines(DataTable dt, EiseBEDbEntities context)
{
var param = new SqlParameter("Payments", SqlDbType.Structured)
{
Value = dt,
TypeName = "dbo.udtRAPayment",
Direction = ParameterDirection.Input
};
var r = context.Database.ExecuteSqlCommand("EXEC spRAPaymentInsertBulk", param);
}
The line fires, no errors, but the table yields no results. I profiler'ed my Db, and caught the code that executes. Here is a summarized, masked example:
--THIS IS ADDED BY ME TO CLEAR THE TABLE
delete from EiseBEDb..RAPayment
declare #p3 dbo.udtRAPayment
insert into #p3 values(1234879,987654,1,123.49,'2017-09-20 00:00:00','2017-09-20 00:00:00',NULL,0,'2018-07-22 00:00:00',10,0,NULL,NULL,1,N'This is a long string',NULL,NULL,NULL,N'',N'',N'1234',N'','2017-09-12 00:00:00',NULL,NULL)
--THIS CODE YIELDS RESULTS
--EXEC spRAPaymentInsertBulk #Payments=#p3
--THIS CODE YIELDS NO RESULTS
exec sp_executesql N'EXEC spRAPaymentInsertBulk',N'#Payments [dbo].[udtRAPayment] READONLY',#Payments=#p3
--THIS IS ADDED BY ME TO CHECK THE DATA
select * from EiseBEDb..RAPayment
What am I missing, and is there a way I can get passed this? The only solution I can think of is to not use EF to pass my data, but rather just making a connection to the Db manually end calling that working line of code.
Here is spRAPaymentInsertBulk, fieldnames omitted due to confidentiality.
CREATE PROCEDURE [dbo].[spRAPaymentInsertBulk]
(
#Payments udtRAPayment readonly
)
AS
begin
insert into RAPayment (Field1, field2)
select Field1, field2
from #Payments p
end
I'm using SQL Server 2017.
My problem was with the text I passed, this resolved the issue:
var r = context.Database.ExecuteSqlCommand("exec spRAPaymentInsertBulk #Payments", param);
I want to use a SELECT statement into a table based on multiple values from ListBox and get multiple IDs and then run an INSERT statement and store the multiple IDs into a different table from INSERT statement.
My code below is not working as I am getting "NULL" in single row instead of multiple IDs in multiple rows.
I am using a stored procedure for all the SQL statements.
Please see my code below:
Code-behind of my ASPX web page:
string listboxvalue = "";
foreach (ListItem item in listbox.Items)
{
if (item.Selected)
{
listboxvalue += item.Text + ',';
}
}
listboxvalue = listboxvalue.Substring(0, listboxvalue.Length - 1);
cmd.Parameters.AddWithValue("spselectvalue", listboxvalue);
Stored procedure:
#spselectvalue nvarchar(MAX),
// Select multiple Ids based on multiple items from list box
DECLARE #Dis TABLE (DisID int)
INSERT INTO #Dis
SELECT DId
FROM [table name]
WHERE [COLUMN] IN ('+#spselectvalue +')
EXEC sp_executesql #Dis
// Insert multiple Ids (from above select statement) into different table
INSERT INTO [dbo].[DifferentTable] ([SelectedIds])
VALUES
(
(SELECT DisID from #Dis)
)
Seems like your problem is that you don't know how to get a list of ID's into a stored procedure for processing. Hopefully this example will help. If you do it this way, you won't have to mess with comma-delimited strings or dynamic SQL.
First, define a SQL type that can contain your list of IDs:
CREATE TYPE dbo.MyList
AS TABLE
(
ID VarChar(50)
);
Then, write a stored procedure that accepts this type as its input parameter:
CREATE PROCEDURE dbo.InsertAList
#List AS dbo.MyList READONLY
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [dbo].[DifferentTable]
SELECT ID FROM #List
END
Now to bind your data to the stored procedure from the c# end. To bind it, you have to store the data in a DataTable first, which is relatively easy:
var table = new DataTable();
table.Columns.Add("ID", typeof(string));
foreach (ListItem item in listBox.Items.Where(i => i.Selected))
{
table.Rows.Add(item.Text);
}
Then submit the table to the stored procedure like so:
var cmd = new SqlCommand()
{
CommandType = CommandType.StoredProcedure,
CommandText = "InsertAList",
Connection = myConnection
};
cmd.Parameters.Add(new SqlParameter("#List", SqlDbType.Structured)
{
TypeName = "dbo.MyList",
Value = table
});
cmd.ExecuteNonQuery();
That doesn't seem like a valid (or logical) SQL. The exec sp_executesql #Dis shouldn't be there or #Dis shouldn't be a table.
When using sp_executesql the parameter should be a string containing the statement to execute (documentation).
Detailed answer can be found here.
Also: the INSERT INTO #Dis ... line won't work as you expect when written that way, for the reasons mentioned in the linked question (and Erland Sommarskog blog mentioned there).
Selected values considered as numeric.
DECLARE #Sqltext nvarchar(max) =
'INSERT INTO [dbo].[DifferentTable] ([SelectedIds])
SELECT DId
FROM [table name]
WHERE [COLUMN] IN (' + #spselectvalue + ')'
EXEC sp_executesql #Sqltext
I have a below stored procedure with output parameter.
ALTER proc [dbo].[CRS_GetNewMessageCount]
#CaseId int,
#Type varchar(50),
#Location varchar(50),
#Count int out
as
begin
if #location='admin'
begin
if #type='consumer'
select #Count=(Select count(fdId) from tb_CRS_Messages where fdCaseId=#CaseId and fdIsConsumerType=1 and fdIsReadatAdmin=0)
else
select #Count=(Select count(fdId) from tb_CRS_Messages where fdCaseId=#CaseId and fdIsMemberType=1 and fdIsReadatAdmin=0)
end
else
begin
if #type='consumer'
select #Count=(Select count(fdId) from tb_CRS_Messages where fdCaseId=#CaseId and fdIsConsumerType=1 and fdIsReadatFront=0)
else
select #Count=(Select count(fdId) from tb_CRS_Messages where fdCaseId=#CaseId and fdIsMemberType=1 and fdIsReadatFront=0)
END
SELECT #Count
END
It is perfect working at SQL server see below output :
I am calling this stored procedure through Entity Framework :
using (DbContext db = new DbContext())
{
try
{
var NewMessage = new ObjectParameter("Count", typeof(int));
int returnValue = 0;
db.CRS_GetNewMessageCount(CaseId, type, location, NewMessage);
int.TryParse(NewMessage.Value.ToString(), out returnValue);
return returnValue;
}
catch { return 0; }
}
It gives null value in output parameter.
Please help.
Not sure whether it makes a difference, but did you try to use "typeof(Int32)" instead of "typeof(int)"? Also try NewMessage as ObjectParameter and not as var, maybe it makes a difference.
ObjectParameter NewMessage = new ObjectParameter("NewMessage ", typeof(Int32));
Did you try to run your stored procedure without parameters, means return #Count-variable with a value no matter what parameters you enter? Like this you can identify whether your input parameters are wrong (handed over by C#) or not.
Hope this helps How to: Execute a Query Using a Stored Procedure with In and Out Parameters
https://msdn.microsoft.com/en-us/library/bb896334(v=vs.100).aspx
have a stored Procedure that do some operation and return 1 or 0 as below.
CREATE PROCEDURE dbo.UserCheckUsername11
(
#Username nvarchar(50)
)
AS
BEGIN
SET NOCOUNT ON;
IF Exists(SELECT UserID FROM User WHERE username =#Username)
return 1
ELSE
return 0
END
Using linq I tried to get return value. But i am getting the output as -1 always.
Below is my Linq code :
using (PlanGenEntities3 entity2 = new PlanGenEntities3())
{
var result = entity2.Entity_test_GetHoliday();
string output = result.ToString();
}
How to solve this ?
Just declare your scalar variable as a output parameter for SQL query, then in the code you could retrieve its value this way:
```
using (PlanGenEntities3 myContext = new PlanGenEntities3())
{
var outputParameter = new System.Data.Objects.ObjectParameter("OutputParameterName", typeof(int));
myContext.Entity_test_GetHoliday(outputParameter );
Console.WriteLine(outputParameter.Value);
}
```
I suppose there is way to access the default scalar output of your SP in the similar manor.
If he had it as an output parameter it would automatically show as a reference parameter of the proper corresponding data type in the LINQ call.
He wouldn't really need to create a parameter object to contain that.