Десериализация с двоичным форматированием

У меня есть программа, которая сериализует объект и отправляет его по сети:

TcpClient client = new TcpClient();
client.ReceiveTimeout = 10000;
client.SendTimeout = 10000;
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8888);
client.Connect(serverEndPoint);

BinaryFormatter binaryformatter = new BinaryFormatter();

NetworkStream networkStream = client.GetStream();
if (networkStream.CanWrite)
{
    binaryformatter.Serialize(networkStream, kort);
}

А с другой стороны я получаю и десериализую код как таковой:

TcpClient tcpClient = (TcpClient)client;
tcpClient.SendTimeout = 10000;
tcpClient.ReceiveTimeout = 10000;
NetworkStream clientStream = tcpClient.GetStream();
try
{
    if (clientStream.CanRead)
    {
        BinaryFormatter binaryformatter = new BinaryFormatter();
        binaryformatter.Binder = new AllowAllAssemblyVersionsDeserializationBinder();

        Kort tempkort = (Kort)binaryformatter.Deserialize(clientStream);
        SetImage(tempkort);
    }
}
catch (SerializationException e)
{
    MessageBox.Show("Failed to deserialize. Reason: " + e.Message);
    throw;
}
finally
{
    clientStream.Close();
    tcpClient.Close();
}

Но когда я десериализовал, я получил эту ошибку о том, что сборка отсутствует:

«Необработанное исключение типа System.Runtime.Serialization.SerializationException произошло в Server.exe. Дополнительная информация: не удалось найти сборку« Клиент, версия = 1.0.0.0, культура = нейтральная, PublicKeyToken = null ».

который я решил с этим:

sealed class AllowAllAssemblyVersionsDeserializationBinder : SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {     
        String currentAssembly = Assembly.GetExecutingAssembly().FullName;

        // In this case we are always using the current assembly
        typeName = "Server.Kort";
        assemblyName = currentAssembly;

        // Get the type using the typeName and assemblyName
        Type typeToDeserialize = Type.GetType(String.Format("{0}, {1}",
            typeName, assemblyName));

        return typeToDeserialize;
    }
}

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

«Объект типа Server.Kort не может быть преобразован в тип Server.Kort+kortvalör

И я не знаю, как это исправить.


person Krysvac    schedule 12.05.2015    source источник
comment
Я отредактировал ваше название. См. Должны ли вопросы включать «теги» в свои заголовки?, где нет единого мнения, не следует.   -  person John Saunders    schedule 12.05.2015
comment
Но когда я десериализовал, я получил сообщение об отсутствии сборки или сообщение об этой ошибке.   -  person Mehrzad Chehraz    schedule 12.05.2015
comment
Я получил ошибку. Необработанное исключение типа «System.Runtime.Serialization.SerializationException» произошло в Server.exe. Дополнительная информация: не удалось найти сборку «Клиент, версия = 1.0.0.0, культура = нейтральная, PublicKeyToken = null».   -  person Krysvac    schedule 12.05.2015


Ответы (1)


Класс Kort на отправляющей стороне должен содержать экземпляр вложенного типа (возможно, enum?) С именем kortvalör. И поскольку BinaryFormatter сериализует публичные и частные поля вместо свойств, вложенный тип может быть полностью невидимым для внешнего мира, но все же сериализоваться.

Например, мне удалось воспроизвести ваше исключение "Object of type Server.Kort cannot be converted to type Server.Kort+kortvalör" с помощью вашего связывателя со следующим классом:

[Serializable]
public class Kort
{
    // Private enum that is invisible to the outside world.
    enum kortvalör 
    {
        Zero,
        One,
        Two,
        Three
    }

    kortvalör valör = kortvalör.Three;

    public int Value
    {
        get
        {
            return (int)valör;
        }
        set
        {
            // Check to make sure the incoming value is in a valid range.
            var newvalör = (kortvalör)value;
            if (Enum.IsDefined(typeof(kortvalör), newvalör))
                valör = newvalör;
            else
                valör = default(kortvalör);
        }
    }
}

При десериализации класса выше ваше связующее будет вызываться дважды: один раз с typeName для Kort, а затем один раз с именем типа "MyClientNamespace.Kort+kortvalör". Поскольку ваша связка игнорирует входящий typeName и возвращает typeof(Kort), это не удается.

У вас есть несколько вариантов решения этой проблемы:

  1. Извлеките ваш класс Kort в общую DLL и свяжите его как с отправляющим, так и с получающим приложениями. Тогда проблема уйдет.

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

person dbc    schedule 12.05.2015
comment
Большое спасибо, я сделал первый шаг, и он отлично работает. - person Krysvac; 12.05.2015