Десериализация MemoryStream - неожиданное поведение - PullRequest
2 голосов
/ 23 июня 2010

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

У меня две сборки;AppToDB.dll и AppFromDB.dll.У меня есть 3-я сборка - MyCustomObject.dll - на которую обе эти сборки содержат ссылку.MyCustomObject.dll расширяется MarshalByRefObject.

В моем AppToDB.dll я выполняю следующий код:

    public bool serializeToDB(MyCustomObject obj)
    {            
        OdbcDataAdapter da = new OdbcDataAdapter();
        MemoryStream memStream = new MemoryStream();

        try
        {
            ObjRef marshalledObj = RemotingServices.Marshal((System.MarshalByRefObject)obj);

            // Serialize the object; construct the desired formatter
            IFormatter oBFormatter = new BinaryFormatter();

            // Try to serialize the object
            oBFormatter.Serialize(memStream, marshalledObj);

            // Create byte array
            byte[] serialized = memStream.ToArray();

            // Build the query to write to the database
            string queryString = 
                     "INSERT INTO MyCustomObject(id, object) VALUES(?, ?)";
            OdbcCommand command = new OdbcCommand(queryString, connection);
            command.Parameters.AddWithValue("id", 1);
            command.Parameters.AddWithValue("object", serialized);

            // Write the object byte array to the database
            int num = command.ExecuteNonQuery();
     }
     catch { }
 }

В AppFromDB.dll Я выполняю этот код:

    public OCR.Batch deserializeFromDB()
    {            
        MemoryStream memStream = new MemoryStream();

        try
        {
            string queryString = "SELECT object FROM FCBatch";
            OdbcCommand command = new OdbcCommand(queryString, connection);
            OdbcDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess);

            // Size of the BLOB buffer.
            int bufferSize = 100;
            // The BLOB byte[] buffer to be filled by GetBytes.
            byte[] outByte = new byte[bufferSize];
            // The bytes returned from GetBytes.
            long retval;
            // The starting position in the BLOB output.
            long startIndex = 0;

            MemoryStream dbStream = new MemoryStream();

            while (reader.Read())
            {
                // Reset the starting byte for the new BLOB.
                startIndex = 0;

                // Read bytes into outByte[] and retain the number of bytes returned.
                retval = reader.GetBytes(0, startIndex, outByte, 0, bufferSize);

                // Continue while there are bytes beyond the size of the buffer.
                while (retval == bufferSize)
                {
                    dbStream.Write(outByte, 0, bufferSize);
                    dbStream.Flush();

                    // Reposition start index to end of last buffer and fill buffer.
                    startIndex += bufferSize;
                    retval = reader.GetBytes(0, startIndex, outByte, 0, bufferSize);
                }

                // Write the remaining buffer.
                dbStream.Write(outByte, 0, (int)retval);
                dbStream.Flush();
            }
            // Close the reader and the connection.
            reader.Close();

            dbStream.Position = 0;
            object temp = oBFormatter.Deserialize(dbStream);
            MyCustomObject obj = (MyCustomObject)temp;

            return null;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
            return null;
        }
    }

ОК, поэтому в обеих частяхкода вы можете увидеть MemoryStream объект.В первом AppToDB он создан, и, если посмотреть на его содержимое, он содержит 707 байтов.Хорошо.Я записываю это в базу данных и сохраняю там как BLOB.Теперь в AppFromDB я извлекаю BLOB и сохраняю его в массиве byte[].Я снова записываю массив byte[] в MemoryStream и вижу, что мои MemoryStream объекты содержат 707 байтов, и все они на месте, как и оригинал.Кажется, я успешно передал объект!

Теперь проблема заключается в object temp = oBFormatter.Deserialize(dbStream);.Как только я пытаюсь десериализовать, мой object является Прозрачным Прокси, и я не могу привести к MyCustomObject !!Как мне вернуть мой оригинальный объект?Как в имени # @ & можно получить объект MemoryStream .... В памяти ... готовый к сериализации ... и вдруг это снова Прозрачный прокси.

Я в растерянности.Помощь приветствуется.Я буду молиться # @ & за того, у кого есть ответ;)

Редактировать 1 Хорошо, я должен сказать, что сейчас все начинает обретать смысл (хотя проблема сохраняется).Моя проблема: у меня есть объект (включая состояние) на одной стороне, и мне нужно сохранить его в базе данных, чтобы я мог использовать его несколько дней спустя другим процессом на другой стороне.

Мой объект не сериализуем,потому что он оборачивает сторонний объект, который не помечен как сериализуемый.Так что мой единственный вариант, похоже, маршал, который возвращает ObjRef, который, в свою очередь, сериализуем.Но, конечно, спустя несколько дней, объект, который я десериализирую, является просто ссылкой, и мой первоначальный объект исчез.

Как мне решить мою проблему?Должно быть, все больше людей сталкивались с этим, и я просто не могу найти ответ ...

Редактировать 2 ОК, я думаю, что я собираюсь написать свой собственный сериализуемый класс, изоморфныйСторонний объект.Затем выполните весь сторонний объект и, сохраните / оберните информацию о его состоянии и т. Д. Затем сериализуйте мой объект в базу данных ... Кажется, это мой единственный вариант.

Редактировать 3 Запускпо этой проблеме снова через некоторое время.Просто понял, что решение, опубликованное в Edit 2, не будет работать.Я должен десериализовать в объект, который знает сторонняя сборка, так как он будет продолжать выполнять над ним операции.

1 Ответ

1 голос
/ 23 июня 2010

Вы сериализуете ObjRef, который является не исходным объектом, а скорее «удаленной ссылкой», содержащей всю информацию для передачи ссылки.

Из MSDN (выделениешахта):

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

После маршалинга класса, реализующего MarshalByRefObject, представляющий его объект ObjRef переносится через канал в другой домен приложения, возможно, в другой процесс или компьютер.Когда ObjRef десериализуется в целевом домене приложения, он анализируется для создания прозрачного прокси для удаленного объекта MBR .Эта операция называется unmarshaling.

Редактировать (из-за предоставленной новой информации):

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

  • Используйте сериализацию XML с XmlSerializer.Сериализация XML отличается тем, что она сериализует и десериализует все открытые свойства объекта вместо сериализации полей и / или пользовательских данных (когда ISerializable реализуется объектом, подлежащим сериализации).Поэтому, если сторонние объекты являются просто контейнерами данных, сериализация открытых свойств достаточно хороша, и они предоставляют конструктор по умолчанию, с этим решением вам должно быть хорошо.

  • Создайте свой собственный«низкоуровневый» код сериализации, использующий отражение для получения полей и их сериализации.Этот подход обычно требует, чтобы ваше приложение работало с полным доверием (для отражения и изменения закрытых полей с помощью отражения требуются разрешения, которые обычно отсутствуют в более низких доверительных отношениях).Вот некоторые методы, которые могут вам помочь: FormatterServices.GetSerializableMembers () для получения полей для сериализации, FormatterServices.GetObjectData () для получения данных полей из экземпляра объекта, FormatterServices.GetSafeUninitializedObject () при десериализации для создания нового неинициализированного экземпляра (конструктор не вызывается) и FormatterServices.PopulateObjectMembers () для записи полей обратно в новый экземпляр.Если у вас есть только простые типы данных, которые сериализуются в полях, вы можете сериализовать и десериализовать object[], который вы используете для хранения данных поля.

  • Ваша текущая идея, котораявручную написать реплику сторонних объектов.Это может быть очень болезненным и в основном работает только тогда, когда будет работать и XML-сериализация.Если для sinatce свойства доступны только для чтения, при таком подходе вы далеко не уедете.

...