Как передать параметры в сквозной запрос в c #?

У меня есть программа C #, которая использует OPENQUERY() для выбора со связанного сервера. Этот сквозной запрос принимает параметр. Чтобы защититься от SQL-инъекции, я хотел бы передать этот параметр аналогично SqlCommand.Parameters.AddWithValue, однако OPENQUERY() не принимает переменные.

До сих пор я использовал SqlCommand для передачи параметров запросам, выполняемым к таблицам в SQL Server. Однако мне также нужно получить доступ к связанному серверу, который является Oracle. Есть ли способ добавить этот параметр, не объединяя его как строку?

string query = "SELECT * FROM OPENQUERY(linked_server, 
'SELECT * FROM User.Table WHERE col1 = @parameter1 ')";

РЕДАКТИРОВАТЬ: У меня нет разрешений на создание хранимых процедур на удаленном сервере Oracle. Поэтому выполнение Sp_executesql для хранимой процедуры не кажется мне немедленным ответом.


person user3750325    schedule 19.04.2018    source источник
comment
Может быть, вы могли бы заставить SQL Server действовать как представление для собранной информации? Может быть, превратить его в распределенную базу данных на основе копий? Может быть, превратить все это в Webserivce или что-то в этом роде? Любой способ обойти сложности серверной части должен работать.   -  person Christopher    schedule 20.04.2018
comment
ранее заданный вопрос: stackoverflow.com/questions/3378496/   -  person gordy    schedule 25.04.2018
comment
Это действительно вопрос, который задавали ранее, но если да, то ответ - НЕТ. Насколько я могу видеть, все варианты в другом вопросе либо используют конкатенацию строк где-то, либо они извлекают все удаленные строки и фильтруют локально.   -  person ewramner    schedule 25.04.2018
comment
Вы всегда выбираете из одного стола? Распределенный запрос может по-прежнему работать эффективно.   -  person Alex    schedule 27.04.2018
comment
Я не знаком с OPENQUERY. Так что мне просто любопытно. Можете ли вы просто создать хранимую процедуру на локальном или удаленном сервере, а затем использовать ее с параметрами? (SqlCommand.Parameters.AddWithValue)   -  person DxTx    schedule 28.04.2018
comment
У вас есть контроль над базой данных оракула? Можете ли вы создавать там процедуры и выполнять их с помощью связанного сервера?   -  person Zohar Peled    schedule 29.04.2018
comment
Вы пытались создать синоним для таблицы связанных серверов и выполнить свой запрос по синониму?   -  person Mohammad Nikravesh    schedule 30.04.2018


Ответы (4)


Специально для связанных серверов есть вызов EXEC (см. docs):

EXEC( 'SELECT * FROM User.Table WHERE col1 = ?', '<param>' ) AT linked_server

Вы можете поместить все это в string query, и вы будете защищены от SQL-инъекции в оператор SELECT, хотя вы можете получить синтаксическую ошибку для оператора EXEC.

person edixon    schedule 30.04.2018
comment
После установки RPC Out на true я почти заработал. Мне было трудно объединить некоторые символы в? например: EXEC ('SELECT * FROM User.Table WHERE col1 LIKE? -%', @var) At connected_server - person user3750325; 01.05.2018
comment
AFAIK насчет LIKE, я бы попробовал "НРАВИТСЯ?" в запросе и создайте аргумент с соответствующей последовательностью текста и "%". Это сработало с "LIKE? -%"? - person edixon; 01.05.2018
comment
@ user7733611 EXEC('SELECT * FROM User.Table WHERE col1 LIKE ? || ''%''', @var) At linked_server или EXEC('SELECT * FROM User.Table WHERE col1 LIKE CONCAT(?, ''%'')', @var) At linked_serverТо же возможно, используя подход из моего ответа. - person Lukasz Szozda; 01.05.2018

Это не лучший подход с точки зрения производительности, но вы можете отфильтровать SQL Server сторону:

string query = "SELECT * 
                FROM OPENQUERY(linked_server, 'SELECT * FROM User.Table') s
                WHERE col1 = @parameter1";

РЕДАКТИРОВАТЬ:

Из Как передать переменную в запрос связанного сервера:

Когда вы запрашиваете связанный сервер, вы часто выполняете сквозной запрос, который использует оператор OPENQUERY, OPENROWSET или OPENDATASOURCE.

В этой статье представлены три примера того, как передать переменную в запрос связанного сервера.

Чтобы передать переменную в одну из функций передачи, необходимо создать динамический запрос.

Подход 1:

Передавать основные значения

Если базовая инструкция Transact-SQL известна, но вам необходимо передать одно или несколько конкретных значений, используйте код, аналогичный приведенному в следующем примере:

DECLARE @TSQL varchar(8000), @VAR char(2)
SELECT  @VAR = 'CA'
 
SELECT  @TSQL = 'SELECT * FROM OPENQUERY(MyLinkedServer
                  ,''SELECT * FROM pubs.dbo.authors WHERE state = ''''' 
                  + @VAR + ''''''')'
EXEC (@TSQL)

Подход 2:

Используйте хранимую процедуру Sp_executesql

Чтобы избежать многослойных кавычек, используйте код, похожий на следующий пример:

DECLARE @VAR char(2)
SELECT  @VAR = 'CA'
EXEC MyLinkedServer.master.dbo.sp_executesql
   N'SELECT * FROM pubs.dbo.authors WHERE state = @state',
   N'@state char(2)',
   @VAR

и в вашем примере:

DECLARE @parameter1 <your_type> = ?;

EXEC linked_server.master.dbo.sp_executesql
     @'SELECT * FROM User.Table WHERE col1 = @parameter1 '
    ,N'@parameter1 <your_type>'
    ,@parameter1;

Если вам нужно выполнить другую операцию на локальной стороне:

DECLARE @parameter1 <your_type> = ?;
CREATE #temp(col_name <type>, ...);

INSERT INTO #temp(col_name)    
EXEC linked_server.master.dbo.sp_executesql
     @'SELECT col_name1,... FROM User.Table WHERE col1 = @parameter1 '
    ,N'@parameter1 <your_type>'
    ,@parameter1;

SELECT *
FROM #temp
-- JOIN any local table (SQL Server's side)
-- WHERE any_condition;
person Lukasz Szozda    schedule 25.04.2018
comment
К сожалению, таблица на связанном сервере слишком велика для этого. - person user3750325; 26.04.2018

Чтобы получить некоторое кеширование запросов SQL-сервера, лучше всего использовать sp_executesql хранимая процедура.

Он по своей конструкции любит передавать параметры в запрос, что хорошо для безопасности (предотвращение SQL-инъекций), а также производительности, когда вам нужно учитывать подразумеваемые преобразования (настоящий убийца производительности).

Вы можете сделать это просто, позвонив

using (SqlCommand cmd = _con.CreateCommand())
{
     cmd.CommandType = CommandType.StoredProcedure;
     cmd.CommandText = "sp_executesql";
     cmd.Parameters.Add("sel_query", myQuery);
     return cmd.ExecuteReader();
}  

Переменная myQuery может содержать любой допустимый TSQL и может быть сколь угодно сложной. Просто убедитесь, что все, что в нем, предназначено и безопасно как для вашего сервера, так и для того, к которому вы подключаетесь.

Некоторые динамические образцы можно найти при поиске в Интернете, см. этот пример. Однако вы можете сделать шаг назад, выпить кофе и подумать, возможно, вы хотели бы отделить определение таблицы других серверов от своего кода.

Вы можете подумать о создании представления на своем сервере, которое запрашивает связанный сервер, тогда вам нужно только запросить локальный объект, также хорошо работает с хранимыми процедурами и функциями.

Разделение также позволяет вам запрашивать другие базы данных, для которых у вашего клиента нет драйверов. Я создал подобные представления в DB2, Oracle и т. Д., Используя этот метод без необходимости установки драйверов в клиентских системах.

person Walter Vehoeven    schedule 28.04.2018

Вот лучший пример для создания динамического запроса и его выполнения. https://www.codeproject.com/Articles/20815/Building-Dynamic-SQL-In-a-Stored-Procedure. Я думаю, это тебе поможет.

person Vinod Ghunake    schedule 27.04.2018
comment
Вложенный sp_executesql тоже был моей первоначальной идеей, но после некоторого тестирования я пришел к выводу, что в его случае это не сработает, т. Е. Все равно разрешит SQL-инъекцию. Впрочем, возможно, я сделал это неправильно. - person Alex; 27.04.2018