Для нескольких таблиц с идентификационными полями мы реализуем схему безопасности на уровне строк, используя триггеры представлений и вместо триггеров для этих представлений. Вот упрощенная примерная структура:
-- 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
Есть ли у кого-нибудь предложения о том, как это сделать лучше?
Я знаю людей, которые прочитают это и скажут: «Триггеры — это ЗЛО, не используйте их!» Хотя я ценю ваши убеждения, пожалуйста, не делайте этого «предложения».