I am trying to complete a seemingly simple task that has turned into a several hour adventure: Getting ##Identity from TableAdapter.Insert().
Here's my code:
protected void submitBtn_Click(object sender, EventArgs e)
{
AssetsDataSetTableAdapters.SitesTableAdapter sta = new AssetsDataSetTableAdapters.SitesTableAdapter();
int insertedID = sta.Insert(siteTxt.Text,descTxt.Text);
AssetsDataSetTableAdapters.NotesTableAdapter nta = new AssetsDataSetTableAdapters.NotesTableAdapter();
nta.Insert(notesTxt.Text, insertedID, null,null,null,null,null,null);
Response.Redirect("~/Default.aspx");
}
One answer suggests all I may have to do is change the ExecuteMode. I tried that. This makes GetData() quit working (because I'm returning a scalar now instead of rowdata) (I need to keep GetData()). It also does not solve the issue in that the insertedID variable is still set to 1.
I tried creating a second TableAdapter in the TypedDataSet.XSD and setting the property for that adapter to "scalar", but it still fails with the variable getting a value of 1.
The generated insert command is
INSERT INTO [dbo].[Sites] ([Name], [Description]) VALUES (#Name, #Description);
SELECT Id, Name, Description FROM Sites WHERE (Id = SCOPE_IDENTITY())
And the "Refresh the Data Table" (adds a select statement after Insert and Update statements to retrieve Identity" is also set.
Environment
SQL Server 2008 R2, Visual Studio 2010, .NET 4, Windows XP, all local same machine.
What's causing this?
EDIT/UPDATE
I want to clarify that I am using auto-generated code within Visual Studio. I don't know what the "tool" that generated the code is, but if you double click the *.XSD file it displays a UI of the SQL Table Schema's and associated TableAdapter's. I want to keep using the auto-generated code and somehow enable getting the Identity. I don't want to write this all by hand with stored procedures.
The real answer:
read the notes below!
Get identity from tableadapter insert function
I keep getting questions about this issue very often and never found
time to write it down.
Well, problem is following: you have table with primary key with int
type defined as Identity and after insert you need to know PK value of
newly inserted row. Steps for accomplishing to do this are following:
use wizard to add new insert query (let's call it InsertQuery) in the
body of query just add SELECT SCOPE_IDENTITY() at the bottom after
saving this query, change ExecuteMode property of this query from
NonQuery to Scalar in your code write following (ta is TableAdapter
instance) :
int id;
try
{
id = Convert.toInt32(ta.InsertQuery(firstName, lastName, description));
}
catch (SQLException ex)
{
//...
}
finally
{
//...
}
Make money with this! :) Posted 12th March 2009 by Draško Sarić
From:
http://quickdeveloperstips.blogspot.nl/2009/03/get-identity-from-tableadapter-insert.html
Notes:
Setting the ExecutMode to Scalar is possible via the Properties of your generated Insert Query. (press F4).
In my version (Visual Studio 2010 SP1 ) the select statement was generated automatically.
Here's my SQL Code that works.
CREATE PROCEDURE [dbo].[Branch_Insert]
(
#UserId uniqueidentifier,
#OrganisationId int,
#InsertedID int OUTPUT
)
AS
SET NOCOUNT OFF;
INSERT INTO [Branch] ([UserId], [OrganisationId])
VALUES (#UserId, #OrganisationId);
SELECT Id, UserId, OrganisationId FROM Branch WHERE (Id = SCOPE_IDENTITY())
SELECT #InsertedID = SCOPE_IDENTITY()
Then when I create the Table Adapter - I can instantly see the #InsertedID parameter.
Then from code, all I do is:
int? insertedId = 0;
branchTA.Insert(userId, orgId, ref insertedId);
I'm not 100% whether using ref is the best option but this works for me.
Good luck.
All of the info is here, but I didn't find any single answer complete, so here is the complete steps I use.
Add an insert query, and append SELECT SCOPE_IDENTITY() to it, like so:
INSERT INTO foo(bar) VALUES(#bar);
SELECT SCOPE_IDENTITY()
Make sure you add a ; to the end of the INSERT statement that VS creates for you.
After you Finish the add query wizard, make sure the query is selected in the design view then change Execute Mode to Scalar from the properties pane.
Make sure you use Convert.ToInt32() when you call the query from your code, like so:
id = Convert.ToInt32( dataTableAdapter.myInsertQuery("bar") )
You will not get compiler errors without the Convert.ToInt32, but you will get the wrong return value.
Also, any time you modify the query, you have to reset the Execute Mode back to Scalar, because VS will change it back to Non Query every time.
Here's how you do it (in the visual Designer)
Right Click the Table Adapter and "Add Query"
SQL Statements - Choose Update (best auto-gen parameters)
Copy and paste your SQL, it can be multi-line, just make sure that the "Query Designer" doesn't open up, as it will not be able to interpret the multiple commands - my example shows a sample "merge" set of statements (note that new SERVERS have Merge commands).
UPDATE YOURTABLE
SET YourTable_Column1 = #YourTable_Column1, YourTable_Column2 = #YourTableColumn2
WHERE (YourTable_ID = #YourTable_ID)
IF ##ROWCOUNT=0
INSERT INTO YOURTABLE ([YourTable_Column1], [YourTable_Column2])
VALUES (#YourTable_Column1, #YourTable_Column2)
#YourTable_ID = SCOPE_IDENTITY()
Change/Add the #YourTable_ID Parameters from the query properties window/sidebar. In the Parameter Collection Editor, The ID parameter needs to be have a Direction of InputOutput, so that the value gets updated when the Table Adapter function is called. (Special note: Make sure that whatever column you make InputOutput that the designer doesn't have this column as "Read Only" and that the data types match up as well, otherwise change the column in the datatable, or the parameter information accordingly)
This should save the need of writing a Stored procedure for such a simple activity.
Much Wow. You will notice that this method is a fast way of doing Data Layer functions without having to break into the SQL procedures and write up a ton of Procedures. The only problem, there is a lot of dancing you have to do...
You'll need to setup the insert to return the identity as an output value and then grab it as a parameter in your adapter.
These two links should get you going:
http://www.akadia.com/services/dotnet_autoincrement.html
http://msdn.microsoft.com/en-us/library/ks9f57t0.aspx
You have exactly two choices:
change your SQL code manually
use whatever Visual Studio generates
I'd use the following SQL and ExecuteScalar.
INSERT INTO [dbo].[Sites] ([Name], [Description])
OUTPUT INSERTED.ID
VALUES (#Name, #Description);
One way is to run a select query after the insert command. A good way is to wrap the original command like this:
public int WrapInsert(Parameters)
{
.....
int RowsAffected = this.Insert(..Parameters..);
if ( RowsAffected > 0)
{
try
{
SqlCommand cm = this.Connection.CreateCommand();
cm.CommandText = "SELECT ##IDENTITY";
identity = Convert.ToInt32(cm.ExecuteScalar());
}
finally
{
....
}
}
return RowsAffected;
}
Related
How am I supposed to get the IDENTITY of an inserted row?
I know about ##IDENTITY and IDENT_CURRENT and SCOPE_IDENTITY, but don't understand the implications or impacts attached to each.
Can someone please explain the differences and when I would be using each?
##IDENTITY returns the last identity value generated for any table in the current session, across all scopes. You need to be careful here, since it's across scopes. You could get a value from a trigger, instead of your current statement.
SCOPE_IDENTITY() returns the last identity value generated for any table in the current session and the current scope. Generally what you want to use.
IDENT_CURRENT('tableName') returns the last identity value generated for a specific table in any session and any scope. This lets you specify which table you want the value from, in case the two above aren't quite what you need (very rare). Also, as #Guy Starbuck mentioned, "You could use this if you want to get the current IDENTITY value for a table that you have not inserted a record into."
The OUTPUT clause of the INSERT statement will let you access every row that was inserted via that statement. Since it's scoped to the specific statement, it's more straightforward than the other functions above. However, it's a little more verbose (you'll need to insert into a table variable/temp table and then query that) and it gives results even in an error scenario where the statement is rolled back. That said, if your query uses a parallel execution plan, this is the only guaranteed method for getting the identity (short of turning off parallelism). However, it is executed before triggers and cannot be used to return trigger-generated values.
I believe the safest and most accurate method of retrieving the inserted id would be using the output clause.
for example (taken from the following MSDN article)
USE AdventureWorks2008R2;
GO
DECLARE #MyTableVar table( NewScrapReasonID smallint,
Name varchar(50),
ModifiedDate datetime);
INSERT Production.ScrapReason
OUTPUT INSERTED.ScrapReasonID, INSERTED.Name, INSERTED.ModifiedDate
INTO #MyTableVar
VALUES (N'Operator error', GETDATE());
--Display the result set of the table variable.
SELECT NewScrapReasonID, Name, ModifiedDate FROM #MyTableVar;
--Display the result set of the table.
SELECT ScrapReasonID, Name, ModifiedDate
FROM Production.ScrapReason;
GO
I'm saying the same thing as the other guys, so everyone's correct, I'm just trying to make it more clear.
##IDENTITY returns the id of the last thing that was inserted by your client's connection to the database.
Most of the time this works fine, but sometimes a trigger will go and insert a new row that you don't know about, and you'll get the ID from this new row, instead of the one you want
SCOPE_IDENTITY() solves this problem. It returns the id of the last thing that you inserted in the SQL code you sent to the database. If triggers go and create extra rows, they won't cause the wrong value to get returned. Hooray
IDENT_CURRENT returns the last ID that was inserted by anyone. If some other app happens to insert another row at an unforunate time, you'll get the ID of that row instead of your one.
If you want to play it safe, always use SCOPE_IDENTITY(). If you stick with ##IDENTITY and someone decides to add a trigger later on, all your code will break.
The best (read: safest) way to get the identity of a newly-inserted row is by using the output clause:
create table TableWithIdentity
( IdentityColumnName int identity(1, 1) not null primary key,
... )
-- type of this table's column must match the type of the
-- identity column of the table you'll be inserting into
declare #IdentityOutput table ( ID int )
insert TableWithIdentity
( ... )
output inserted.IdentityColumnName into #IdentityOutput
values
( ... )
select #IdentityValue = (select ID from #IdentityOutput)
Add
SELECT CAST(scope_identity() AS int);
to the end of your insert sql statement, then
NewId = command.ExecuteScalar()
will retrieve it.
From MSDN
##IDENTITY, SCOPE_IDENTITY, and IDENT_CURRENT are similar functions in that they return the last value inserted into the IDENTITY column of a table.
##IDENTITY and SCOPE_IDENTITY will return the last identity value generated in any table in the current session. However, SCOPE_IDENTITY returns the value only within the current scope; ##IDENTITY is not limited to a specific scope.
IDENT_CURRENT is not limited by scope and session; it is limited to a specified table. IDENT_CURRENT returns the identity value generated for a specific table in any session and any scope. For more information, see IDENT_CURRENT.
IDENT_CURRENT is a function which takes a table as a argument.
##IDENTITY may return confusing result when you have an trigger on the table
SCOPE_IDENTITY is your hero most of the time.
When you use Entity Framework, it internally uses the OUTPUT technique to return the newly inserted ID value
DECLARE #generated_keys table([Id] uniqueidentifier)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID INTO #generated_keys
VALUES('Malleable logarithmic casing');
SELECT t.[TurboEncabulatorID ]
FROM #generated_keys AS g
JOIN dbo.TurboEncabulators AS t
ON g.Id = t.TurboEncabulatorID
WHERE ##ROWCOUNT > 0
The output results are stored in a temporary table variable, joined back to the table, and return the row value out of the table.
Note: I have no idea why EF would inner join the ephemeral table back to the real table (under what circumstances would the two not match).
But that's what EF does.
This technique (OUTPUT) is only available on SQL Server 2008 or newer.
Edit - The reason for the join
The reason that Entity Framework joins back to the original table, rather than simply use the OUTPUT values is because EF also uses this technique to get the rowversion of a newly inserted row.
You can use optimistic concurrency in your entity framework models by using the Timestamp attribute: 🕗
public class TurboEncabulator
{
public String StatorSlots)
[Timestamp]
public byte[] RowVersion { get; set; }
}
When you do this, Entity Framework will need the rowversion of the newly inserted row:
DECLARE #generated_keys table([Id] uniqueidentifier)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID INTO #generated_keys
VALUES('Malleable logarithmic casing');
SELECT t.[TurboEncabulatorID], t.[RowVersion]
FROM #generated_keys AS g
JOIN dbo.TurboEncabulators AS t
ON g.Id = t.TurboEncabulatorID
WHERE ##ROWCOUNT > 0
And in order to retrieve this Timetsamp you cannot use an OUTPUT clause.
That's because if there's a trigger on the table, any Timestamp you OUTPUT will be wrong:
Initial insert. Timestamp: 1
OUTPUT clause outputs timestamp: 1
trigger modifies row. Timestamp: 2
The returned timestamp will never be correct if you have a trigger on the table. So you must use a separate SELECT.
And even if you were willing to suffer the incorrect rowversion, the other reason to perform a separate SELECT is that you cannot OUTPUT a rowversion into a table variable:
DECLARE #generated_keys table([Id] uniqueidentifier, [Rowversion] timestamp)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID, inserted.Rowversion INTO #generated_keys
VALUES('Malleable logarithmic casing');
The third reason to do it is for symmetry. When performing an UPDATE on a table with a trigger, you cannot use an OUTPUT clause. Trying do UPDATE with an OUTPUT is not supported, and will give an error:
Cannot use UPDATE with OUTPUT clause when a trigger is on the table
The only way to do it is with a follow-up SELECT statement:
UPDATE TurboEncabulators
SET StatorSlots = 'Lotus-O deltoid type'
WHERE ((TurboEncabulatorID = 1) AND (RowVersion = 792))
SELECT RowVersion
FROM TurboEncabulators
WHERE ##ROWCOUNT > 0 AND TurboEncabulatorID = 1
I can't speak to other versions of SQL Server, but in 2012, outputting directly works just fine. You don't need to bother with a temporary table.
INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES (...)
By the way, this technique also works when inserting multiple rows.
INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES
(...),
(...),
(...)
Output
ID
2
3
4
##IDENTITY is the last identity inserted using the current SQL Connection. This is a good value to return from an insert stored procedure, where you just need the identity inserted for your new record, and don't care if more rows were added afterward.
SCOPE_IDENTITY is the last identity inserted using the current SQL Connection, and in the current scope -- that is, if there was a second IDENTITY inserted based on a trigger after your insert, it would not be reflected in SCOPE_IDENTITY, only the insert you performed. Frankly, I have never had a reason to use this.
IDENT_CURRENT(tablename) is the last identity inserted regardless of connection or scope. You could use this if you want to get the current IDENTITY value for a table that you have not inserted a record into.
ALWAYS use scope_identity(), there's NEVER a need for anything else.
One other way to guarantee the identity of the rows you insert is to specify the identity values and use the SET IDENTITY_INSERT ON and then OFF. This guarantees you know exactly what the identity values are! As long as the values are not in use then you can insert these values into the identity column.
CREATE TABLE #foo
(
fooid INT IDENTITY NOT NULL,
fooname VARCHAR(20)
)
SELECT ##Identity AS [##Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
SET IDENTITY_INSERT #foo ON
INSERT INTO #foo
(fooid,
fooname)
VALUES (1,
'one'),
(2,
'Two')
SET IDENTITY_INSERT #foo OFF
SELECT ##Identity AS [##Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
INSERT INTO #foo
(fooname)
VALUES ('Three')
SELECT ##Identity AS [##Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
-- YOU CAN INSERT
SET IDENTITY_INSERT #foo ON
INSERT INTO #foo
(fooid,
fooname)
VALUES (10,
'Ten'),
(11,
'Eleven')
SET IDENTITY_INSERT #foo OFF
SELECT ##Identity AS [##Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
SELECT *
FROM #foo
This can be a very useful technique if you are loading data from another source or merging data from two databases etc.
Create a uuid and also insert it to a column. Then you can easily identify your row with the uuid. Thats the only 100% working solution you can implement. All the other solutions are too complicated or are not working in same edge cases.
E.g.:
1) Create row
INSERT INTO table (uuid, name, street, zip)
VALUES ('2f802845-447b-4caa-8783-2086a0a8d437', 'Peter', 'Mainstreet 7', '88888');
2) Get created row
SELECT * FROM table WHERE uuid='2f802845-447b-4caa-8783-2086a0a8d437';
Even though this is an older thread, there is a newer way to do this which avoids some of the pitfalls of the IDENTITY column in older versions of SQL Server, like gaps in the identity values after server reboots. Sequences are available in SQL Server 2016 and forward which is the newer way is to create a SEQUENCE object using TSQL. This allows you create your own numeric sequence object in SQL Server and control how it increments.
Here is an example:
CREATE SEQUENCE CountBy1
START WITH 1
INCREMENT BY 1 ;
GO
Then in TSQL you would do the following to get the next sequence ID:
SELECT NEXT VALUE FOR CountBy1 AS SequenceID
GO
Here are the links to CREATE SEQUENCE and NEXT VALUE FOR
Complete solution in SQL and ADO.NET
const string sql = "INSERT INTO [Table1] (...) OUTPUT INSERTED.Id VALUES (...)";
using var command = connection.CreateCommand();
command.CommandText = sql;
var outputIdParameter = new SqlParameter("#Id", SqlDbType.Int) { Direction = ParameterDirection.Output };
command.Parameters.Add(outputIdParameter);
await connection.OpenAsync();
var outputId= await command.ExecuteScalarAsync();
await connection.CloseAsync();
int id = Convert.ToInt32(outputId);
After Your Insert Statement you need to add this. And Make sure about the table name where data is inserting.You will get current row no where row affected just now by your insert statement.
IDENT_CURRENT('tableName')
I am attempting to get the OUTPUT table from an INSERT command on a SQL Server database (v14).
DECLARE #i TABLE (TICKET_ID int, CREATED_DATE datetime);
INSERT INTO dbo.TICKET (SITE_ID, TICKET_TITLE, USER_ID)
OUTPUT INSERTED.TICKET_ID, INSERTED.CREATED_DATE INTO #i
VALUES (#s, #t, #q);
SELECT * FROM #i;
I have 3 parameters that are being populated for the site, ticket title and user by #s, #t and #u respectively.
I am calling the command using a SqlClient.SqlDataReader Object with ExecuteReader Function on the 4.5 Framework.
The command successfully inserts the record into the database table, but does not return the table #i with the ticket ID (Auto Increment) and created date (default =GETDATE) which is what I am expected to present to the user once saved to the database.
I have played around with the removing of the #i TABLE in the command, but I get a .NET error:
The target table 'name' of the DML statement cannot have any enabled triggers if the statement contains an OUTPUT clause without INTO clause
I have found a couple of links that provide some ways to fix these but it did not fix my issue
https://learn.microsoft.com/en-us/previous-versions/dotnet/articles/ms971497(v=msdn.10)?redirectedfrom=MSDN
Cannot use UPDATE with OUTPUT clause when a trigger is on the table
Is there any assistance that can be provided to assist in what I am trying to achieve?
It seems a pain to have to but another option might be just to do this:
INSERT INTO dbo.TICKET (SITE_ID, TICKET_TITLE, USER_ID)
VALUES (#s, #t, #q);
SELECT TICKET_ID, CREATED_DATE
FROM dbo.TICKET
WHERE SITE_ID = #s AND TICKET_TITLE = #t AND USER_ID = #q;
I don't know why you'd need the table. I think you need to just execute this:
INSERT INTO dbo.TICKET (SITE_ID, TICKET_TITLE, USER_ID)
OUTPUT inserted.TICKET_ID, inserted.CREATED_DATE
VALUES (#s, #t, #q);
I can't see any value in the table. When you execute this statement, it'll return results to the client, just like a SELECT statement would.
Hope that helps.
I'm trying to properly bind a Data Adapter to my SQL database to insert records submitted by a person in a c# program. I feel like I am 80% of the way, but now i've hit a hitch.
So as in the code below, I can create and bind a Data Table just fine. I've tested the delete functions and they work just fine. I am now attempting to have a 'save' button insert a new row to my database. The problem I have now is that a user is supposed to put in their 'notes' and then hit save. I auto populate the rest of the columns, but I do not know how to grab the notes that the user entered.
Here is my code so far:
string userVerify = User.CurrentUser.UserName.ToString();
int participantID = this.mParticipant.ParticipantID;
DateTime date = DateTime.Now;
string properRow = dtNotes[1, dtNotes.NewRowIndex - 1].Value.ToString();
sqlDataAdapter.InsertCommand = new SqlCommand("INSERT INTO xxMyDatabasexx (ParticipantID,Verifier,Notes,Date) VALUES (#participantID,#notes, #userVerify,#date);");
sqlDataAdapter.InsertCommand.Parameters.AddWithValue("#participantID", participantID);
sqlDataAdapter.InsertCommand.Parameters.AddWithValue("#userVerify", userVerify);
sqlDataAdapter.InsertCommand.Parameters.AddWithValue("#date", date);
sqlDataAdapter.InsertCommand.Parameters.AddWithValue("#notes", properRow);
sqlDataAdapter.Fill(dataTable);
sqlDataAdapter.Update(dataTable);
I am aware that the properRow variable's logic is wrong. Of course if there are no rows then the program will crash, but also if no new note has been entered it will just reproduce the last note entered which of course is wrong as well. When i look into my dataTable at the time of sqlDataAdapter.Fill, I can see the note in the correct column but I don't know how to simply save it.
Any help is appreciated. Thanks.
EDIT:
What I was unaware of is that the InsertCommand (naturally) also needs the ExecuteNonQuery command with it. I was under the assumption that since both Delete and Update did not, that Insert wouldn't either. This seemed to fix the issue. Thanks all for the help.
You can insert the record into SQL Database without need for DataAdapter just by using Command object as shown in the following code snippet (just pass your Insert SQL statement string):
void SqlExecuteCommand(string InsertSQL)
{
try
{
using (SqlConnection _connSqlCe = new SqlConnection("Conn String to SQL DB"))
{
_connSql.Open();
using (SqlCommand _commandSqlCe = new SqlCommand(CommandSQL, _connSql))
{
_commandSql.CommandType = CommandType.Text;
_commandSql.ExecuteNonQuery();
}
}
}
catch { throw; }
}
The general format of SQL INSERT query string is shown below:
INSERT INTO YourTable (column1,column2,column3,...)
VALUES (value1,value2,value3,...);
You can further extend this solution by adding parameters to the SQL String/Command in order to protect against possibility of SQL injection (see the following example):
INSERT INTO YourTable (column1,column2,column3,...)
VALUES (#param1,#param2,#param3,...);
_commandSql.Parameters.Add("#param1","abc");
_commandSql.Parameters.Add("#param2","def");
_commandSql.Parameters.Add("#param3","ghijklm");
You can also use the alternative syntax for SQL Parameters, like for e.g.:
_commandSql.Parameters.Add("#param1", SqlDbType.NChar).Value = "abc";
_commandSql.Parameters.Add("#param2", SqlDbType.NChar).Value = "def";
_commandSql.Parameters.Add("#param3", SqlDbType.NVarChar).Value = "ghijklm";
Pertinent to your particular question, it should be like:
"INSERT INTO xxMyDatabasexx (ParticipantID, Verifier, Notes, [Date]) VALUES (#participantID, #userVerify, #notes, #dt)"
_commandSql.Parameters.Add("#ParticipantID",SqlDbType.NChar).Value= participantID;
_commandSql.Parameters.Add("#userVerify",SqlDbType.NChar).Value= userVerify ;
_commandSql.Parameters.Add("#notes",SqlDbType.NVChar).Value= properRow ;
_commandSql.Parameters.Add("#dt",SqlDbType.DateTime).Value= DateTime.Now;
Note: in case ParticipantID is the IDENTITY (i.e. Autonumber) Column, then do not include it in INSERT statement.
Hope this may help.
It seems to me that You are a bit lost. The way adapters are meant to work is
fill table from database via adapter (or take empty table)
bind table to GUI or manually transfer the information to GUI
change/add/delete data in table via binding or manually
update changes (inserts/updates/deletes) into database via adapter
The changes in table are automatically traced, so the adapter knows, what should be updated/inserted/deleted and use appropriate commands.
If You use adapter just as a holder for command You can ExecuteNonQuery with arbitrary parameters, You pass the whole concept and do not need adapter at all (see #AlexBells answer).
Apart from this, are You really going to write all that plumbing code by hand? Life is too short. If I were You, I would look for some ORM. You get simple CRUDs or concurrency checking with no effort.
I have the following databasescheme in SQL Server Manager 2014.
I'm making a C#-windows application in Visual Studio and I want to insert a new orderline and a new order. The problem is that the primary keys of both tables, auto-generate in server manager, so I haven't yet the value of the primary key of the order-table, but I need that value to fill into the foreign key of the orderLine column. How can I insert these two rows.
Kind regards
SCOPE_IDENTITY returns the last identity value inserted into an identity column in the same scope. A scope is a module: a stored procedure, trigger, function, or batch. Therefore, two statements are in the same scope if they are in the same stored procedure, function, or batch.
You can use SqlCommand.ExecuteScalar to execute the insert command and retrieve the new ID in one query.
using (var con = new SqlConnection(ConnectionString)) {
int newOrderID;
var cmd = "INSERT INTO Order (column_name) VALUES (#Value) ;SELECT CAST(scope_identity() AS int)";
using (var insertCommand = new SqlCommand(cmd, con)) {
insertCommand.Parameters.AddWithValue("#Value", "bar");
con.Open();
newOrderID = (int)insertCommand.ExecuteScalar();
}
}
This will allow you to catch the last generated OrderId and use it in the Insert Statement for the OrderLine table.
Another option is to use the following SQL code:
string command = "INSERT INTO Order(totalPrice) OUTPUT INSERTED.ID VALUES(#totalPrice)" // this will be a parameter from your code
Then the OrderId can be taken from :
Int32 orderId = (Int32) command.ExecuteScalar();
While scope_id() works fine for single rows, you really should learn to use the output clause as scope_id() is useless for multiple rows inserted with a single sql statement.
See this prior question for a simple example of using the output clause.
Obviously this allows you to retrieve more than just the identity value too.
ADDED
Also useful is the new sequence feature (added for 2012) instead of using identity. If your are coming from other databases this may seem a more natural solution.
Sequence is very useful if you would like to share a single sequence among several tables -- although this is an uncommon design I have used it a few times.
If you're using any form of direct SQL, you need to receive the SCOPE_IDENTITY() value immediately after inserting your order, then use that value to insert your lines.
INSERT INTO Order
SELECT SCOPE_IDENTITY() AS NewId; OR RETURN SCOPE_IDENTITY(); OR DECLARE #OrderId INT = SCOPE_IDENTITY();
INSERT INTO OrderLine
Otherwise, use Entity Framework and it will automatically retrieve your new IDs and assign to dependencies.
I have a stored procedure that contains dynamic select. Something like this:
ALTER PROCEDURE [dbo].[usp_GetTestRecords]
--#p1 int = 0,
--#p2 int = 0
#groupId nvarchar(10) = 0
AS
BEGIN
SET NOCOUNT ON;
DECLARE #query NVARCHAR(max)
SET #query = 'SELECT * FROM CUSTOMERS WHERE Id = ' + #groupId
/* This actually contains a dynamic pivot select statement */
EXECUTE(#query);
END
In SSMS the stored procedure runs fine and shows result set.
In C# using Entity Framework it shows returning an int instead of IEnumerable?
private void LoadTestRecords()
{
TestRecordsDBEntities dataContext = new TestRecordsDBEntities();
string id = ddlGroupId.SelectedValue;
List<TestRecord> list = dataContext.usp_GetTestRecords(id); //This part doesn't work returns int
GridView1.DataSource = list;
}
Generated function for usp_GetTestRecords
public virtual int usp_GetTestRecords(string groupId)
{
var groupIdParameter = groupId != null ?
new ObjectParameter("groupId", groupId) :
new ObjectParameter("groupId", typeof(string));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction("usp_GetTestRecords", groupIdParameter);
}
I get this when I have a stored procedure that includes an "exec" call into a temporary table, such as:
insert into #codes (Code, ActionCodes, Description)
exec TreatmentCodes_sps 0
It appears that Entity Framework gets confused as to what should be returned by the procedure. The solution I've come across is to add this at the top of the sproc:
SET FMTONLY OFF
After this, all is well.
I got the same problem, and found solution here
Move to your .edmx
At Model Browser Window/Function Imports find your procedure then double click it
Change the return type to you want
Save .edmx and check the return type again.
It should be what you need now.
Entity Framework can't tell what your stored procedure is returning. I've had success creating a table variable that mirrors the data from your SELECT statement. Just insert into the table variable then do a select from that table variable. EF should pick it up.
See Ladislav Mrnka's answer in this Stack Overflow post
https://stackoverflow.com/a/7131344/4318324
I had the same basic problem.
Adding
SET FMTONLY OFF
To a procedure you are trying to import during the import will address this problem.
It's a good practice to remove the line afterwards unless the purpose of the database is solely to provide schema for EF (Entity Framework).
The main reason for caution is that EF uses this setting to prevent data mutations when trying to obtain metadata.
If you refresh your entity model from a database any procedures with this line in them can potentially update the data in that database just by trying to obtain the schema.
I wanted to add a further note on this so it's not needed to fully scan through the other link.
if you want to try to use FMTONLY here are a couple things to keep in mind.
when FMTONLY is on:
1) only the schema is returned (no) rows.
similar to adding a blanket false statement to your where clause (ie "where 1=0")
2) flow control statements are ignored
Example
set fmtonly on
if 1=1
begin
select 1 a
end
else
begin
select 1 a,2 b
end
while 1=1
select 1 c
The above returns NO rows whatsoever and the metadata for each of the three queries
For this reason some people suggest toggling it off in a way that takes advantage of it's non-observance of flow control
if 1=0
begin
set fmtonly off
end
In fact you could use this to introduce logic that tracks this
set fmtonly off
declare #g varchar(30)
set #g = 'fmtonly was set to off'
if 1=0
begin
set fmtonly off
set #g = 'fmtonly was set to on'
end
select #g
Think VERY CAREFULLY before trying to use this feature as it is both deprecated and potentially makes sql extremely hard to follow
the MAIN concepts that need to be understood are the following
1. EF turns FMTONLY on to prevent MUTATING data from executing stored procedures
when it executes them during a model update.
(from which it follows)
2. setting FMTONLY off in any procedure that EF will attempt to do a schema scan
(potentially ANY and EACHONE) introduces the potential to mutate database
data whenever *anyone* attempts to update their database model.
Entity Framework will automatically return a scalar value if your stored procedure doesn't have a primary key in your result set. Thus, you'd have to include a primary key column in your select statement, or create a temp table with a primary key in order for Entity Framework to return a result set for your stored procedure.
I had the same problem, I changed the name of return fields by 'AS' keyword and addressed my problem. One reason for this problem is naming column names with SQL Server reserved keywords.
The example is fallows:
ALTER PROCEDURE [dbo].[usp_GetProducts]
AS
BEGIN
SET NOCOUNT ON;
SELECT
, p.Id
, p.Title
, p.Description AS 'Description'
FROM dbo.Products AS p
END
Best solution I found is to cheat a little.
In the store procedure, comment everything, put a first line with a select [foo]='', [bar]='' etc...
Now update the model, go to the mapped function, select complex type and click on Get Column Information and then Create Complex Type.
Now comment the fake select and un-comment the real store procedure body.
When you generated your model class for your stored procedure, you chose scalar return result by mistake. you should remove your stored procedure from your entity model, then re-add the stored procedure. In the dialog for the stored procedure, you can choose the return type you are expecting. Do not just edit the generated code.. this may work now, but the generated code can be replaced if you make other changes to your model.
I have pondered this a bit and I think I have a better/simpler answer
If you have a complex stored that gives entity framework some difficultly (for current versions of Entity Framework that are using the FMTONLY tag to aquire schema)
consider doing the folowing at the beginning of your stored procedure.
--where [columnlist] matches the schema you want EF to pick up for your stored procedure
if 1=0
begin
select
[columnlist]
from [table list and joins]
where 1=0
end
if you are okay loading your result set into a table variable
you can do the following to help keep your schema in sync
declare #tablevar as table
(
blah int
,moreblah varchar(20)
)
if 1=0
begin
select * from #tablevar
end
...
-- load data into #tablevar
select * from #tablevar
If you need to do this, then you might be better off just making a partial of the dbcontext and creating the C# function yourself that will use SqlQuery to return the data you need. Advantages over some of the other options is:
Don't have to change anything when the model updates
Won't get overwritten if you do it directly in the generated class (someone above mention this as if it's an option :) )
Don't have to add anything to the proc itself that could have side effects now or later on
Example Code:
public partial class myEntities
{
public List<MyClass> usp_GetTestRecords(int _p1, int _p2, string _groupId)
{
// fill out params
SqlParameter p1 = new SqlParameter("#p1", _p1);
...
obj[] parameters = new object[] { p1, p2, groupId };
// call the proc
return this.Database.SqlQuery<MyClass>(#"EXECUTE usp_GetTestRecords #p1, #p2, #groupId", parameters).ToList();
}
}
Just change to
ALTER PROCEDURE [dbo].[usp_GetTestRecords]
--#p1 int = 0,
--#p2 int = 0
#groupId nvarchar(10) = 0
AS
BEGIN
SET NOCOUNT ON;
SELECT * FROM CUSTOMERS WHERE Id = #groupId
END
I know this is an old thread but in case someone has the same problems I'll tell my woes.
As a help to find the issue, run sql profiler when you add your stored proc. Then you can see what entity framework is passing as parameters to generate your resultset. I imagine nearly always it will pass null parameter values. If you are generating sql on the fly by concatenating string values and parameter values and some are null then the sql will break and you wont get a return set.
I haven't needed to generate temp tables or anything just an exec command.
Hope it helps
During import
SET FMTONLY ON
can be used for taking the sp schema.
If you change the sp and want to update the new one, you should delete the old defined function from edmx file (from xml), because although deleting sp from model browser, it is not deleted in edmx. For example;
<FunctionImport Name="GetInvoiceByNumber" ReturnType="Collection(Model.Invoice_Result)">
<Parameter Name="InvoiceNumber" Mode="In" Type="Int32" />
</FunctionImport>
I had the same problem, and when I delete the FuctionImport tag of corresponding sp totally, the model updated right. You can find the tag by searching the function name from visual studio.
You may have luck opening up the model browser, then going to Function Imports, double clicking the stored procedure in question and then manually clicking "Get Column Information" and then clicking "Create New Complex Type". This usually sorts out the problem.
Well I had this issue as well but after hours of online searching none of above methods helped.
Finally I got to know that It will happen if your store procedure is getting some parameters as null and which generate any error in query execution.
Entity Framework will generate method for store procedure by defining the complex entity model. Due to that null value your store procedure will return and int value.
Please check your store procedure either its providing empty result set with null values. It will fix your problem. Hopefully.
I think this is a problem of permissions on the database, I don't know what exactly could be, but, in my job we use Active Directory users to grant applications connect to databases, this accounts are specially created for the applications, each app has its own user account, well, as a developers I have permissions for read, write and other basic things, no alter, and no advanced features, and I have this same problem running Visual Studio with my normal account, then, what I did was to open Visual Studio selecting the option "as a different user" on the context menu, and I put the AD login granted for the application and voila!, now my Stored Procedures are loading with all the fields I was expected, before that, my Stored Procedures was returning as int. I hope this help someone, maybe the VIEW DEFINITION permissions on database account do the trick
If SQL Authentication is in place, verify that the user credential that is being used to connect Entity Framework to the database has the proper rights to read from CUSTOMERS table.
When Entity Framework uses SQL Authentication to map complex objects (i.e stored procedures that SELECTs more than one column), if any of the tables from within such stored procedure don't have set up the Read permission, the mapping will result in returning INT instead of the desired Result set.