Dlaczego robisz to w wielu wypowiedziach? Dlaczego nie:
INSERT dbo.Items (item_name, item_cost, item_code)
OUTPUT inserted.ItemID, @ProjectID, @ItemQuantity
INTO dbo.project_items(item_id, project_id, item_quantity)
VALUES (@ItemName, @ItemCost, @ItemCode);
Teraz wystarczy wywołać tylko jedną funkcję ExecuteNonQuery() a Twoja aplikacja nie musi dbać o faktycznie SCOPE_IDENTITY() wygenerowana wartość. (Nadal możesz pobrać SCOPE_IDENTITY() jeśli chcesz oczywiście użyć ExecuteScalar - ale jak słusznie zauważa Nenad, wybierz jedno zamiast dzwonić do obu.)
Ponieważ teraz wiemy, że istnieje tutaj jawny klucz obcy, nadal możemy zredukować kod C# do jednego wywołania, nawet jeśli nie możemy użyć OUTPUT klauzula.
DECLARE @i INT;
INSERT dbo.Items (item_name, item_cost, item_code)
SELECT @ItemName, @ItemCost, @ItemCode;
SELECT @i = SCOPE_IDENTITY();
INSERT dbo.project_items(item_id, project_id, item_quantity)
SELECT @i, @ProjectID, @ItemQuantity
SELECT @i; -- if necessary
Jeszcze czystsze byłoby umieszczenie tego w procedurze składowanej.