What causes UI hiccups when using C# BinaryFormatter.Deserialize? - c#

I'm using a BinaryFormatter to serialize and deserialize a list of items into byte[] arrays, and I've noticed that for a significant number of elements in the list my UI will hang or have "hiccups". When I say significant, I'm talking about 10K items (each with a collection of items of their own).
What is interesting is that the serialization and deserialization occurs on a separate thread, so I would not have thought UI interruptions would have occurred. I was just curious if anyone has dealt with anything similar, and if there are any workarounds.
The code snippets below are from the stripped down sol'n that I'm testing with. While I'm using the BinaryFormatter to read and write to disk here as well, the part I'm more interested in is what is in the SerializationHelper class (where it is being done in memory).
I also realize that the UI thread will be interrupted to post status updates, however those are negligible. I'm noticing the UI hangs while BinaryFormatter.Deserialize is executing and nothing is being updated on the UI.
DataReader:
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace SerializationTesting
{
internal class DataReader : DataIOBase
{
#region Constants
private const int MAX_STEPS = 2;
#endregion
#region Declarations
private string _file;
#endregion
public DataReader(string file)
{
_file = file;
}
protected override void DoWork()
{
UpdateStatus(0, MAX_STEPS, "Reading from file...");
byte[] serializedData = ReadFromDisk();
UpdateStatus(1, MAX_STEPS, "Deserializing data...");
if (serializedData == null) return;
DeserializeData(serializedData);
UpdateStatus(2, MAX_STEPS, "Finished!");
}
private byte[] ReadFromDisk()
{
byte[] serializedData = null;
using (FileStream stream = new FileStream(_file, FileMode.Open, FileAccess.Read))
{
using (BufferedStream bufferedStream = new BufferedStream(stream))
{
BinaryFormatter formatter = new BinaryFormatter();
serializedData = formatter.Deserialize(bufferedStream) as byte[];
}
}
return serializedData;
}
private void DeserializeData(byte[] serializedData)
{
List<Data> dataList = SerializationHelper.Deserialize(serializedData, new List<Data>());
dataList.Clear();
dataList = null;
serializedData = null;
}
}
}
DataWriter:
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace SerializationTesting
{
internal class DataWriter : DataIOBase
{
#region Declarations
private string _file;
private int _count;
#endregion
public DataWriter(string file, int count)
{
_file = file;
_count = count;
}
protected override void DoWork()
{
int maxSteps = _count + 2;
UpdateStatus(0, maxSteps, "Creating data...");
List<Data> dataList = CreateData(maxSteps);
UpdateStatus(_count, maxSteps, "Serializing data...");
byte[] serializedData = SerializationHelper.Serialize(dataList, null);
UpdateStatus(_count + 1, maxSteps, "Writing to file...");
WriteToDisk(serializedData, maxSteps);
serializedData = null;
dataList.Clear();
dataList = null;
UpdateStatus(maxSteps, maxSteps, "Finished!");
}
private List<Data> CreateData(int maxSteps)
{
List<Data> dataList = new List<Data>();
for (int i = 0; i < _count; i++)
{
UpdateStatus(i, maxSteps, string.Format("Creating item {0}...", i + 1));
Data data = new Data();
dataList.Add(data);
}
return dataList;
}
private void WriteToDisk(byte[] serializedData, int maxSteps)
{
using (FileStream stream = new FileStream(_file, FileMode.Create, FileAccess.Write))
{
using (BufferedStream bufferedStream = new BufferedStream(stream))
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(bufferedStream, serializedData);
}
}
}
}
}
DataIOBase:
using System.Diagnostics;
using System.Threading;
namespace SerializationTesting
{
internal abstract class DataIOBase
{
#region Declarations
private Thread _thread;
#endregion
#region Events
public event UpdateStatusHandler OnStatusChange;
public event ProcessCompleteHandler OnComplete;
#endregion
public void Start()
{
KillThread();
_thread = new Thread(new ThreadStart(ThreadBody));
_thread.Start();
}
private void KillThread()
{
if (_thread == null) return;
if (!_thread.IsAlive) return;
try
{
_thread.Abort();
}
catch { }
}
private void ThreadBody()
{
Stopwatch sw = new Stopwatch();
sw.Start();
try
{
DoWork();
}
catch { }
finally
{
sw.Stop();
if (OnComplete != null)
{
OnComplete(sw.ElapsedMilliseconds);
}
}
}
protected abstract void DoWork();
protected void UpdateStatus(int curr, int max, string status)
{
if (OnStatusChange == null) return;
OnStatusChange(curr, max, status);
}
}
}
Data:
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Security.Permissions;
namespace SerializationTesting
{
[Serializable]
public class Data : ISerializable
{
#region Static Members
private static readonly int COLLECTION_SIZE_MIN = 1;
private static readonly int COLLECTION_SIZE_MAX = 20;
private static Random _rand = null;
private static Random Rand
{
get
{
if (_rand == null)
{
_rand = new Random();
}
return _rand;
}
}
#endregion
#region Instance Properties
public string Name1 { get; set; }
public string Name2 { get; set; }
public string Name3 { get; set; }
public string Name4 { get; set; }
public string Name5 { get; set; }
public int Num1 { get; set; }
public int Num2 { get; set; }
public int Num3 { get; set; }
public int Num4 { get; set; }
public int Num5 { get; set; }
public bool Bool1 { get; set; }
public bool Bool2 { get; set; }
public bool Bool3 { get; set; }
public bool Bool4 { get; set; }
public bool Bool5 { get; set; }
public Dictionary<int, Data> Collection { get; set; }
#endregion
public Data(bool createCollection = true)
{
Init(createCollection);
}
#region Init
private void Init(bool createCollection)
{
try
{
Name1 = CreateString();
Name2 = CreateString();
Name3 = CreateString();
Name4 = CreateString();
Name5 = CreateString();
Num1 = CreateInt();
Num2 = CreateInt();
Num3 = CreateInt();
Num4 = CreateInt();
Num5 = CreateInt();
Bool1 = CreateBool();
Bool2 = CreateBool();
Bool3 = CreateBool();
Bool4 = CreateBool();
Bool5 = CreateBool();
CreateCollection(createCollection);
}
catch { }
}
private string CreateString()
{
int length = Rand.Next(1, 31);
char[] value = new char[length];
for (int i = 0; i < length; i++)
{
int charValue = Rand.Next(48, 91);
value[i] = (char)i;
}
return new string(value);
}
private int CreateInt()
{
return Rand.Next(1, 11);
}
private bool CreateBool()
{
int value = Rand.Next(0, 2);
return value == 0 ? false : true;
}
private void CreateCollection(bool populateCollection)
{
Collection = new Dictionary<int, Data>();
if (!populateCollection) return;
int count = Rand.Next(COLLECTION_SIZE_MIN, COLLECTION_SIZE_MAX + 1);
for (int i = 0; i < count; i++)
{
Data data = new Data(false);
Collection.Add(i, data);
}
}
#endregion
#region Serialization
public Data(SerializationInfo info, StreamingContext context)
{
SerializationHelper sh = new SerializationHelper(info);
Name1 = sh.GetItem("Name1", string.Empty);
Name2 = sh.GetItem("Name2", string.Empty);
Name3 = sh.GetItem("Name3", string.Empty);
Name4 = sh.GetItem("Name4", string.Empty);
Name5 = sh.GetItem("Name5", string.Empty);
Num1 = sh.GetItem("Num1", -1);
Num2 = sh.GetItem("Num2", -1);
Num3 = sh.GetItem("Num3", -1);
Num4 = sh.GetItem("Num4", -1);
Num5 = sh.GetItem("Num5", -1);
Bool1 = sh.GetItem("Bool1", false);
Bool2 = sh.GetItem("Bool2", false);
Bool3 = sh.GetItem("Bool3", false);
Bool4 = sh.GetItem("Bool4", false);
Bool5 = sh.GetItem("Bool5", false);
Collection = sh.GetItem("Collection", new Dictionary<int, Data>());
}
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Name1", Name1);
info.AddValue("Name2", Name2);
info.AddValue("Name3", Name3);
info.AddValue("Name4", Name4);
info.AddValue("Name5", Name5);
info.AddValue("Num1", Num1);
info.AddValue("Num2", Num2);
info.AddValue("Num3", Num3);
info.AddValue("Num4", Num4);
info.AddValue("Num5", Num5);
info.AddValue("Bool1", Bool1);
info.AddValue("Bool2", Bool2);
info.AddValue("Bool3", Bool3);
info.AddValue("Bool4", Bool4);
info.AddValue("Bool5", Bool5);
info.AddValue("Collection", Collection);
}
#endregion
}
}
SerializationHelper:
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace SerializationTesting
{
public class SerializationHelper
{
#region Declarations
private SerializationInfo _serializationInfo;
#endregion
public SerializationHelper(SerializationInfo serializationInfo)
{
_serializationInfo = serializationInfo;
}
#region Get Item
public T GetItem<T>(string item, T defaultValue)
{
try
{
object value = _serializationInfo.GetValue(item, typeof(T));
return (T)value;
}
catch
{
return defaultValue;
}
}
#endregion
#region Binary Serialization
public static T Deserialize<T>(byte[] serializedObject, T defaultValue, SerializationBinder binder = null)
{
if (serializedObject == null) return defaultValue;
try
{
object deserializedObject;
BinaryFormatter binaryFormatter = new BinaryFormatter();
if (binder != null)
{
binaryFormatter.Binder = binder;
}
using (MemoryStream stream = new MemoryStream(serializedObject))
{
stream.Seek(0, 0);
deserializedObject = binaryFormatter.Deserialize(stream);
}
if (!(deserializedObject is T))
{
return defaultValue;
}
return (T)deserializedObject;
}
catch
{
return defaultValue;
}
}
public static byte[] Serialize(object o, byte[] defaultValue, SerializationBinder binder = null)
{
if (o == null) return null;
try
{
byte[] serializedObject;
BinaryFormatter binaryFormatter = new BinaryFormatter();
if (binder != null)
{
binaryFormatter.Binder = binder;
}
using (MemoryStream stream = new MemoryStream())
{
binaryFormatter.Serialize(stream, o);
serializedObject = stream.ToArray();
}
return serializedObject;
}
catch
{
return defaultValue;
}
}
#endregion
}
}

FYI, as of Nov 2020, MS recommends not using using BinaryFormatter in your code.
Instead, consider using JsonSerializer or XmlSerializer. For more information, see BinaryFormatter security guide.

Related

how to write json file with size limit (100KB) and once size cross, write new file?

I have a blocking collection which gets filled with data by some app1.
I have subscribed to that blocking collection and need to write a file with below case,
start writing a file .
if the file size crossed 100kb, close the first file and starts a new file.
and if there is no data coming from app1, lets say for 1 minute, then close the file.
Currently with below code, I'm only able to write per blocking collection into per file, how to proceed with my above requirement, please suggest.
class Program
{
private static BlockingCollection<Message> messages = new BlockingCollection<Message>();
private static void Producer()
{
int ctr = 1;
while (true)
{
messages.Add(new Message { Id = ctr, Name = $"Name-{ctr}" });
Thread.Sleep(1000);
ctr++;
}
}
private static void Consumer()
{
foreach (var message in messages.GetConsumingEnumerable())
{
Console.WriteLine(JsonConvert.SerializeObject(message));
using (var streamWriter = File.CreateText(Path.Combine(#"C:\TEMP", $"File-{ DateTime.Now.ToString("yyyyMMddHHmmssfff")}.json")))
{
using (var writer = new JsonTextWriter(streamWriter))
{
writer.Formatting = Formatting.Indented;
writer.WriteStartObject();
writer.WritePropertyName("Data");
writer.WriteStartArray();
writer.WriteRawValue(JsonConvert.SerializeObject(message));
writer.WriteEndArray();
writer.WriteEndObject();
}
}
}
}
static void Main(string[] args)
{
var producer = Task.Factory.StartNew(() => Producer());
var consumer = Task.Factory.StartNew(() => Consumer());
Console.Read();
}
}
This will take the messages with any size and divided it to a different JSON file. You need to specify the size using maxchar. before that, you have to check the size of the last file like this and you have to pass the same file name and the new size if else create a new file and divide the message.
using System;
using System.IO;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Collections;
using Newtonsoft.Json;
using System.Runtime.Remoting.Messaging;
using System.Text;
namespace Program
{
class Program
{
public static string last_path = "";
public static readonly string BYE = "bye";
private static BlockingCollection<Message> messages = new BlockingCollection<Message>();
private static void Producer()
{
int ctr = 1;
while (true)
{
messages.Add(new Message { Id = ctr, Name = $"Name-{ctr}" });
Thread.Sleep(1000);
ctr++;
}
}
private static void Consumer()
{
foreach (var message in messages.GetConsumingEnumerable())
{
Console.WriteLine(JsonConvert.SerializeObject(message));
string json = JsonConvert.SerializeObject(message);
int maxchar = 102400;
if (last_path != "")
{
long length = new FileInfo(last_path).Length;
if (length < maxchar)
{
maxchar = maxchar - unchecked((int)length);
dividefile(last_path, maxchar, json);
}
else
{
dividefile("", maxchar, json);
}
}
else
{
dividefile("", maxchar, json);
}
}
}
public static void dividefile(string path, int maxchar, string message)
{
//FileStream fileStream = new FileStream(yourfile, FileMode.Open, FileAccess.Read);
byte[] byteArray = Encoding.UTF8.GetBytes(message);
MemoryStream stream = new MemoryStream(byteArray);
using (StreamReader streamReader = new StreamReader(stream))
{
Int64 x1 = stream.Length;
char[] fileContents = new char[maxchar];
int charsRead = streamReader.Read(fileContents, 0, maxchar);
// Can't do much with 0 bytes
if (charsRead == 0)
throw new Exception("File is 0 bytes");
while (charsRead > 0)
{
x1 = x1 - maxchar;
if (x1 > 0)
{
string s = new string(fileContents);
if (path == "")
{
last_path = Path.Combine(#"C:\TEMP", $"File-{ DateTime.Now.ToString("yyyyMMddHHmmssfff")}.json");
path = "";
}
else
{
last_path = path;
}
AppendTransaction(last_path, s);
charsRead = streamReader.Read(fileContents, 0, maxchar);
}
else
{
int m = (int)(((x1 + maxchar) % maxchar));
string messagechunk = new string(fileContents, 0, m);
if (path == "")
{
last_path = Path.Combine(#"C:\TEMP", $"File-{ DateTime.Now.ToString("yyyyMMddHHmmssfff")}.json");
path = "";
}
else
{
last_path = path;
}
AppendTransaction(last_path, messagechunk);
charsRead = streamReader.Read(fileContents, 0, m);
}
}
}
}
private static void AppendTransaction(string path , string transaction)
{
string filename = path;
bool firstTransaction = !File.Exists(filename);
JsonSerializer ser = new JsonSerializer();
ser.Formatting = Formatting.Indented;
ser.TypeNameHandling = TypeNameHandling.Auto;
using (var fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read))
{
Encoding enc = firstTransaction ? new UTF8Encoding(true) : new UTF8Encoding(false);
using (var sw = new StreamWriter(fs, enc))
using (var jtw = new JsonTextWriter(sw))
{
if (firstTransaction)
{
sw.Write("[");
sw.Flush();
}
else
{
fs.Seek(-Encoding.UTF8.GetByteCount("]"), SeekOrigin.End);
sw.Write(",");
sw.Flush();
}
ser.Serialize(jtw, transaction);
sw.Write(']');
}
}
}
static void Main(string[] args)
{
var producer = Task.Factory.StartNew(() => Producer());
var consumer = Task.Factory.StartNew(() => Consumer());
Console.Read();
}
class Message
{
public int ProductorThreadID { get; set; }
public int CustomerThreadID { get; set; }
public string key { get; set; }
public string content { get; set; }
public string Name { get; internal set; }
public int Id { get; internal set; }
public bool endThread()
{
return string.Compare(key, Program.BYE) == 0;
}
public string ToString(bool isProductor)
{
return string.Format("{0} Thread ID {1} : {2}", isProductor ? "Productor" : "Customer",
isProductor ? ProductorThreadID.ToString() : CustomerThreadID.ToString(),
content);
}
}
}
}

Load and save to text from IList<T>

Here is my code -
class Appointments:IAppointments
{
private readonly IList<IAppointment> _list = new List<IAppointment>();
public Appointments()
{
}
public bool Load()
{
throw new NotImplementedException();
}
public bool Save()
{
throw new NotImplementedException();
}
public IEnumerable<IAppointment> GetAppointmentsOnDate(DateTime date)
{
throw new NotImplementedException();
}
public int IndexOf(IAppointment item)
{
return _list.IndexOf(item);
}
public void Insert(int index, IAppointment item)
{
_list.Insert(index, item);
}
public void RemoveAt(int index)
{
_list.RemoveAt(index);
}
public IAppointment this[int index]
{
get
{
return _list[index];
}
set
{
_list[index] = value;
}
}
public void Add(IAppointment item)
{
_list.Add(item);
}
public void Clear()
{
_list.Clear();
}
public bool Contains(IAppointment item)
{
return _list.Contains(item);
}
public void CopyTo(IAppointment[] array, int arrayIndex)
{
_list.CopyTo(array, arrayIndex);
}
public int Count
{
get { return _list.Count; }
}
public bool IsReadOnly
{
get { return _list.IsReadOnly; }
}
public bool Remove(IAppointment item)
{
return _list.Remove(item);
}
public IEnumerator<IAppointment> GetEnumerator()
{
return _list.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
foreach (IAppointment item in _list)
{
if (item == null)
{
break;
}
yield return item;
}
I would like help on how to do the load and save methods. The save method needs to save to a text file. The load method needs to load from a txt file.
IAppointment interface -
namespace Calendar
{
public interface IAppointment
{
DateTime Start { get; }
int Length { get; }
string DisplayableDescription { get; }
bool OccursOnDate(DateTime date);
}
}
The website is complaining that this is mostly code so I am going to write this pointless sentence until it hopefully goes away. Thank you for your patience.
This is the serialization code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace Calendar
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
//add to appointments to object below
Appointments appointments = new Appointments()
{
appointments = new List<Appointment>() {
new Appointment(){ start = DateTime.Today, length = 2, displayableDescription = "Meeting wiith Joe", occursOnDate = true},
new Appointment(){ start = DateTime.Today.AddDays(2), length = 3, displayableDescription = "Meeting wiith Jane", occursOnDate = false}
}
};
//use this code for writing
XmlSerializer serializer = new XmlSerializer(typeof(Appointments));
StreamWriter writer = new StreamWriter(FILENAME);
serializer.Serialize(writer, appointments);
writer.Flush();
writer.Close();
writer.Dispose();
//use this code for reading
XmlSerializer xs = new XmlSerializer(typeof(Appointments));
XmlTextReader reader = new XmlTextReader(FILENAME);
Appointments newAppointments = (Appointments)xs.Deserialize(reader);
}
}
[XmlRoot("appointments")]
public class Appointments
{
[XmlElement("appointment")]
public List<Appointment> appointments { get; set; }
}
[XmlRoot("appointment")]
public class Appointment
{
[XmlElement("start")]
public DateTime start { get; set; }
[XmlElement("length")]
public int length { get; set; }
[XmlElement("displayableDescription")]
public string displayableDescription { get; set; }
[XmlElement("occursOnDate")]
public bool occursOnDate { get; set; }
}
}
I would just use Json as it's much cleaner and smaller than XML. So for example:
private IList<IAppointment> _list {get; set;}
public Appointments()
{
_list = Load() ?? new List<IAppointment>();
}
public List<IAppointment> Load()
{
try
{
var json = File.ReadAllText(".Appointments.json");
var list = JsonConvert.DeserializeObject<List<IAppointment>>(json);
return list;
}
catch
{
return null;
}
}
public bool Save()
{
try
{
var json = JsonConvert.SerializeObject(_list);
File.WriteAllText(".Appointments.json",json);
return true;
}
catch
{
return false;
}
}
Don't forget to add Newtonsoft.Json to your project via Nuget:
To install Json.NET, run the following command in the Package Manager Console
PM> Install-Package Newtonsoft.Json -Version 6.0.8
I combined my code into your classes. Will need to be debugged but should help you understand serialization better.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace Calendar
{
class Program
{
static void Main(string[] args)
{
}
}
public class IAppointments : IAppointment
{
public DateTime Start { get; set; }
public int Length { get; set; }
public string DisplayableDescription { get; set; }
public bool OccursOnDate(DateTime date)
{
return true;
}
}
class Appointments : IAppointments
{
const string FILENAME = #"c:\temp\test.xml";
private readonly IList<IAppointment> _list = new List<IAppointment>();
public void Add(IAppointment item)
{
_list.Add(item);
}
public bool Load()
{
XmlSerializer xs = new XmlSerializer(typeof(c_Appointments));
XmlTextReader reader = new XmlTextReader(FILENAME);
c_Appointments newAppointments = (c_Appointments)xs.Deserialize(reader);
foreach (Appointment appointment in newAppointments.appointments)
{
IAppointments newAppointment = new IAppointments();
newAppointment.Start = appointment.start;
newAppointment.Length = appointment.length;
newAppointment.DisplayableDescription = appointment.displayableDescription;
this.Add(newAppointment);
}
return true;
}
public bool Save()
{
c_Appointments appointments = new c_Appointments();
appointments.appointments = new List<Appointment>();
foreach (IAppointment iAppointment in _list)
{
Appointment newAppoint = new Appointment();
appointments.appointments.Add(newAppoint);
newAppoint = (Appointment)iAppointment;
}
XmlSerializer serializer = new XmlSerializer(typeof(c_Appointments));
StreamWriter writer = new StreamWriter(FILENAME);
serializer.Serialize(writer, appointments);
writer.Flush();
writer.Close();
writer.Dispose();
return true;
}
}
public interface IAppointment
{
DateTime Start { get; }
int Length { get; }
string DisplayableDescription { get; }
bool OccursOnDate(DateTime date);
}
[XmlRoot("appointments")]
public class c_Appointments
{
[XmlElement("appointment")]
public List<Appointment> appointments { get; set; }
}
[XmlRoot("appointment")]
public class Appointment
{
[XmlElement("start")]
public DateTime start { get; set; }
[XmlElement("length")]
public int length { get; set; }
[XmlElement("displayableDescription")]
public string displayableDescription { get; set; }
}
}
​

Avoid duplicated code in XML serializable classes

I have two Serializable classes with very similar code. Actually, except for the part where specific constructor is called, serialization code is identical.
Is there a way to create a common class to contain the common parts, so that specific classes (subclasses?) can implement only the constructor part? I can think of generics, factory pattern, but could not figure out how to do it.
// Fictitious classes
[Serializable]
public class FlightParameters {
public double MaxHeight { get; set; }
pulbic double MaxSpeedKmPerHour { get; set; }
public static FlightParameters Load(String fname) {
FlightParameters result;
using (var fs = new FileStream(fname, FileMode.OpenOrCreate)) {
var serializer = new XmlSerializer(typeof(FlightParameters));
try {
result = (FlightParameters)serializer.Deserialize(fs);
}
// catch "file not found"
catch (InvalidOperationException) {
result = new FlightParameters() {
MaxHeight = 30000;
MaxSpeedKmPerHour = 1500;
}
serializer.Serialize(fs, result);
}
return result;
}
}
}
[Serializable]
public class SailingParameters {
public double MaxDepth { get; set; }
pulbic double MaxSpeedKnots { get; set; }
public static SailingParameters Load(String fname) {
SailingParameters result;
using (var fs = new FileStream(fname, FileMode.OpenOrCreate)) {
var serializer = new XmlSerializer(typeof(SailingParameters));
try {
result = (SailingParameters)serializer.Deserialize(fs);
}
// catch "file not found"
catch (InvalidOperationException) {
result = new SailingParameters() {
MaxDepth = 13000;
MaxSpeedKnots = 15;
}
serializer.Serialize(fs, result);
}
return result;
}
}
}
Usage:
FlightParameters _fparam = FlightParameters.Load(somePath);
SailingParameters _sparam = SailingParameters.Load(someOtherPath);
The easiest way I can see to do that would be something like:
static class XmlUtils {
public static T Load<T>(string filename, Func<T> onMissing = null)
where T : class, new()
{
using (var fs = File.OpenRead(filename)) {
var serializer = new XmlSerializer(typeof(T));
try {
return (T)serializer.Deserialize(fs);
} catch (InvalidOperationException) { // catch "file not found"
return onMissing == null ? new T() : onMissing();
}
}
}
}
allowing something like;
public static SailingParameters Load(string filename) {
return XmlUtils.Load<SailingParameters>(filename, () => new SailingParameters {
MaxDepth = 13000;
MaxSpeedKnots = 15;
});
}

Write/Restore ObservableCollection<T>

I have huge problem with saveing and restore ObservableCollection to IsolatedData.
I'm trying with this code.
Helper class for Observable
public class ListItem {
public String Title { get; set; }
public bool Checked { get; set; }
public ListItem(String title, bool isChecked=false) {
Title = title;
Checked = isChecked;
}
private ListItem() { }
}
IsoHelper
public class IsoStoreHelper {
private static IsolatedStorageFile _isoStore;
public static IsolatedStorageFile IsoStore {
get { return _isoStore ?? (_isoStore = IsolatedStorageFile.GetUserStoreForApplication()); }
}
public static void SaveList<T>(string folderName, string dataName, ObservableCollection<T> dataList) where T : class {
if (!IsoStore.DirectoryExists(folderName)) {
IsoStore.CreateDirectory(folderName);
}
if (IsoStore.FileExists(folderName + "\\" + dataName+".dat")) {
IsoStore.DeleteFile(folderName + "\\" + dataName + ".dat");
}
string fileStreamName = string.Format("{0}\\{1}.dat", folderName, dataName);
try {
using (var stream = new IsolatedStorageFileStream(fileStreamName, FileMode.Create, IsoStore)) {
var dcs = new DataContractSerializer(typeof(ObservableCollection<T>));
dcs.WriteObject(stream, dataList);
}
} catch (Exception e) {
Debug.WriteLine(e.Message);
}
}
public static ObservableCollection<T> LoadList<T>(string folderName, string dataName) where T : class {
var retval = new ObservableCollection<T>();
if (!IsoStore.DirectoryExists(folderName) || !IsoStore.FileExists(folderName + "\\" + dataName + ".dat")) {
return retval;
}
string fileStreamName = string.Format("{0}\\{1}.dat", folderName, dataName);
var isf = IsoStore;
try {
var fileStream = IsoStore.OpenFile(fileStreamName, FileMode.OpenOrCreate);
if (fileStream.Length > 0) {
var dcs = new DataContractSerializer(typeof(ObservableCollection<T>));
retval = dcs.ReadObject(fileStream) as ObservableCollection<T>;
}
} catch {
retval = new ObservableCollection<T>();
}
return retval;
}
}
And I'm trying to use it this way
public partial class MainPage : PhoneApplicationPage{
public ObservableCollection<ListItem> ListItems = new ObservableCollection<ListItem>();
bool isListSaved;
private void Panorama_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) {
if (strTag.Equals("list") ) {
isListSave = false;
ListItems = IsoStoreHelper.LoadList<ListItem>("settings", "ListItems");
} else if (!isListSave) {
IsoStoreHelper.SaveList<ListItem>("settings", "ListItems", ListItems);
}
}
}
I keep getting A first chance exception of type 'System.Security.SecurityException' occurred in System.Runtime.Serialization.ni.dll when I try read saved file at line ReadObject(fileStream) but the FileAccess looks fine.
Any conclusion will be appreciated.
SOLVED:
Like Dmytro Tsiniavskyi said I totaly forgot about [DataContract] and [DataMember] in ListItem. Whats more I found better solution for saving and loading data. I end up with this code for ListItem
[DataContract]
public class ListItem {
[DataMember]
public String Title { get; set; }
[DataMember]
public bool Checked { get; set; }
public ListItem(String title, bool isChecked=false) {
Title = title;
Checked = isChecked;
}
private ListItem() { }
}
And this code for save/load collection which was originally founded here and modified a litte bit for better useage.
public partial class IsolatedRW {
public static void SaveData<T>(string fileName, T dataToSave) {
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication()) {
try {
if (store.FileExists(fileName)) {
store.DeleteFile(fileName);
}
if (!store.DirectoryExists("Settings")) store.CreateDirectory("Settings");
IsolatedStorageFileStream stream;
using (stream = store.OpenFile("Settings/"+fileName+".xml", System.IO.FileMode.Create, System.IO.FileAccess.Write)) {
var serializer = new DataContractSerializer(typeof(T));
serializer.WriteObject(stream, dataToSave);
}
stream.Close();
} catch (System.Security.SecurityException e) {
//MessageBox.Show(e.Message);
return;
}
Debug.WriteLine(store.FileExists("Settings/" + fileName + ".xml"));
}
}
public static T ReadData<T>(string fileName) {
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication()) {
Debug.WriteLine(store.FileExists("Settings/" + fileName + ".xml"));
if (store.FileExists("Settings/" + fileName + ".xml")) {
IsolatedStorageFileStream stream;
using (stream = store.OpenFile("Settings/"+fileName+".xml", FileMode.OpenOrCreate, FileAccess.Read)) {
try {
var serializer = new DataContractSerializer(typeof(T));
return (T)serializer.ReadObject(stream);
} catch (Exception) {
return default(T);
}
}
stream.Close();
}
return default(T);
}
}
}
Try to add [DataContract] attribute for your ListItem class.
[DataContract]
public class ListItem {
[DataMember]
public String Title { get; set; }
[DataMember]
public bool Checked { get; set; }
public ListItem(String title, bool isChecked=false) {
Title = title;
Checked = isChecked;
}
private ListItem() { }
}

C# Serialization over socket, one class is getting serialized another does not

im am trying to make for a school project a client-server application in C#
My problem is that one class gets serialized ok and is sent over the socket, and the other is not and i cant figure it out.
Employee class (and also Bonus) is getting serialized, but when i try to pass to
the formater a Transfer instance
formatter.Serialize(stream, transferObj);
it throws exception: NotSupportedException
with message: Memory stream is not expandable.
sendToServer()
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
System.Net.IPAddress ipAdd = System.Net.IPAddress.Parse("127.0.0.1");
System.Net.IPEndPoint remoteEP = new IPEndPoint(ipAdd, 6666);
socket.Connect("127.0.0.1", 6666);
Transfer t = new Transfer();
Employee e = new Employee();
Bonus b = new Bonus(); b.setAmmount(234); b.setDescription("xxxx");
e.getBonuses().Add(b);
byte[] buffer = new byte[1000];
System.Console.WriteLine("client started");
IFormatter formatter = new BinaryFormatter();
Stream stream = new MemoryStream(buffer);
formatter.Serialize(stream, e);
// Employee and Bonus are serialized but not Transfer
stream.Flush();
socket.Send(buffer,buffer.Length,0);
Employee Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Permissions;
using System.Runtime.Serialization;
namespace Entity
{
[Serializable]
public class Employee : ISerializable
{
private int id;
private long version;
private String username;
private String password;
private String role;
private String name;
private String surname;
private long salary;
private DateTime experience;
private DateTime birthDate;
private String sex;
private List<Bonus> bonuses;
public Employee() {
bonuses = new List<Bonus>();
}
protected Employee(SerializationInfo info, StreamingContext context)
{
id = info.GetInt32("id");
version = info.GetInt32("version");
username = info.GetString("username");
password = info.GetString("password");
role = info.GetString("role");
name = info.GetString("name");
surname = info.GetString("surname");
salary = info.GetInt32("salary");
experience = (DateTime) info.GetValue("exp", typeof(DateTime));
birthDate = (DateTime)info.GetValue("birth", typeof(DateTime));
sex = info.GetString("sex");
bonuses = (List<Bonus>) info.GetValue("bonuses", typeof(List<Bonus>));
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getSurname()
{
return surname;
}
public void setSurname(String surname)
{
this.surname = surname;
}
public long getSalary()
{
return salary;
}
public void setSalary(long salary)
{
this.salary = salary;
}
public DateTime getExperience()
{
return experience;
}
public void setExperience(DateTime experience)
{
this.experience = experience;
}
public DateTime getBirthDate()
{
return birthDate;
}
public void setBirthDate(DateTime birthDate)
{
this.birthDate = birthDate;
}
public List<Bonus> getBonuses()
{
return bonuses;
}
public void setBonuses(List<Bonus> bonuses)
{
this.bonuses = bonuses;
}
public override string ToString()
{
return name + " " + surname;
}
public override int GetHashCode()
{
int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
public override bool Equals(System.Object obj)
{
// If parameter is null return false.
if (obj == null)
{
return false;
}
// If parameter cannot be cast to Point return false.
Employee p = obj as Employee;
if ((System.Object)p == null)
{
return false;
}
// Return true if the fields match:
return id == p.id;
}
public void setSex(String sex)
{
this.sex = sex;
}
public String getSex()
{
return sex;
}
public long getVersion()
{
return version;
}
public void setVersion(long version)
{
this.version = version;
}
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
public String getRole()
{
return role;
}
public void setRole(String role)
{
this.role = role;
}
#region ISerializable Members
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("id", id);
info.AddValue("version", version);
info.AddValue("username", username);
info.AddValue("password", password);
info.AddValue("role", role);
info.AddValue("name", name);
info.AddValue("surname", surname);
info.AddValue("salary", salary);
info.AddValue("exp", experience);
info.AddValue("birth", birthDate);
info.AddValue("sex", sex);
info.AddValue("bonuses", bonuses);
}
#endregion
}
}
Transfer Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Permissions;
using System.Runtime.Serialization;
namespace Entity
{
[Serializable]
public class Transfer : ISerializable
{
private Employee employee;
private String method;
private String message;
private List<Employee> queriedEmployees;
public Transfer() {
queriedEmployees = new List<Employee>();
}
protected Transfer(SerializationInfo info, StreamingContext context)
{
employee = (Employee) info.GetValue("employee", typeof(Employee) );
method = info.GetString("method");
message = info.GetString("message");
queriedEmployees = (List<Employee>) info.GetValue("qe", typeof(List<Employee>));
}
public Employee getEmployee()
{
return employee;
}
public String getMethod()
{
return method;
}
public String getMessage()
{
return message;
}
public List<Employee> getQueriedEmployees()
{
return queriedEmployees;
}
public void setEmployee(Employee e)
{
employee = e;
}
public void setMessage(String mes)
{
message = mes;
}
public void setMethod(String meth)
{
method = meth;
}
public void setQueriedEmployees(List<Employee> elist)
{
queriedEmployees = elist;
}
#region ISerializable Members
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("employee", employee);
info.AddValue("method", method);
info.AddValue("message", message);
info.AddValue("qe", queriedEmployees);
}
#endregion
}
}
It doesn't let you because you explicitly initialize the MemoryStream with a fixed length byte array. See this: http://weblogs.asp.net/ashicmahtab/archive/2008/08/25/memorystream-not-expandable-invalid-operation-exception.aspx
Try something like this instead:
IFormatter formatter = new BinaryFormatter();
Stream stream = new MemoryStream();
formatter.Serialize(stream, e);
byte[] buffer = ((MemoryStream)stream).ToArray();
By providing a buffer to the MemoryStream constructor you are fixing it to that size. If you just use the default constructor, the stream should be expandable to whatever size is required.
Stream stream = new MemoryStream();
See this blog entry
setting a larger buffer will let me serialize Transfer class

Categories

Resources