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:
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
INSERT INTO Table1(fields...) OUTPUT INSERTED.id VALUES (...)
, or older method:INSERT INTO Table1(fields...) VALUES (...); SELECT SCOPE_IDENTITY();
you can get it in c# using ExecuteScalar(). – S.SerpooshanOUTPUT
clause in SQL Server. – Oded