07-31-2023, 03:29 AM
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:][1] <sup>[🕗][2]</sup>
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
[1]:
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:][1] <sup>[🕗][2]</sup>
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:
-
[To see links please register here]
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
[1]:
[To see links please register here]
[2]:[To see links please register here]