I created an extension method that will tell me the size of every object I create
like this:
public static int CalculateKilobytes(this object notSuspectingCandidate)
{
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, notSuspectingCandidate);
return stream.ToArray().Count() / 1000;
}
}
Since I'm using serializaion, not all objects will be able to return answer, only the serializble ones. Is there a way to attach this method to an object that can be serialized?
you can use Type.IsSerializable Property.
public static int CalculateKilobytes(this object notSuspectingCandidate)
{
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
if (notSuspectingCandidate.GetType().IsSerializable) {
formatter.Serialize(stream, notSuspectingCandidate);
return stream.ToArray().Count() / 1000;
}
return 0;
}
}
It's a really bad practice to serialize a object only to get its size, if you plan to serialize it again later.
Use with caution.
The extension method will be applied to all objects, you must check if it has the custom property in it.
This check can do the job.
if (notSuspectingCandidate.GetType().GetCustomAttributes(typeof(SerializableAttribute), true).Length == 0)
{
return -1; // An error
}
Another way is to put a extension method to ISerializable and use the interface in all your required types.
public static int CalculateKilobytes(this ISerializable notSuspectingCandidate)
{
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, notSuspectingCandidate);
return stream.ToArray().Count() / 1000;
}
}
Related
I have a method in my code behind to retrieve a get a pdf file from an API and return the byte[]
byte[] byteArray = response.Content.ReadAsByteArrayAsync().Result; ;
using (MemoryStream pdfStream = new MemoryStream())
{
pdfStream.Write(byteArray, 0, byteArray.Length);
pdfStream.Position = 0;
return new FileStreamResult(pdfStream, "application/pdf");
}
How in Blazor server to I create a link in my .razor component to consume this byte[] so that when the user clicks the link, it triggers the file download?
Your solution is close because you're creating the appropriate result, but you simply need the method that returns it.
Set up your API controller like the following:
[ApiController]
public class DownloadController : ControllerBase {
[HttpGet]
public ActionResult Get() {
byte[] byteArray = response.Content.ReadAsByteArrayAsync().Result; ;
using (MemoryStream pdfStream = new())
{
pdfStream.Write(byteArray, 0, byteArray.Length);
pdfStream.Position = 0;
var result = FileStreamResult(pdfStream, "application/pdf");
result.FileDownloadName = "sample.txt";
return result;
}
}
}
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.
I'm loading my document like so:
WebClient client = new WebClient();
client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);
client.OpenReadAsync(new Uri("Rolls.xml", UriKind.Relative));
Then on the Read Completed:
XDocument doc = XDocument.Load(XmlReader.Create(e.Result));
using (Stream stream = e.Result)
{
{
foreach (var roll in _rollsToAddStudentTo)
{
doc.Element("rolls").Add(new XElement("rollid", roll));
}
doc.Save(stream);
}
}
The problem is when it gets to the save I get the error
"Specified method is not supported."
Help will be much appreciated.
Cheers
Thanks Jehof,
So, how would I incorporate my document into that async method?
foreach (var roll in _rollsToAddStudentTo)
{
doc.Element("rolls").Add(new XElement("rollid", roll));
}
WebClient client = new WebClient();
client.OpenWriteCompleted += new OpenWriteCompletedEventHandler(client_OpenWriteCompleted);
client.OpenWriteAsync(new Uri("Rolls.xml", UriKind.Relative));
I have resolved this by changing my logic to below.
using (IsolatedStorageFile isoStore =
IsolatedStorageFile.GetUserStoreForApplication())
{
// Create new file
using (IsolatedStorageFileStream isoStream =
new IsolatedStorageFileStream("Rolls.xml",
FileMode.Create, isoStore))
{
// Write to the Isolated Storage for the user.
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
// Create an XmlWriter.
using (XmlWriter writer = XmlWriter.Create(isoStream, settings))
{
writer.WriteStartDocument();
writer.WriteStartElement("Rolls");
foreach (var roll in _rollsToAddStudentTo)
{
writer.WriteStartElement("roll");
writer.WriteAttributeString("rollid", roll);
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.WriteEndDocument();
}
}
}
The stream you try to save the XDocument is readonly. Cause it is the stream you get passed as argument to your method client_OpenReadCompleted that is registered to the event OpenReadCompleted.
If you want to save your XDocument back via WebClient you need to call one of the OpenWriteAsync-methods.
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
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?