Застрял на методе SqlDataReader.GetValues

Я вызываю хранимую процедуру SQL, которая возвращает небольшую таблицу пользователей и их боссов. Что я хотел бы сделать, так это сравнить эту информацию с переменной в другой части моего приложения, чтобы включить/отключить редактирование строки GridView. Кажется, что для этого метод GetValues вернет то, что мне нужно, то есть весь набор данных, который возвращает хранимая процедура. Я думаю, что как только у меня будут эти данные в моем приложении, я смогу загрузить этот набор данных в массив и прокрутить его, чтобы сделать то, что мне нужно.

Проблема вот в чем. В моем коде ниже я получаю сообщение об ошибке

Невозможно неявно преобразовать тип «int» в «объект»

Когда я смотрю на документация по этому методу, возвращаемое значение - int.

Мой вопрос: поскольку хранимая процедура содержит строковые данные (имена пользователей и боссов), почему мехтод GetValues возвращает int? Как мои фактические данные представлены в виде числа и как я могу получить свои данные в массиве на основе строк?

Я просмотрел много примеров и информации в Интернете, похоже, это обычная проблема, с которой сталкиваются новые люди. Я просто не понимаю или не понимаю, и я не делаю никакого прогресса. Любая помощь приветствуется!

    public Object[] GetDeptAppData()
    {
        Object[] myObject;
        string sp_deptapp, sp_status, sp_supervisor;

        //execute stored procedure to get data from database
        SqlConnection sqlConnection = new SqlConnection("Data Source=MyServer;Initial Catalog=MyCatalog;Persist Security Info=True;User ID=MyUser;Password=MyPassword");
        SqlCommand cmd = new SqlCommand();
        SqlDataReader reader;

        cmd.CommandText = "SP_Admin_DeptApp_Load";
        cmd.CommandType = CommandType.StoredProcedure;

        cmd.Connection = sqlConnection;
        sqlConnection.Open();
        reader = cmd.ExecuteReader();

        if (reader.HasRows == true)
        {
            //sp_deptapp = reader.GetValue(0).ToString();
            //sp_status = reader.GetValue(1).ToString();
            //sp_supervisor = reader.GetValue(2).ToString();

            myObject = reader.GetValues(myObject);
        }
        else
        {
            //nothing in reader, throw exception
        }
        sqlConnection.Close();

        return myObject;
    }

person Rob    schedule 06.02.2014    source источник


Ответы (3)


Ошибка, с которой вы столкнулись, находится в этой строке:

myObject = reader.GetValues(myObject);

myObject — это массив объектов, а метод GetValues возвращает целое число, содержащее число, помещенное в массив. Итак, чтобы решить вашу ошибку, вы можете изменить строку на следующее:

var count = reader.GetValues(myObject);

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

while (reader.Read())
{
    // Read row and add row to list
}

Пример использования SqlDataReader см. в этом ссылка.


Образец

Если вы хотите получить несколько строк и сохранить данные, я предлагаю добавить класс для хранения данных строки (вы можете проверить типы данных, чтобы они соответствовали типам, возвращаемым SP):

public class Data
{
    public string DeptApp { get; set; }
    public string Status { get; set; } // this might be another data type
    public string Supervisor { get; set; }
}

Затем извлеките строки следующим образом:

public Data[] GetDeptAppData()
{
    //execute stored procedure to get data from database
    using (SqlConnection sqlConnection = new SqlConnection("Data Source=MyServer;Initial Catalog=MyCatalog;Persist Security Info=True;User ID=MyUser;Password=MyPassword"))
    {
        SqlCommand cmd = new SqlCommand();
        cmd.CommandText = "SP_Admin_DeptApp_Load";
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Connection = sqlConnection;
        sqlConnection.Open();
        using (var reader = cmd.ExecuteReader())
        {
            if (!reader.HasRows)
            {
                // throw exception
            }
            var lst = new List<Data>();
            while (reader.Read())
            {
                var row = new Data();
                row.DeptApp = reader.GetString(0);
                row.Status = reader.GetString(1);
                row.Supervisor = reader.GetString(2);
                lst.Add(row);
            }
            return lst.ToArray();
        }
    }
}
person Markus    schedule 06.02.2014
comment
myObject — это массив объектов. Это нормально? Я думал, что myObject будет единым объектом, содержащим результаты хранимой процедуры. Метод GetValues ​​возвращает целое число, содержащее число, помещенное в массив. - Значит, все, что он делает, это что-то считает? Что он считает, и почему меня это волнует? Мне все равно, сколько строк я возвращаю данные из хранимой процедуры, я забочусь о фактических данных. Кажется, что var COUNT будет содержать только число, представляющее, сколько элементов находится в хранимой процедуре. - person Rob; 06.02.2014
comment
@Rob: нет, myObject объявлен как Object[], так что это массив объектов. SqlDataReader используется для извлечения одной строки данных за другой. GetValues ​​сохраняет данные в параметре и возвращает количество прочитанных полей из текущей строки (!). Если вы хотите получить несколько строк, вам нужно добавить цикл. Я расширим свой ответ. - person Markus; 06.02.2014
comment
Раньше у меня был WHILE lopp, но я не думал, что мне это нужно, потому что я ожидал, что GetValues() вернет все мои данные одним большим выстрелом в myObject - person Rob; 06.02.2014
comment
@Rob: я обновил образец. Обратите внимание, что SqlDataReader — это способ быстрого извлечения данных только в прямом направлении. Он возвращает данные построчно. - person Markus; 06.02.2014
comment
@Rob, если вы хотите, чтобы все операции выполнялись в одной команде, вы можете заглянуть в команду Fill( SqlDataAdapter. Он даст вам все результаты за один шаг и сохранит результаты в объекте DataTable или DataSet. - person Scott Chamberlain; 07.02.2014
comment
Извините за задержку, был занят другим рабочим заданием. Это сработало отлично, Маркус, спасибо! Несколько вопросов, если вы все еще здесь. 1. public Data[] GetDeptAppData() — объявляем ли мы тип данных DATA? Не уверен в объяснении этого ключевого слова. 2. var reader = cmd.ExecuteReader() - Не уверен в значении/использовании VAR здесь. Я немного почитал здесь, msdn.microsoft.com/en-us/library/ bb383973.aspx, хотя не уверен, что понял :) Остальное я понял. Мы составляем список и добавляем элементы построчно. Затем мы выбрасываем его как массив с помощью метода .ToArray. Спасибо еще раз!! - person Rob; 10.02.2014
comment
@Rob: приятно слышать, что это помогло. Данные были просто образцом имени, так как я не знаю контекста вашего проекта. Никакого ключевого слова. Вы можете использовать var, чтобы позволить компилятору определить тип данных для вас. Таким образом, вы можете заменить var reader = cmd.ExecuteReader() на SqlDataReader reader = cmd.ExecuteReader(). Компилятор знает, что ExecuteReader возвращает SqlDataReader и, таким образом, делает читатель переменной типа SqlDataReader. Обычно работает очень хорошо, хотя в некоторых очень редких случаях компилятор может не определить правильный тип. - person Markus; 10.02.2014
comment
@Маркус, И последнее. Как я могу вызвать этот метод и присвоить его переменной? Я переименовал DATA[ в myData[] для удобства чтения. Я попытался использовать array[] myArray = Rob.GetDeptAppData(); но получил сообщение об ошибке, говорящее, что он не может преобразовать .myData [] в массив []. Поскольку новый метод создает массив (возврат lst.ToArray), что нужно преобразовать? Другими словами, как я могу вызвать метод и вернуть массив в вызывающий метод для использования? - person Rob; 11.02.2014
comment
@Rob: либо myData[] x = GetDeptAppData(); или var x = GetDeptAppData(); должно сработать. - person Markus; 11.02.2014
comment
С этим все сделано, работает отлично. Еще раз спасибо, Маркус! Я должен тебе интернет-пиво :) - person Rob; 11.02.2014

GetValues возвращает int, то есть "количество экземпляров Object в массиве", поэтому вы не можете присвоить его myObject. Это загадочно. На самом деле это означает (я полагаю) количество значений столбца, возвращаемых для текущей строки.

Тебе нужно:

int numColumnsRetrieved = reader.GetValues(myObject);

или что-то подобное.

Затем ваши данные присутствуют внутри myObject, но только для текущей строки, согласно документам "Заполняет массив объектов значениями столбца текущей строки". Итак, вам все еще нужно повторять, например:

var myObjectList = new List<object[]>(); // ... shudder ...
while (reader.Read())
{
    var myObject[] = new object[reader.FieldCount];
    int instances = reader.GetValues(myObject);
    myObjectList.Add(myObject);
}

Возможно, вам будет полезно использовать более конкретный метод, например GetString:

var usersAndBosses = new List<Tuple<string, string>>();
while (reader.Read())
{
    string username = reader.GetString(0); // column index 0
    string bossname = reader.GetString(1); // column index 1
    usersAndBosses.Add(Tuple.Create(username, bossname));
}

ИЗМЕНИТЬ

Если вы хотите ложное использование возвращаемого значения из GetValues, вы можете свести таблицу строк в список строк:

var myStringList = new List<string>();
while (reader.Read())
{
    var myObject[] = new object[100];
    int colCount = reader.GetValues(myObject);
    for (int i = 0; i < colCount; i++)
    {
        myStringList.Add((string)myObject[i]);
    }
}
person Andy Brown    schedule 06.02.2014
comment
Тот же вопрос, что и у другого человека, который ответил, почему меня волнует «подсчет экземпляров объекта в массиве»? Меня интересуют данные, а не их количество. Число ничего не делает для меня. Мне нравится ваш второй блок, у меня есть аналогичная вещь в моем коде выше, закомментированном. Я могу определить, используя while(reader.read()), 3 уникальных столбца данных, используя reader.getValue(0).ToString(), но тогда мне просто нужно загрузить их в массив, а затем передать метода с использованием RETURN. - person Rob; 06.02.2014
comment
Первый ответ: я бы избегал передачи массивов объектов, C# — строго типизированный язык (даже кортежи — это ленивое кодирование, хотя, по крайней мере, вы можете принудительно проверять типы во время компиляции). - person Andy Brown; 06.02.2014
comment
Второй ответ: если, например, вы передаете массив объектов длиной 10, если было только 3 столбца, он сообщит вам, что поместил только 3 значения в массив длиной 10. Я никогда не нашел для него применения. GetValues не предназначен для вывода всех строк и столбцов, ни один метод не предназначен, поэтому вам нужно выполнить итерацию, используя reader.Read() - person Andy Brown; 06.02.2014

Ниже приведена функция ExecuteSelectQuery(...) для получения результатов любых запросов SELECT.

Результатом ExecuteSelectQuery является список массивов элементов System.Object, которые могут быть преобразованы в требуемые типы в вызывающем коде.

public sealed class DbPerformer
{
    private const string mySqlServerName = "localhost";
    private const string dataBaseName = "testdb";
    private const string uid = "login";
    private const string password = "password";

    private MySqlConnection connection;

    public DbPerformer()
    {
        string connectionString;
        connectionString = "server=" + mySqlServerName + ";";
        connectionString += "Username=" + uid + ";";
        connectionString += "Password=" + password;

        connection = new MySqlConnection(connectionString);
    }

    private bool OpenConnection()
    {
        try
        {
            connection.Open();
            return true;
        }
        catch (MySqlException ex)
        {
            switch (ex.Number)
            {
                case 0:
                    Debug.WriteLine("Cannot connect to server {0}", mySqlServerName);
                    break;

                case 1045:
                    Debug.WriteLine("Invalid username/password.");
                    break;
            }
            return false;
        }
    }

    private bool CloseConnection()
    {
        try
        {
            connection.Close();
            return true;
        }
        catch (MySqlException ex)
        {
            Debug.WriteLine(ex.Message);
            return true;
        }
    }

    public List<object[]> ExecuteSelectQuery(string selectQuery)
    {
        List<object[]> resultList = new List<object[]>();

        if (true == this.OpenConnection())
        {
            MySqlCommand command = new MySqlCommand(selectQuery, connection);
            MySqlDataReader resultReader = command.ExecuteReader();

            while (resultReader.Read())
            {
                object[] resultRow = new object[resultReader.FieldCount];
                resultReader.GetValues(resultRow);
                resultList.Add(resultRow);
            }

            resultReader.Close();
            this.CloseConnection();
        }
        else
        {
            throw new Exception("Can not open connection to database.");
        }
        return resultList;
    }
}
person Denis Kochetkov    schedule 26.10.2018