Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSharpNotesForProfessionals.pdf
Скачиваний:
57
Добавлен:
20.05.2023
Размер:
6.12 Mб
Скачать

System.Runtime.Serialization.SerializationException was unhandled

Message=The ObjectManager found an invalid number of fixups. This usually indicates a problem in the Formatter.Source=mscorlib

StackTrace:

at System.Runtime.Serialization.ObjectManager.DoFixups()

at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)

at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)

at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream)

at Microsoft.Samples.TestV1.Main(String[] args) in c:\Users\andrew\Documents\Visual Studio 2013\Projects\vts\CS\V1 Application\TestV1Part2\TestV1Part2.cs:line 29

at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()

at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)

at System.Threading.ThreadHelper.ThreadStart()

Why?

The ObjectManager has a di erent logic to resolve dependencies for arrays and for reference and value types. We added an array of new the reference type which is absent in our assembly.

When ObjectManager attempts to resolve dependencies it builds the graph. When it sees the array, it can not fix it immediately, so that it creates a dummy reference and then fixes the array later.

And since this type is not in the assembly and dependencies can’t be fixed. For some reason, it does not remove the array from the list of elements for the fixes and at the end, it throws an exception “IncorrectNumberOfFixups”.

It is some ‘gotchas’ in the process of serialization. For some reason, it does not work correctly only for arrays of new reference types.

A Note:

Similar code will work correctly if you do not use arrays with new classes

And the first way to fix it and maintain compatibility?

Use a collection of new structures rather than classes or use a dictionary(possible classes), because a dictionary it’s a collection of keyvaluepair(it’s structure)

Use ISerializable, if you can't change the old code

Section 125.4: Making an object serializable

Add the [Serializable] attribute to mark an entire object for binary serialization:

[Serializable] public class Vector

{

public int X; public int Y; public int Z;

[NonSerialized]

public decimal DontSerializeThis;

GoalKicker.com – C# Notes for Professionals

635

[OptionalField] public string Name;

}

All members will be serialized unless we explicitly opt-out using the [NonSerialized] attribute. In our example, X, Y, Z, and Name are all serialized.

All members are required to be present on deserialization unless marked with [NonSerialized] or [OptionalField]. In our example, X, Y, and Z are all required and deserialization will fail if they are not present in the stream. DontSerializeThis will always be set to default(decimal) (which is 0). If Name is present in the stream, then it will be set to that value, otherwise it will be set to default(string) (which is null). The purpose of [OptionalField] is to provide a bit of version tolerance.

Section 125.5: Serialization surrogates (Implementing ISerializationSurrogate)

Implements a serialization surrogate selector that allows one object to perform serialization and deserialization of another

As well allows to properly serialize or deserialize a class that is not itself serializable

Implement ISerializationSurrogate interface

public class ItemSurrogate : ISerializationSurrogate

{

public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)

{

var item = (Item)obj; info.AddValue("_name", item.Name);

}

public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)

{

var item = (Item)obj;

item.Name = (string)info.GetValue("_name", typeof(string)); return item;

}

}

Then you need to let your IFormatter know about the surrogates by defining and initializing a SurrogateSelector and assigning it to your IFormatter

var surrogateSelector = new SurrogateSelector();

surrogateSelector.AddSurrogate(typeof(Item), new StreamingContext(StreamingContextStates.All), new ItemSurrogate());

var binaryFormatter = new BinaryFormatter

{

SurrogateSelector = surrogateSelector

};

Even if the class is not marked serializable.

//this class is not serializable public class Item

{

private string _name;

GoalKicker.com – C# Notes for Professionals

636

public string Name

{

get { return _name; } set { _name = value; }

}

}

The complete solution

using System; using System.IO;

using System.Runtime.Serialization;

using System.Runtime.Serialization.Formatters.Binary;

namespace BinarySerializationExample

{

class Item

{

private string _name;

public string Name

{

get { return _name; } set { _name = value; }

}

}

class ItemSurrogate : ISerializationSurrogate

{

public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)

{

var item = (Item)obj; info.AddValue("_name", item.Name);

}

public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)

{

var item = (Item)obj;

item.Name = (string)info.GetValue("_name", typeof(string)); return item;

}

}

class Program

{

static void Main(string[] args)

{

var item = new Item

{

Name = "Orange"

};

var bytes = SerializeData(item);

var deserializedData = (Item)DeserializeData(bytes);

}

private static byte[] SerializeData(object obj)

{

var surrogateSelector = new SurrogateSelector(); surrogateSelector.AddSurrogate(typeof(Item), new

GoalKicker.com – C# Notes for Professionals

637

StreamingContext(StreamingContextStates.All), new ItemSurrogate());

var binaryFormatter = new BinaryFormatter

{

SurrogateSelector = surrogateSelector

};

using (var memoryStream = new MemoryStream())

{

binaryFormatter.Serialize(memoryStream, obj); return memoryStream.ToArray();

}

}

private static object DeserializeData(byte[] bytes)

{

var surrogateSelector = new SurrogateSelector(); surrogateSelector.AddSurrogate(typeof(Item), new

StreamingContext(StreamingContextStates.All), new ItemSurrogate());

var binaryFormatter = new BinaryFormatter

{

SurrogateSelector = surrogateSelector

};

using (var memoryStream = new MemoryStream(bytes)) return binaryFormatter.Deserialize(memoryStream);

}

}

}

Section 125.6: Adding more control by implementing ISerializable

That would get more control over serialization, how to save and load types

Implement ISerializable interface and create an empty constructor to compile

[Serializable]

public class Item : ISerializable

{

private string _name;

public string Name

{

get { return _name; } set { _name = value; }

}

public Item ()

{

}

protected Item (SerializationInfo info, StreamingContext context)

{

_name = (string)info.GetValue("_name", typeof(string));

}

public void GetObjectData(SerializationInfo info, StreamingContext context)

GoalKicker.com – C# Notes for Professionals

638

{

info.AddValue("_name", _name, typeof(string));

}

}

For data serialization, you can specify the desired name and the desired type

info.AddValue("_name", _name, typeof(string));

When the data is deserialized, you will be able to read the desired type

_name = (string)info.GetValue("_name", typeof(string));

GoalKicker.com – C# Notes for Professionals

639