Повторное создание двумерного двойного массива из массива byte [], хранящегося в SQL - PullRequest
0 голосов
/ 27 апреля 2019

У меня есть несколько очень больших матриц (иногда двойных, но, возможно, других типов), которые могут иметь различные размеры.Я хотел бы сохранить весь массив как varbinary (max)

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

Таблица SQL выглядит следующим образом:

create table test_varbinary_table(
    id int not null IDENTITY PRIMARY KEY ,
    name varchar(256) not null,
    rows int not null,
    cols int not null,
    vb_data varbinary(max) null
)

Хранимая процедура для записиданные здесь: (удалены, потому что, может быть, это просто запутывает проблему) C # для записи примера записи здесь: (удалено, потому что я думаю, что эта часть работает, и я не знаю, что это помогает решить проблему)

Хранимая процедура GetData:

ALTER PROCEDURE [dbo].[GetData] 
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- Insert statements for procedure here
    SELECT * from test_varbinary_table
END

Я прочитал таблицу с

    try
    {
        using (SqlConnection conn = new SqlConnection(connString))
        {
            conn.Open();
            SqlDataReader rdrMatrix = null;
            DataTable tblMatrix = new DataTable();

            using (SqlCommand cmd = new SqlCommand("dbo.GetData", conn))
            {
                cmd.CommandType = CommandType.StoredProcedure;
                rdrMatrix = cmd.ExecuteReader(CommandBehavior.CloseConnection);
                tblMatrix.Load(rdrMatrix);
                String strResult = "";
                foreach (DataRow dr in tblMatrix.Rows)
                {
                    int r = 2, c = 3;
                    double[,] dary = new double[r, c];
                    byte[] retData = new byte[6 * sizeof(double)];
                    String retName;
                    retName = dr["name"].ToString();
                    strResult += retName + "\r\n";
                    retData = (byte[])dr["vb_data"];  // potential problem?
                    Buffer.BlockCopy(retData, 0, dary, 0, dary.Length); // potential problem?

                    for (int ri = 0; ri< 2; ri++)
                    {
                        strResult += "     ";
                        for (int ci = 0; ci<3; ci++)
                        {
                            strResult += " " + dary[ri,ci] ;
                        }
                        strResult += "\r\n";
                    }

                    strResult +=  "\r\n" ;
                }
                textBox1.Text = strResult;
            }
            conn.Close();
        }
        //textBox1.Text = "No Failure!";
    }

Для кода записи я просто вручную изменил содержимое, чтобы записать несколько разных записей,Когда я проверяю таблицу в SSMS, я вижу, что она успешно добавила строки.Хотя я не могу прочитать поток байтов, он меняется, если я изменяю данные, и остается неизменным, если я не изменяю данные.Однако когда я запускаю код c # для чтения записей, я получаю один и тот же результат каждый раз ... нули.

Вывод выглядит следующим образом:

foo
0 0 0
0 0 0
bar
0 0 0
0 0 0

Так или иначе, он либо не читает поле @data, либо не преобразует его в byte [] должным образом, либо блок-копия работает неправильно.(Я думаю.) В любом случае, он возвращает нули вместо данных, которые я хранил.

1 Ответ

0 голосов
/ 28 апреля 2019

Мне удалось решить проблему с помощью BlockCopy для кодирования матрицы в виде байтового массива, который я мог записать в varbinary. При чтении я использовал BitConverter для декодирования из байтового массива в двойной [,]. Я не знаю, почему я не смог заставить BlockCopy работать в другом направлении. Я что-то упустил.

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

Таблица, в которой хранится матрица:

CREATE TABLE [dbo].[test_varbinary_table](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [name] [varchar](8) NOT NULL,
    [version] [varchar](16) NOT NULL,
    [date] [datetime] NOT NULL,
    [notes] [varchar](max) NULL,
    [rows] [int] NOT NULL,
    [cols] [int] NOT NULL,
    [checksum] [float] NULL,
    [vb_data] [varbinary](max) NULL
) 

Метод для кодирования двойного [,] в байтовый массив, который будет записан непосредственно в varbinary:

public byte[] DoubleMatrixToByteArray(double[,] doubleArray)
{
    int rows = doubleArray.GetLength(0);
    int cols = doubleArray.GetLength(1);
    byte[] byteArray = new byte[rows * cols * sizeof(double)];
    Buffer.BlockCopy(doubleArray, 0, byteArray, 0, byteArray.Length);
    return byteArray;
}

Метод декодирования из varbinary обратно в двумерный двойной массив:

    public double[,] VarBinaryToDoubleMatrix (byte[] byteArray, int rows, int cols)
    {
        double[,] doubleArray = new double[rows, cols];
        for (int r=0; r<rows; r++)
        {
            for (int c=0; c<cols; c++)
            {
                doubleArray[r,c] = BitConverter.ToDouble(byteArray, r * cols * sizeof(double) + c * sizeof(double));
            }
        }
        return doubleArray;
    }

Контрольная сумма:

// Summation of all cells in a matrix, just a quick verification check.
// The intent is to verify that when a matrix is read from the database,
// the reconstructed matrix has the same checksum as the one that was saved.
//
    public double computeChecksum(double[,] dblArray)
    {
        int rank = dblArray.Rank; // rank is the number of dimensions of an array.
        int rows = dblArray.GetLength(0);
        int cols = dblArray.GetLength(1);
        double sum = 0.0;

        for (int r = 0; r < rows; r++)
        {
            for (int c = 0; c < cols; c++)
            {
                sum += dblArray[r, c];
            }
        }

        return sum;
    }
}

Я сохраняю массив с контрольной суммой, которую читаю вместе с полем varbinary. Затем я пересчитываю контрольную сумму и сравниваю ее с сохраненным значением контрольной суммы.

...