I have a table Student. This table has a column ID which is auto-increment. I want to write a stored procedure to add a new student and return the ID. I'm newbie at this.
This is my code. Please check if it's wrong and fix for me and show me how to code to use it in C#. I used Entity Framework 4.
#Name NVARCHAR(50),
#Birthday DATETIME,
#ID bigint OUTPUT
AS
BEGIN
SET XACT_ABORT ON
BEGIN
INSERT INTO [Student](Name, Birthday) VALUES (#Name, #Birthday);
SELECT #ID = SCOPE_IDENTITY()
END
END
It is better you can C# code instead SP when your working with EF4.
Using Entity Framework, this is all done automagically for you.
using (MyEntities context = new MyEntities())
{
var student = new Student()
{
Name = "some value",
Birthday = "some Birthday"
};
context.Students.AddObject(student);
context.SaveChanges();
int ID = student.ID; // You will get here the Auto-Incremented table ID value.
}
Saving in Entity Framework will automatically update the "auto-increment" ID column. You just read it out after the call to .SaveChanges();
EDIT:
Also read this post if you encounter any issues getting the "auto-increment" ID value.
#Name NVARCHAR(50),
#Birthday DATETIME,
#ID bigint OUTPUT
AS
BEGIN
SET XACT_ABORT ON
BEGIN
INSERT INTO [Student](Name,Birthday)
VALUES(#Name,#Birthday);
SELECT #ID = SCOPE_IDENTITY()
END
I just added commas in between fields
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.aspx
This should provide you all the information you need to build a C# application calling your Stored Procedure.
Related
I have a Windows application written in C# using embedded firebird sql version 2.5.5.26952, which I am re-working it to update to embedded firebird sql version 4.0.0.2496. I have update the fdb file to the new version,and all the tables and sprocs, are there. When run a cmd.Fill() command for a selected statement rows are returned, if I do a update for a row in the table, I get the expected results back fine. but If I do a insert nothing is returned, and no errors are thrown, but the data is added to the database. If I run the sproc from the FireRobin application, the data is inserted, and a row is returned, so I'm at a loss to know why it is not working from my C# application. below is slimmed down version of the code.
The 2.5 version is using FirebirdSql.Data.FirebirdClient.4.10.0.0
The 4.0 version is using FirebirdSql.Data.FirebirdClient.9.0.2
using (var cmd = new FbDataAdapter("PROC_UPSERTPEOPLE", _connection)
{
DataTable data = new DataTable();
cmd.SelectCommand.Parameters.Add("SURNAME", FbDbType.Text).Value = item.Surname;
cmd.SelectCommand.Parameters.Add("FORENAMENAME", FbDbType.Text).Value = item.Forename);
var transaction = _connection.BeginTransaction();
cmd.SelectCommand.CommandType = CommandType.StoredProcedure;
cmd.SelectCommand.Transaction = transaction;
var result = cmd.Fill(data);
transaction.Commit();
}
On a update result contains 1, and data has the expected result, but on a insert result = 0, and data has no rows in.
Any help would be appreciated.
This is the simple version fo the sproc in question
CREATE OR ALTER PROCEDURE PROC_UPSERTPEOPLE_SLIM
(
RECID INTEGER,
SURNAME VARCHAR(100),
FORENAME VARCHAR(100)
)
RETURNS
(
ID INTEGER,
LSURNAME VARCHAR(100),
LFORENAME VARCHAR(100)
)
AS
DECLARE VARIABLE local_id integer;
DECLARE VARIABLE local_surname varchar(100);
DECLARE VARIABLE local_forename varchar(100);
BEGIN
select
ID,
FORENAME,
SURNAME
FROM
APA_PEOPLE
WHERE
(:RECID IS NOT NULL AND ID = :RECID)
OR (:RECID IS NULL
AND FORENAME = :FORENAME
AND SURNAME = :SURNAME)
INTO
:local_id,
:local_forename,
:local_surname;
IF (:local_id IS NULL) then
begin
UPDATE OR INSERT INTO APA_PEOPLE(FORENAME, SURNAME)
VALUES(:FORENAME, :SURNAME)
MATCHING (FORENAME, SURNAME);
end
else
begin
UPDATE APA_PEOPLE SET FORENAME = :FORENAME,
SURNAME = :SURNAME
WHERE ID = :local_id;
end
FOR
SELECT
ID,
SURNAME,
FORENAME
from
APA_PEOPLE
WHERE
(:RECID IS NOT NULL AND ID = :RECID)
OR (:RECID IS NULL
AND FORENAME = :FORENAME
AND SURNAME = :SURNAME)
INTO
:ID,
:LSURNAME,
:LFORENAME
DO
begin
suspend;
end
END;
Update
To answer my own question, being mainly a TSQL developer, DSQL seems strange, change the sproc to the following, which is simpler
CREATE OR ALTER PROCEDURE PROC_UPSERTPEOPLE_SLIM (
RECID integer,
SURNAME varchar(100),
FORENAME varchar(100)
)
RETURNS (ID integer)SQL SECURITY INVOKER
AS
BEGIN
UPDATE OR INSERT INTO APA_PEOPLE(FORENAME, SURNAME)
VALUES(:FORENAME, :SURNAME)
MATCHING (FORENAME, SURNAME)
RETURNING ID INTO :ID;
END;
but also had to change the way it was called, to use
EXECUTE PROCEDURE PROC_UPSERTPEOPLE_SLIM(#RECID, #SURNAME, #FORENAME)
This does seem counter intuitive, I had assumed a stored procedure was a stored procedure, and there are not two different flavors. Oh well it works now, so move on to getting the rest of the app to work.
Model model=new Model();
model.id=0;
model.name="";
model.surname="";
db.Model.Add(model);
db.SaveChanges();
int id_returned=model.id;
This block gives me the id field of the inserted row.
But i use stored procedure for this insert process.
Model model=new Model();
model.id=0;
model.name="";
model.surname="";
int returned_id2=db.Sp_Model_insert(model.name, model.surname);
this block inserts the row. returned_id2 returns -1.
How can i get the row id that inserted via Sp ?
Here is my Stored Procedure:
USE [KayaShop]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [KS_ACCOUNTS].[SP_AccountModules_Insert](
#Area nvarchar(50),
#Controller nvarchar(50),
#Action nvarchar(50),
#SubAction nvarchar(50),
#Name nvarchar(50),
#TopName nvarchar(50),
#Level int,
#Visible int,
#Clickable int,
#HomePage int,
#Icon nvarchar(20),
#Status int
)
AS
BEGIN
SET NOCOUNT ON
INSERT INTO [KS_ACCOUNTS].[AccountModules]
([Area]
,[Controller]
,[Action]
,[SubAction]
,[Name]
,[TopName]
,[Level]
,[Visible]
,[Clickable]
,[HomePage]
,[Icon]
,[Status])
VALUES
(#Area,
#Controller,
#Action,
#SubAction,
#Name,
#TopName,
#Level,
#Visible,
#Clickable,
#HomePage,
#Icon,
#Status)
END
SELECT SCOPE_IDENTITY()
Add this after the insert query in your stored procedure
Multiple ways:
SCOPE_IDENTITY() - https://msdn.microsoft.com/en-us/library/ms190315.aspx - Returns the last single IDENTITY value in the current session's current scope.
##IDENTITY - https://msdn.microsoft.com/en-us/library/ms187342.aspx - Returns the last single IDENTITY value in the current session (regardless of scope).
INSERT INTO ... VALUES ... OUTPUT - https://msdn.microsoft.com/en-us/library/ms177564.aspx - outputs all inserted values, including IDENTITY values, this is the only way to retrieve IDENTITY values when your INSERT statement will insert multiple rows.
As your stored procedure only seems to insert a single row, you should be fine with SCOPE_IDENTITY.
Note that if all your stored procedure does is INSERT you probably don't need a stored procedure, your database client code should perform the INSERT directly.
Note that you should not use the RETURN keyword to return the IDENTITY value to the caller, you should use output parameters - a stored procedure's return-value is intended to convey status information and it is restricted to just an int value (e.g. return 0 for success).
I added these lines to the end of my sp:
SELECT #Id =SCOPE_IDENTITY()
RETURN
and at my code side:
int Id = 0;
ObjectParameter prm = new ObjectParameter("Id", typeof(int));
DB.SP_AccountModules_Insert(DTO.Area, DTO.Controller, DTO.Action, DTO.SubAction, DTO.Name, DTO.TopName, DTO.Level, DTO.Visible, DTO.Clickable, DTO.HomePage, DTO.Icon, DTO.Status, prm);
Id = Convert.ToInt32(prm.Value);
return Id;
i send and out parameter from my code to my sp, then i assign the last inserted id to my parameter and return it to my code. thats it.
Thank you for all help.
I have two tables and I need to update values in them via a stored procedure. Tried too much to update but some times it update the first table only, others the second or even fail due to cannot allow duplicates. Also when it updates the WHOLE data in the table becomes the same as the new updated ones. I've now reached to this error after all these lines of codes
Cannot insert the value NULL into column 'Emp_ID',table 'DatePics'; column does not allow nulls. UPDATE fails.The statement has been terminated
Here is the SQL code :
ALTER procedure [dbo].[UpdateEmp]
#EmpName nvarchar(100),
#Nationality nvarchar(30),
#Passport nvarchar(20),
#ContractDate date,
#HealthDate date
AS
BEGIN
set nocount on;
DECLARE #IDs table (ID int )
UPDATE Employee SET
EmpName=#EmpName, Nationality=#Nationality, Visa=#Visa, Passport=#Passport,
ReceivedDate=#ReceivedDate,IDIssue=#IDIssue, IDExpiry=#IDExpiry, Sponsor=#Sponsor
output inserted.ID into #IDs (ID)
WHERE ID = #ID
UPDATE DatePics SET
FingerDate=#FingerDate, ContractDate=#ContractDate, HealthDate=#HealthDate
where Emp_ID in (select ID from #IDs);
END
After writing the stored procedure code, I wrote the C# code like this:
private void updatebtn_Click(object sender, EventArgs e)
{
SqlCommand cmd = new SqlCommand();
cmd.Connection = db.con;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "UpdateEmp";
cmd.Parameters.AddWithValue("#EmpName", NameSeartxt.Text);
cmd.Parameters.AddWithValue("#Nationality", NatSeartxt.Text);
cmd.Parameters.AddWithValue("#Passport", PassSeartxt.Text);
cmd.Parameters.AddWithValue("#ContractDate", ContractSeartxt.Text);
cmd.Parameters.AddWithValue("#HealthDate", HealthSeartxt.Text);
db.con.Open();
int up = cmd.ExecuteNonQuery();
if (up > 0)
{
MessageBox.Show("Update done ", "DONE !");
SearNametxt.Text = "";
}
else
{
MessageBox.Show("Failed to update", "FAIL !");
SearNametxt.Text = "";
}
db.con.Close();
}
Any clue?
I can see three problems with your query. 1 You declare ID, but don't assign it before using it, so it will always be NULL for the first query, so this will never update any rows:
DECLARE #ID int
UPDATE FrstTable SET
EmpName=#EmpName, Nationality=#Nationality, Passport=#Passport
WHERE ID = #ID
Secondly, you are using SCOPE_IDENTITY to attempt to get the ID of the record that has been updated. You can't do that, SCOPE_IDENTITY will return the last inserted ID, it is not affected by updates. You will need to use OUTPUT to get the Updated ID:
DECLARE #IDs TABLE (ID INT);
UPDATE FirstTable
OUTPUT inserted.ID INTO #Ids (ID)
SET EmpName = #EmpName,
Nationality = #Nationality,
Passport = #Passport;
Thirdly, your second update statement has no where clause, so will update the entire table:
UPDATE ScndTable
SET Emp_ID=#ID, ContractDate=#ContractDate, HealthDate=#HealthDate
WHERE EmpID IN (SELECT ID FROM #Ids);
Your stored procedure looks weird to me. I believe there should be a WHERE cluase for the second UPDATE otherwise it will always update the whole ScndTable table. set #ID = SCOPE_IDENTITY(); seems to be reduntant here. Are you trying to perform insert into ScndTable if there's no corresponding Emp_ID there? Finnaly explicitly create a transaction to update either both tables or none.
Hope it helps!
Please assign the value of #ID variable, before executing the first update statement.
I think you are trying to update some row, so you can pass the 'id' value from the CSHARP code. When you use the SCOPE_IDENTITY, you will get the last inserted value. Try to pass the ID value from the front end.
I have a Vendor object within my View Model. When I insert this vendor record into the database, I want to retrieve the ID of this Vendor because it will be used as a foreign key on another table immediately after (in a different sproc). I'm attempting to do this with test as the ID I need to retrieve. The following code doesn't work because proc_amcInsertApplicationServerRelationship expects an integer, but test is of type of Object Parameter.
I guess my questions are:
Am I approaching this correctly? If not, what would be a better approach? Also, based on my current approach, is there something simple I'm overlooking that I could do to get this to work? Here's my code (sorry if I'm not providing enough detail):
[HttpPost]
public ActionResult Create(ApplicationViewModel applicationViewModel)
{
try
{
// TODO: Add insert logic here
ObjectParameter test = new ObjectParameter("ID", typeof (int));
var vendorID = db.proc_amcInsertNewVendor(applicationViewModel.Vendor.Company, applicationViewModel.Vendor.StreetAddress, applicationViewModel.Vendor.SecondaryStreetAddress,
applicationViewModel.Vendor.City, applicationViewModel.Vendor.State, applicationViewModel.Vendor.ZipCode, applicationViewModel.Vendor.PhoneNumber,
applicationViewModel.Vendor.Website, test);
foreach (var serverID in applicationViewModel.ServerIDs)
{
db.proc_amcInsertApplicationServerRelationship(test, serverID);
}
return RedirectToAction("Index");
}
catch (Exception exception)
{
return View();
}
}
EDIT: Per Request, here's my stored procedure.
#Company varchar(100)
,#StreetAddress varchar(100)
,#SecondaryStreetAddress varchar(50)
,#City varchar(50)
,#State varchar(50)
,#ZipCode varchar(10)
,#PhoneNumber varchar(15)
,#Website varchar(200)
,#ID int = NULL OUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
INSERT INTO [dbo].[amc_Vendors]
(
[Company]
,[StreetAddress]
,[SecondaryStreetAddress]
,[City]
,[State]
,[ZipCode]
,[PhoneNumber]
,[Website]
)
VALUES
(
#Company
,#StreetAddress
,#SecondaryStreetAddress
,#City
,#State
,#ZipCode
,#PhoneNumber
,#Website
)
SET #Id = SCOPE_IDENTITY()
END
GO
In your SP after insert statement. Use
Select ##IDENTITY As ReturnedId
In your service code, do:
int Id=db.proc_amcInsertApplicationServerRelationship(serverId).FirstOrDefault().ReturnedId;
Use this Id for your further processing.
There is solution to this but you need to make sure for the followings:
-What you can try with your current code is return Identity of recently added Items with ##Identity in SQL procedure and get the same from command as return value or as output parameter.
Use the returned value and sent the same in next operation.
But as far as my expertise says this would be not good option. Since you are having dependencies for Query execution, You need to implement Transaction as well.
you can handle them at both database and Application level
Use Single SP to insert records in both table with Transaction or
Use Transaction class at Application level in C#
Hope these would be helpful
Do Following:
In Your SP:
CREATE PROCEDURE dbo.YourSPName
#Param1 varchar(156),
#Id Int OUTPUT
AS
SET NOCOUNT ON;
SELECT #Id = ##IDENTITY
In your C# code:
var outputParameter = new ObjectParameter("Id", typeof(int));
context.YourSPName("ParamValue", outputParameter);
Console.WriteLine(outputParameter.Value);
I have a table with autoincremented primary key. In my code I am trying to receive the new autoincremented value when I execute each 'insert' query. Is there a way to do it programatically?
Thanks.
UPD:
Assume I have a table:
TABLE User ( userID INT NOT NULL AUTO_INCREMENT, name VARCHAR( 25 ) NOT NULL , email VARCHAR( 50 ) NOT NULL , UNIQUE ( userID ) );
And I when I insert new values (name and email) to this table I want automatically receive newly generated userID. Ideally I am looking for any ways to do that with a single transaction and without stored procedures.
Have your sql/stored proc return scope_identity() or if you are using Linq2SQL or EF the entity used for insertion gets the new id.
In the stored proc it is:
ALTER proc [dbo].[SaveBuild](
#ID int = 0 output,
#Name varchar(150)=null,
#StageID int,
#Status char(1)=null
)
as
SET NOCOUNT ON
Insert into Builds
(name, StageID, status)
values (#Name, #StageID, #Status)
select #ID = scope_identity()
RETURN #ID
In the C# code you have:
public int SaveBuild(ref int id, ref string Name)
{
SqlCommand cmd = GetNewCmd("dbo.SaveBuild");
cmd.Parameters.Add("#ID", SqlDbType.Int).Value = id;
cmd.Parameters["#ID"].Direction = ParameterDirection.InputOutput;
cmd.Parameters.Add("#Name", SqlDbType.VarChar).Value = Name;
cmd.Parameters.Add("#StageID", SqlDbType.Int).Value = 0;
ExecuteNonQuery(cmd);
id = (int)cmd.Parameters["#ID"].Value;
return id;
}
Dependent upon your situation, you might be better off using table-valued parameters to pass your inserts to a stored procedure, then use OUTPUT INSERTED to return a table-valued parameter from your stored procedure.
It will drastically reduce the number of hits required if you're processing multiple items.
Are you limited to building SQL on the client and sending it to the server? Cause if you can use a stored procedure, this is easy to do. In the stored proc, do the insert and then, either
Select Scope_Identity() as the last statement in the stored proc., or
Use a output parameter to the stored proc, (say named #NewPKValue) and make the last statement:
Set #NewPKValue = Scope_Identity()
Otherwise, you need to send a batch of commands to the server that include two statements, the insert, and Select Scope_Identity() and execute the batch as though it was a select statement
You could use the SQL statement SELECT scope_identity().