понедельник, января 29, 2007

сериализация сериализовалась-cериализовалась да и десериализовалась

Ситуация: есть некоторые объекты, которые надо долговременно хранить (в БД/на диске).
Платформа: .NET
Очевидное решение: десериализация.

Неочевидная проблема: если у сборки, где определен сериализованный тип меняется строгое имя (strong name), десериализация объета невозможна.
Пояснение: Любой тип в .NET характеризуется именем, структурой И сборкой, в которой он определен. Это сделано целенаправленно, чтобы избежать глюков/дыр в связи с наличием нескольких типов, имеющих одинаковые имена.
Для идентификации сборки используется ее строгое имя, т.е. совокупность имени, версии и 
публичного ключа автора/издателя. 
Если у сборки изменить строгое имя, то при десериализации ранее сериализованного объекта окажется, что система просто не знает, где определен тип, к которому он принадлежит.
Решения:
а) подписывать сборки проекта как можно раньше и не менять публичный ключ.
б) не использовать стандартный механизм сериализации для хранения данных, если есть вероятность, что публичный ключ может быть изменен впоследствии.

Первый вариант, безусловно, наиболее предпочтительный. Но не всегда реальный. Увы.
Второй хорош для RealProgrammer'а, который считает злом использование готовых решений. Можно, скажем, ручками писать поля объектов в поток. Но при необходимости сериализовать объекты разных классов издержки оказываются слишком велики. Еще один вариант - XML-сериализация - может оказаться неприемлемым по всем тем причинам, по которым XML 
оказывается менее предпочтительным, нежели бинарный формат.
Остается последний вариант 
в) найти способ ручного управления выбором типов.
Оказалось, что третий путь (как обычно ;)) наиболее простой и эффективный (с т.зр. расширяемости). Для этого достаточно создать свою реализацию класса System.Runtime.Serialization.SerializationBinder.
Конкретно - перекрыть в нем метод BindToType.
class MyBinder: SerializationBinder{
      public override Type BindToType(string assemblyName, string TypeName){
              // логика выбора типа на основе имени сборки, версии и пр.
             else return null;
      }
}

А затем задать экземпляр своего класса в качестве значения свойства Binder.
BinaryFormatter bf = new BinaryFormatter();
bf.Binder = new MyBinder();

Bingo! Привязка осуществляется корректно.
Причем, насколько я понимаю, даже не обязательно реализовывать в привязываемом типе ISerializable, достаточно атрибута 
[Serializable].

воскресенье, января 21, 2007

Я вот подумал, и решил использовать этот блог для технических постов, ранее публиковавшихся в моем ЖЖ под тегом  "мой Вавилон".
Потому как ЖЖ - он по сути своей всё-таки вещь для более гуманных проявлений жизнедеятельности, нежели сравнение кода, порождаемого компиляторам Хаскелля, Немерля и Волта...
Ну, вы поняли =)
Посему там будут только краткие отрывки из описания моих программных эскапад, а здесь - более развернутые рассказы.