protobuf-net NOT faster than binary serialization? - serialization
I wrote a program to serialize a 'Person' class using XMLSerializer, BinaryFormatter and ProtoBuf. I thought protobuf-net should be faster than the other two. Protobuf serialization was faster than XMLSerialization but much slower than the binary serialization. Is my understanding incorrect? Please make me understand this. Thank you for the help.
EDIT :- I changed the code (updated below) to measure the time only for the serialization and not creating the streams and still see the difference. Could one tell me why?
Following is the output:-
Person got created using protocol buffer in 347 milliseconds
Person got created using XML in 1462 milliseconds
Person got created using binary in 2 milliseconds
Code below
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ProtoBuf;
using System.IO;
using System.Diagnostics;
using System.Runtime.Serialization.Formatters.Binary;
namespace ProtocolBuffers
{
class Program
{
static void Main(string[] args)
{
string folderPath = #"E:\Ashish\Research\VS Solutions\ProtocolBuffers\ProtocolBuffer1\bin\Debug";
string XMLSerializedFileName = Path.Combine(folderPath,"PersonXMLSerialized.xml");
string ProtocolBufferFileName = Path.Combine(folderPath,"PersonProtocalBuffer.bin");
string BinarySerializedFileName = Path.Combine(folderPath,"PersonBinary.bin");
if (File.Exists(XMLSerializedFileName))
{
File.Delete(XMLSerializedFileName);
Console.WriteLine(XMLSerializedFileName + " deleted");
}
if (File.Exists(ProtocolBufferFileName))
{
File.Delete(ProtocolBufferFileName);
Console.WriteLine(ProtocolBufferFileName + " deleted");
}
if (File.Exists(BinarySerializedFileName))
{
File.Delete(BinarySerializedFileName);
Console.WriteLine(BinarySerializedFileName + " deleted");
}
var person = new Person
{
Id = 12345,
Name = "Fred",
Address = new Address
{
Line1 = "Flat 1",
Line2 = "The Meadows"
}
};
Stopwatch watch = Stopwatch.StartNew();
using (var file = File.Create(ProtocolBufferFileName))
{
watch.Start();
Serializer.Serialize(file, person);
watch.Stop();
}
//Console.WriteLine(watch.ElapsedMilliseconds.ToString());
Console.WriteLine("Person got created using protocol buffer in " + watch.ElapsedMilliseconds.ToString() + " milliseconds ");
watch.Reset();
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(person.GetType());
using (TextWriter w = new StreamWriter(XMLSerializedFileName))
{
watch.Start();
x.Serialize(w, person);
watch.Stop();
}
//Console.WriteLine(watch.ElapsedMilliseconds.ToString());
Console.WriteLine("Person got created using XML in " + watch.ElapsedMilliseconds.ToString() + " milliseconds");
watch.Reset();
using (Stream stream = File.Open(BinarySerializedFileName, FileMode.Create))
{
BinaryFormatter bformatter = new BinaryFormatter();
//Console.WriteLine("Writing Employee Information");
watch.Start();
bformatter.Serialize(stream, person);
watch.Stop();
}
//Console.WriteLine(watch.ElapsedMilliseconds.ToString());
Console.WriteLine("Person got created using binary in " + watch.ElapsedMilliseconds.ToString() + " milliseconds");
Console.ReadLine();
}
}
[ProtoContract]
[Serializable]
public class Person
{
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(2)]
public string Name { get; set; }
[ProtoMember(3)]
public Address Address { get; set; }
}
[ProtoContract]
[Serializable]
public class Address
{
[ProtoMember(1)]
public string Line1 { get; set; }
[ProtoMember(2)]
public string Line2 { get; set; }
}
}
I replied to your e-mail; I didn't realise you'd also posted it here. The first question I have is: which version of protobuf-net? The reason I ask is that the development trunk of "v2" deliberately has auto-compilation disabled, so that I can use my unit tests to test both the runtime and pre-compiled versions. So if you are using "v2" (only available in source), you need to tell it to compile the model - otherwise it is running 100% reflection.
In either "v1" or "v2" you can do this with:
Serializer.PrepareSerializer<Person>();
Having done this, the numbers I get (from the code in your e-mail; I haven't checked if the above is the same sample):
10
Person got created using protocol buffer in 10 milliseconds
197
Person got created using XML in 197 milliseconds
3
Person got created using binary in 3 milliseconds
The other factor is the repeats; 3-10ms is frankly nothing; you can't compare numbers around this level. Upping it to repeat 5000 times (re-using the XmlSerializer / BinaryFormatter instances; no false costs introduced) I get:
110
Person got created using protocol buffer in 110 milliseconds
329
Person got created using XML in 329 milliseconds
133
Person got created using binary in 133 milliseconds
Taking this to sillier extremes (100000):
1544
Person got created using protocol buffer in 1544 milliseconds
3009
Person got created using XML in 3009 milliseconds
3087
Person got created using binary in 3087 milliseconds
So ultimately:
when you have virtually no data to serialize, most approaches will be very fast (including protobuf-net)
as you add data, the differences become more obvious; protobuf generally excels here, either for individual large graphs, or lots of small graphs
Note also that in "v2" the compiled model can be fully static-compiled (to a dll that you can deploy), removing even the (already small) spin-up costs.
I have a slightly different opinion than the marked answer. I think the numbers from these tests reflects the meta-data overhead of binary formatter. BinaryFormatter writes meta-data about the class first before writing data, while protobuf writes only data.
For the very small object (one Person object) in your test, the meta-data cost of binary formatter weighs more than real cases, because it is writing more meta-data than data. So, when you increase the repeat count, the meta-data cost is exaggerated, up to the same level as xml serialization in extreme case.
If you serialize a Person array, and the array is large enough, then the meta-data cost will be trivial to the total cost. Then binary formatter should perform similar to protobuf for your extreme repeat test.
PS: I found this page because I'm evaluating different serializers. I also found a blog http://blogs.msdn.com/b/youssefm/archive/2009/07/10/comparing-the-performance-of-net-serializers.aspx which shows test result that DataContractSerializer + binary XmlDictionaryWriter performs several times better than binary formatter. It also tested with very small data. When I did the test myself with large data, I was surprised to find the result was very different. So do test with real data you will actually use.
We serialize quite large objects (about 50 properties) constantly, so I've written a small test to compare BinaryFormatter and protobuf-net, just as you did and here are my results (10000 objects):
BinaryFormatter serialize: 316
BinaryFormatter deserialize: 279
protobuf serialize: 243
protobuf deserialize: 139
BinaryFormatter serialize: 315
BinaryFormatter deserialize: 281
protobuf serialize: 127
protobuf deserialize: 110
That's obviously a very noticeable difference. It is also much faster on the second run (the tests are exactly the same) than it is on the first.
Update. Doing RuntimeTypeModel.Add..Compile generates following results:
BinaryFormatter serialize: 303
BinaryFormatter deserialize: 282
protobuf serialize: 113
protobuf deserialize: 50
BinaryFormatter serialize: 317
BinaryFormatter deserialize: 266
protobuf serialize: 126
protobuf deserialize: 49
If we compare in memory, hard-coded serialization will be quite faster in some situations.
If your class simple, maybe better will write your own serializer...
Slightly modified code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ProtoBuf;
using System.IO;
using System.Diagnostics;
using System.Runtime.Serialization.Formatters.Binary;
namespace ProtocolBuffers
{
class Program
{
static void Main(string[] args)
{
string folderPath = #"../Debug";
string XMLSerializedFileName = Path.Combine(folderPath, "PersonXMLSerialized.xml");
string ProtocolBufferFileName = Path.Combine(folderPath, "PersonProtocalBuffer.bin");
string BinarySerializedFileName = Path.Combine(folderPath, "PersonBinary.bin");
string BinarySerialized2FileName = Path.Combine(folderPath, "PersonBinary2.bin");
if (File.Exists(XMLSerializedFileName))
{
File.Delete(XMLSerializedFileName);
Console.WriteLine(XMLSerializedFileName + " deleted");
}
if (File.Exists(ProtocolBufferFileName))
{
File.Delete(ProtocolBufferFileName);
Console.WriteLine(ProtocolBufferFileName + " deleted");
}
if (File.Exists(BinarySerializedFileName))
{
File.Delete(BinarySerializedFileName);
Console.WriteLine(BinarySerializedFileName + " deleted");
}
if (File.Exists(BinarySerialized2FileName))
{
File.Delete(BinarySerialized2FileName);
Console.WriteLine(BinarySerialized2FileName + " deleted");
}
var person = new Person
{
Id = 12345,
Name = "Fred",
Address = new Address
{
Line1 = "Flat 1",
Line2 = "The Meadows"
}
};
Stopwatch watch = Stopwatch.StartNew();
using (var file = new MemoryStream())
//using (var file = File.Create(ProtocolBufferFileName))
{
watch.Start();
for (int i = 0; i < 100000; i++)
Serializer.Serialize(file, person);
watch.Stop();
}
Console.WriteLine("Person got created using protocol buffer in " + watch.ElapsedMilliseconds.ToString() + " milliseconds ");
watch.Reset();
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(person.GetType());
using (var w = new MemoryStream())
//using (TextWriter w = new StreamWriter(XMLSerializedFileName))
{
watch.Start();
for (int i = 0; i < 100000; i++)
x.Serialize(w, person);
watch.Stop();
}
Console.WriteLine("Person got created using XML in " + watch.ElapsedMilliseconds.ToString() + " milliseconds");
watch.Reset();
using (var stream = new MemoryStream())
//using (Stream stream = File.Open(BinarySerializedFileName, FileMode.Create))
{
BinaryFormatter bformatter = new BinaryFormatter();
watch.Start();
for (int i = 0; i < 100000; i++)
bformatter.Serialize(stream, person);
watch.Stop();
}
Console.WriteLine("Person got created using binary in " + watch.ElapsedMilliseconds.ToString() + " milliseconds");
watch.Reset();
using (var stream = new MemoryStream())
//using (Stream stream = File.Open(BinarySerialized2FileName, FileMode.Create))
{
BinaryWriter writer = new BinaryWriter(stream);
watch.Start();
for (int i = 0; i < 100000; i++)
writer.Write(person.GetBytes());
watch.Stop();
}
Console.WriteLine("Person got created using binary2 in " + watch.ElapsedMilliseconds.ToString() + " milliseconds");
Console.ReadLine();
}
}
[ProtoContract]
[Serializable]
public class Person
{
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(2)]
public string Name { get; set; }
[ProtoMember(3)]
public Address Address { get; set; }
public byte[] GetBytes()
{
using (var stream = new MemoryStream())
{
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(this.Id);
writer.Write(this.Name);
writer.Write(this.Address.GetBytes());
return stream.ToArray();
}
}
public Person()
{
}
public Person(byte[] bytes)
{
using (var stream = new MemoryStream(bytes))
{
BinaryReader reader = new BinaryReader(stream);
Id = reader.ReadInt32();
Name = reader.ReadString();
int bytesForAddressLenght = (int)(stream.Length - stream.Position);
byte[] bytesForAddress = new byte[bytesForAddressLenght];
Array.Copy(bytes, (int)stream.Position, bytesForAddress, 0, bytesForAddressLenght);
Address = new Address(bytesForAddress);
}
}
}
[ProtoContract]
[Serializable]
public class Address
{
[ProtoMember(1)]
public string Line1 { get; set; }
[ProtoMember(2)]
public string Line2 { get; set; }
public byte[] GetBytes()
{
using(var stream = new MemoryStream())
{
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(this.Line1);
writer.Write(this.Line2);
return stream.ToArray();
}
}
public Address()
{
}
public Address(byte[] bytes)
{
using(var stream = new MemoryStream(bytes))
{
BinaryReader reader = new BinaryReader(stream);
Line1 = reader.ReadString();
Line2 = reader.ReadString();
}
}
}
}
and my results:
Person got created using protocol buffer in 141 milliseconds
Person got created using XML in 676 milliseconds
Person got created using binary in 525 milliseconds
Person got created using binary2 in 79 milliseconds
Related
How does C# know the length of string using Binary Writer?
Please look at the code below. This program simply saves a 33-character-length string "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" with an additional byte value of "33". using System.Text; namespace Test { internal class Program { static void Main(string[] args) { string filepath = args[0]; using (var stream = File.Open(filepath, FileMode.Create)) { using (var writer = new BinaryWriter(stream, Encoding.UTF8, false)) { writer.Write(new string('!', 33)); writer.Write((byte)33); } } using (var stream = File.Open(filepath, FileMode.Open)) { using (var reader = new BinaryReader(stream, Encoding.UTF8, false)) { Console.WriteLine(reader.ReadString()); Console.WriteLine(reader.ReadByte()); } } Console.ReadKey(); } } } And here is the binary representation of it: Apparently, the first starting "ox21" is the length of the string - but how on earth does C# know?
The IsolatedStorageSettings.Save method in Windows Phone: does it save the whole dictionary?
Does the IsolatedStorageSettings.Save method in a Windows Phone application save the whole dictionary regardless of the changes we made in it? I.e. if we have say 50 items in it, and change just one, does the Save method saves (serializes, etc) the whole dictionary again and again? Is there any detailed documentation on this class and does anybody know what data storage format is used "under the hood"?
I've managed to find the implementation of the IsolatedStorageSettings.Save method in the entrails of the Windows Phone emulator VHD images supplied with the Windows Phone SDK (the answer to this question on SO helped me to do that). Here is the source code of the method: public void Save() { lock (this.m_lock) { using (IsolatedStorageFileStream isolatedStorageFileStream = this._appStore.OpenFile(this.LocalSettingsPath, 4)) { using (MemoryStream memoryStream = new MemoryStream()) { Dictionary<Type, bool> dictionary = new Dictionary<Type, bool>(); StringBuilder stringBuilder = new StringBuilder(); using (Dictionary<string, object>.ValueCollection.Enumerator enumerator = this._settings.get_Values().GetEnumerator()) { while (enumerator.MoveNext()) { object current = enumerator.get_Current(); if (current != null) { Type type = current.GetType(); if (!type.get_IsPrimitive() && type != typeof(string)) { dictionary.set_Item(type, true); if (stringBuilder.get_Length() > 0) { stringBuilder.Append('\0'); } stringBuilder.Append(type.get_AssemblyQualifiedName()); } } } } stringBuilder.Append(Environment.get_NewLine()); byte[] bytes = Encoding.get_UTF8().GetBytes(stringBuilder.ToString()); memoryStream.Write(bytes, 0, bytes.Length); DataContractSerializer dataContractSerializer = new DataContractSerializer(typeof(Dictionary<string, object>), dictionary.get_Keys()); dataContractSerializer.WriteObject(memoryStream, this._settings); if (memoryStream.get_Length() > this._appStore.get_AvailableFreeSpace() + isolatedStorageFileStream.get_Length()) { throw new IsolatedStorageException(Resx.GetString("IsolatedStorageSettings_NotEnoughSpace")); } isolatedStorageFileStream.SetLength(0L); byte[] array = memoryStream.ToArray(); isolatedStorageFileStream.Write(array, 0, array.Length); } } } } So, as we can see the whole dictionary is serialized every time when we call Save. And we can see from code what method is used to serialize the collection values.
How to encrypt the query string ID in MVC 4 ActionLink
How can I pass the encrypted id in ActionLink. This is what I have written in my view: #model IEnumerable<forumAPP.tblTechnology> #foreach (var item in Model) { string techName=item.TechName; #Html.ActionLink(techName, "Details","Home", new { TopicID = item.TechID },null) // Here I would like to encrypt the TopicID <br /> <br /> #Html.DisplayFor(modelItem => item.TechDesc) }
Here are a couple of simple methods you can use to encode/decode. The encoded value is not secure, and as you can see, decoding it is trivial. If your goal is to obfuscate the id, this will work. If you need to secure it, you should take a different approach. public string Encode( string encodeMe ) { byte[] encoded = System.Text.Encoding.UTF8.GetBytes( encodeMe ); return Convert.ToBase64String( encoded ); } public static string Decode( string decodeMe ) { byte[] encoded = Convert.FromBase64String( decodeMe ); return System.Text.Encoding.UTF8.GetString( encoded ); } So you could place these methods in your controller, and pass the encoded TechId to the view with viewBag int techId = 1; var encoded = Encode(id.ToString()); ViewBag.Encoded = encoded; And then to use it in your link #Html.ActionLink(techName, "Details","Home", new { TopicID = ViewBag.Encoded },null) (Though, you should really consider using a view model. ViewBag, while a convienent and easy way to pass data to the view, is not considered to be best practice. Becoming comfortable with view models and strongly typed views will make your mvc life much easier in the future. Not to mention, produce cleaner and more maintainable code for those that follow you.)
Add A Folder with two classes Class 1 : EncryptedActionParameterAttribute using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Web; using System.Web.Mvc; namespace MVCInvoicClient.Extensions { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class EncryptedActionParameterAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { Dictionary<string, object> decryptedParameters = new Dictionary<string, object>(); if (HttpContext.Current.Request.QueryString.Get("q") != null) { string encryptedQueryString = HttpContext.Current.Request.QueryString.Get("q"); string decrptedString = Decrypt(encryptedQueryString.ToString()); string[] paramsArrs = decrptedString.Split('?'); for (int i = 0; i < paramsArrs.Length; i++) { string[] paramArr = paramsArrs[i].Split('='); decryptedParameters.Add(paramArr[0], Convert.ToInt32(paramArr[1])); } } for (int i = 0; i < decryptedParameters.Count; i++) { filterContext.ActionParameters[decryptedParameters.Keys.ElementAt(i)] = decryptedParameters.Values.ElementAt(i); } base.OnActionExecuting(filterContext); } private string Decrypt(string encryptedText) { string key = "jdsg432387#"; byte[] DecryptKey = { }; byte[] IV = { 55, 34, 87, 64, 87, 195, 54, 21 }; byte[] inputByte = new byte[encryptedText.Length]; DecryptKey = System.Text.Encoding.UTF8.GetBytes(key.Substring(0, 8)); DESCryptoServiceProvider des = new DESCryptoServiceProvider(); inputByte = Convert.FromBase64String(encryptedText); MemoryStream ms = new MemoryStream(); CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(DecryptKey, IV), CryptoStreamMode.Write); cs.Write(inputByte, 0, inputByte.Length); cs.FlushFinalBlock(); System.Text.Encoding encoding = System.Text.Encoding.UTF8; return encoding.GetString(ms.ToArray()); } } } Class 2 : MyExtensions using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Web; using System.Web.Mvc; using System.Web.Routing; namespace MVCInvoicClient.Extensions { public static class MyExtensions { public static MvcHtmlString EncodedActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes) { string queryString = string.Empty; string htmlAttributesString = string.Empty; if (routeValues != null) { RouteValueDictionary d = new RouteValueDictionary(routeValues); for (int i = 0; i < d.Keys.Count; i++) { if (i > 0) { queryString += "?"; } queryString += d.Keys.ElementAt(i) + "=" + d.Values.ElementAt(i); } } if (htmlAttributes != null) { RouteValueDictionary d = new RouteValueDictionary(htmlAttributes); for (int i = 0; i < d.Keys.Count; i++) { htmlAttributesString += " " + d.Keys.ElementAt(i) + "=" + d.Values.ElementAt(i); } } //What is Entity Framework?? StringBuilder ancor = new StringBuilder(); ancor.Append("<a "); if (htmlAttributesString != string.Empty) { ancor.Append(htmlAttributesString); } ancor.Append(" href='"); if (controllerName != string.Empty) { ancor.Append("/" + controllerName); } if (actionName != "Index") { ancor.Append("/" + actionName); } if (queryString != string.Empty) { ancor.Append("?q=" + Encrypt(queryString)); } ancor.Append("'"); ancor.Append(">"); ancor.Append(linkText); ancor.Append("</a>"); return new MvcHtmlString(ancor.ToString()); } private static string Encrypt(string plainText) { string key = "jdsg432387#"; byte[] EncryptKey = { }; byte[] IV = { 55, 34, 87, 64, 87, 195, 54, 21 }; EncryptKey = System.Text.Encoding.UTF8.GetBytes(key.Substring(0, 8)); DESCryptoServiceProvider des = new DESCryptoServiceProvider(); byte[] inputByte = Encoding.UTF8.GetBytes(plainText); MemoryStream mStream = new MemoryStream(); CryptoStream cStream = new CryptoStream(mStream, des.CreateEncryptor(EncryptKey, IV), CryptoStreamMode.Write); cStream.Write(inputByte, 0, inputByte.Length); cStream.FlushFinalBlock(); return Convert.ToBase64String(mStream.ToArray()); } } } Controller Add this line above the controller class Example for your Index [EncryptedActionParameter] In your View #Html.EncodedActionLink("Download Invoice", "FileDownload","DataFiles", new { id = item.DataFilesID }, null) add a using statement #using MVCInvoicClient.Extensions
I came across this while looking for a secure method of doing this. In case someone else wishes to do this securely, you can use the MvcSerializer (I found it in the MVC futures 3 project, I am unsure whether it is included in MVC 4). For example: (new MvcSerializer()).Serialize(<Your data here>, SerializationMode.EncryptedAndSigned) And then to reverse the process... (new MvcSerializer()).Deserialize(<Serialized data here>, SerializationMode.EncryptedAndSigned) This is great because without any extra effort it encrypts and signs the data. The futures project also includes some attributes to make this happen automatically during model binding.
SerializeObject and DeserializeObject
I use this following code but it gives error Service1.svc.cs public static byte[] SerializeObject<T>(T obj) { using (MemoryStream ms = new MemoryStream()) { using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(ms)) { DataContractSerializer dcs = new DataContractSerializer(typeof(T)); dcs.WriteObject(writer, obj); writer.Flush(); return ms.ToArray(); } } } client code Windows phone public static T DeserializeObject<T>(byte[] xml) { using (MemoryStream memoryStream = new MemoryStream(xml)) { using (XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(memoryStream, XmlDictionaryReaderQuotas.Max)) { DataContractSerializer dcs = new DataContractSerializer(typeof(T)); return (T)dcs.ReadObject(reader); } } } I call this DeserializeObject from below code void svc_Get_Conn(object send, GetConnCompletedEventArgs e) { CookieContainer con =DeserializeObject<CookieContainer>(e.Result); } This gives following error Message = "Type 'System.Net.PathList' with data contract name 'PathList:http://schemas.datacontract.org/2004/07/System.Net' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using... How to solve this?
CookieContainer can't be serializable. Check this workaround cheers
Protobuf-net Extension interfaces
I have a problem understanding protobuf-net extensions and how they exactly work. I need to serialize a 2D custom class array and so I decited to wrap it into a class something like: class CustomData : IExtension { private CustomClass[,] data; public CustomClass this[int index_X, int index_Y] { get { return data[index_X, index_Y]; } set { data[index_X, index_Y] = value; } } public Stream BeginQuery() { var stream = new MemoryStream(); int pos = 0; byte[] packedData = SomeStaticClass.ConvertToByteArray(data) using(var writer = new ProtoWriter(stream, null, null)) { ProtoWriter.WriteFieldHeader(1, WireType.String, writer); ProtoWriter.WriteBytes(packedData, writer); } return stream; } public void EndQuery(Stream stream) { stream.Close(); } //... and the other 3 implemented funcs from IExtension } So this ofcourse is in a custom IExtensible which has GetExtensionObject() returning an instance of the CustomData object. The problem comes when I try to append extension data to an instance. Here is what I'm doing: public void DoAppend() { var stream = new MemoryStream(); using (ProtoWriter writer = new ProtoWriter(stream, null, null)) { var test = new CustomExtensibleClass(300, 300); ProtoWriter.AppendExtensionData(test, writer); } var result = stream.ToArray(); } The problem is that "result" contains no data. I expected that the data I appended and written via BeginQuery() will be transfered to the stream of the ProtoWriter but I suppose this is not the case. Can somebody explaing what I am doing wrong or at least how can use the appended data?