Делаем SqlDataAdapter/Datareader действительно доступными только для чтения

Обновленный вопрос: есть ли способ заставить dataadapter принимать только команды, которые не включают какие-либо команды обновления/удаления/создания/удаления/вставки, кроме проверки command.text перед отправкой в ​​dataadapter (в противном случае выдается исключение). есть ли такие встроенные функции, предоставляемые dot net в datareader dataadapter или любом другом?

Примечание. DataReader возвращает результаты, а также принимает запрос на обновление и возвращает результат. (Возможно, я пропускаю какую-то ошибку, но я показываю свою команду обновления непосредственно перед выполнением программы чтения, а затем показываю сообщение после ее успеха, и все идет нормально.


person Sami    schedule 21.09.2012    source источник
comment
дубликат - stackoverflow.com/questions/4900630/   -  person Tobsey    schedule 21.09.2012
comment
Альтернативным вариантом может быть использование метода DataTable.Load, который принимает SqlDataReader, поэтому вам не нужно делать ничего, кроме DataTable.Load(command.ExecuteReader()).   -  person dash    schedule 21.09.2012
comment
Также обратите внимание, что вы не должны полагаться только на это — убедитесь, что вы правильно разрешаете таблицам только разрешение SELECT (GRANT SELECT ON [Table] TO [User]). Кто-то может набрать что-то вроде SELECT * FROM User;DROP TABLE User;, что технически является оператором sql, который возвращает набор результатов :-) Свойство SelectCommand просто предназначено для абстрагирования загрузки данных в адаптер - оно ничего не делает для предотвращения неправильного sql. от запуска.   -  person dash    schedule 21.09.2012
comment
@John - см. мое обновление для второй части. Надеюсь, поможет.   -  person dash    schedule 21.09.2012


Ответы (4)


Не могли бы вы найти строку по некоторым ключевым словам? Как CREATE, UPDATE, INSERT, DROP или если запрос не начинается с SELECT? Или это слишком мелко?

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

person Bmo    schedule 21.09.2012
comment
Да, это вариант, который я пропустил, чтобы упомянуть в своем вопросе. Но я ищу какой-нибудь технически надежный. Также здесь я должен использовать регулярное выражение, которого я хочу избежать - person Sami; 21.09.2012
comment
Тогда я бы пошел по маршруту 2. Настройте логин сервера, который имеет только dataReader. Затем с решением Майка используйте этот логин для строки подключения. Тогда, даже если они попытаются совершить что-то кроме SELECT, сервер скажет «нет». Вы можете обработать это исключение соответственно с помощью trycatch или чего-то еще. - person Bmo; 21.09.2012
comment
Что, если пользователь захочет использовать хранимые процедуры? Вполне возможно передать хранимую процедуру GetAllDataUpdates, например, которая выбирает данные, но содержит слово Update. Анализ пользовательского ввода болезненный :-) Вам действительно следует применять разрешения на уровне БД, чтобы быть абсолютно уверенным. - person dash; 21.09.2012
comment
Лучше один. После того, как выбора нет, я должен сделать это. - person Sami; 21.09.2012

Все, что вам нужно сделать, это убедиться, что для файла DataAdapter не подготовлены инструкции INSERT, UPDATE или DELETE. Ваш код может выглядеть примерно так:

var dataAdapter = new SqlDataAdapter("SELECT * FROM table", "connection string");

OR

var dataAdapter = new SqlDataAdapter("SELECT * FROM table", sqlConnectionObject);

И бац, у вас есть адаптер данных только для чтения.

person Mike Perrenoud    schedule 21.09.2012

Если вам просто нужен DataTable, то следующий метод является коротким и снижает сложность:

public DataTable GetDataForSql(string sql, string connectionString)
{
    using(SqlConnection connection = new SqlConnection(connectionString))
    {
        using(SqlCommand command = new SqlCommand())
        {
            command.CommandType = CommandType.Text;
            command.Connection = connection;
            command.CommandText = sql;
            connection.Open();          
            using(SqlDataReader reader = command.ExecuteReader())
            {
                DataTable data = new DataTable();
                data.Load(reader);
                return data;
            }

        }

    }

}

Применение:

try{
    DataTable results = GetDataForSql("SELECT * FROM Table;", ApplicationSettings["ConnectionString"]);
}
catch(Exception e)
{
    //Logging
    //Alert to user that command failed.
}

Здесь нет необходимости использовать DataAdapter - это не совсем то, что вы хотите. Зачем вообще заниматься перехватом исключений и т. д., если используются команды «Обновить», «Удалить» или «Вставить»? Это не очень подходит для того, что вы хотите сделать.

Важно отметить, что свойство SelectCommand не делает ничего особенного — когда SelectCommand выполняется, оно по-прежнему будет запускать любую переданную ему команду — оно просто ожидает возврата набора результатов, и если нет результаты возвращаются, тогда он возвращает пустой набор данных.

Это означает, что (и вы должны сделать это в любом случае) вы должны явно предоставлять только разрешения SELECT тем таблицам, которые вы хотите, чтобы люди могли запрашивать.

ИЗМЕНИТЬ

Чтобы ответить на ваш другой вопрос, SqlDataReader являются ReadOnly, потому что они работают через курсор в виде пожарного шланга только для чтения. Что это фактически означает:

while(reader.Read()) //Reads a row at a time moving forward through the resultset (`cursor`)
{
   //Allowed
   string name = reader.GetString(reader.GetOrdinal("name"));
   //Not Allowed - the read only bit means you can't update the results as you move through them
   reader.GetString(reader.GetOrdina("name")) = name;
}

Он доступен только для чтения, поскольку не позволяет обновлять записи по мере их перемещения. Однако нет причин, по которым sql, который они выполняют для получения набора результатов, не может обновлять данные.

person dash    schedule 21.09.2012
comment
Вы видели мое обновление? Я использую то же самое, но странно, что он принимает запрос на обновление - person Sami; 21.09.2012
comment
@John Это потому, что все они просто выполняют sql, который вы ему передаете. Единственный верный способ сделать то, что вы хотите, — это ограничить разрешения на уровне базы данных. SqlDataReader и SelectCommand предназначены для возврата наборов результатов из запросов sql - они сами не ограничивают запрос - вы должны это сделать :-) - person dash; 21.09.2012
comment
Например: SELECT * FROM Table; UPDATE Table SET Name = 'Stinky'; — это вполне корректный оператор SQL, который возвращает результаты — вы можете передать его либо в устройство чтения данных, либо в SelectCommand. - person dash; 21.09.2012

Если у вас есть требование только для чтения, пусть ваш TextBox использует строку подключения, которая использует учетную запись только с разрешениями db_datareader в базе данных SQL.

В противном случае, что мешает разработчику, использующему ваш контроль, просто подключиться к базе данных и самостоятельно сеять хаос с помощью SqlCommand?

person tomfanning    schedule 21.09.2012