Для нескольких таблиц, имеющих поля идентичности, мы реализуем схему защиты на уровне строк, используя представления и триггеры вместо этих представлений. Вот упрощенная примерная структура:
-- Table
CREATE TABLE tblItem (
ItemId int identity(1,1) primary key,
Name varchar(20)
)
go
-- View
CREATE VIEW vwItem
AS
SELECT *
FROM tblItem
-- RLS Filtering Condition
go
-- Instead Of Insert Trigger
CREATE TRIGGER IO_vwItem_Insert ON vwItem
INSTEAD OF INSERT
AS BEGIN
-- RLS Security Checks on inserted Table
-- Insert Records Into Table
INSERT INTO tblItem (Name)
SELECT Name
FROM inserted;
END
go
Если я хочу вставить запись и получить ее идентификатор, перед реализацией триггера RLS вместо я использовал:
DECLARE @ItemId int;
INSERT INTO tblItem (Name)
VALUES ('MyName');
SELECT @ItemId = SCOPE_IDENTITY();
С триггером SCOPE_IDENTITY () больше не работает - он возвращает NULL. Я видел предложения по использованию предложения OUTPUT для восстановления идентичности, но я не могу заставить его работать так, как мне нужно. Если я добавлю предложение OUTPUT к представлению вставки, в него ничего не будет введено.
-- Nothing is added to @ItemIds
DECLARE @ItemIds TABLE (ItemId int);
INSERT INTO vwItem (Name)
OUTPUT INSERTED.ItemId INTO @ItemIds
VALUES ('MyName');
Если я добавлю предложение OUTPUT в триггер оператора INSERT, триггер вернет таблицу (я могу просмотреть ее в SQL Management Studio). Я не могу захватить это в вызывающем коде; либо с помощью предложения OUTPUT для этого вызова, либо с помощью SELECT * FROM ().
-- Modified Instead Of Insert Trigger w/ Output
CREATE TRIGGER IO_vwItem_Insert ON vwItem
INSTEAD OF INSERT
AS BEGIN
-- RLS Security Checks on inserted Table
-- Insert Records Into Table
INSERT INTO tblItem (Name)
OUTPUT INSERTED.ItemId
SELECT Name
FROM inserted;
END
go
-- Calling Code
INSERT INTO vwItem (Name)
VALUES ('MyName');
Единственное, о чем я могу думать, это использовать функцию IDENT_CURRENT (). Так как это не работает в текущей области, существует проблема одновременного вставления пользователей и их путаницы. Если вся операция заключена в транзакцию, это предотвратит проблему параллелизма?
BEGIN TRANSACTION
DECLARE @ItemId int;
INSERT INTO tblItem (Name)
VALUES ('MyName');
SELECT @ItemId = IDENT_CURRENT('tblItem');
COMMIT TRANSACTION
У кого-нибудь есть предложения, как сделать это лучше?
Я знаю людей, которые прочтут это и скажут: «Триггеры - ЗЛО, не используйте их!» Хотя я ценю ваши убеждения, пожалуйста, не предлагайте это «предложение».