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
Related
Thanks to NHMountainGoat for an answer!
Implementing Interface looks a good choice so we have only the 'needed' method instanciated.
It looks like this now:
EDIT
class Machine
{
//REM: MachineConnexion is a link to the main server where asking the data
internal linkToPLC LinkToPLC;
public IlinkToPLC ILinkPLC;
public interface IlinkToPLC//Interface to linkPLC
{
Int16 MachineNumIS { get; set; }
}
internal class linkToPLC : IlinkToPLC
{
private Int16 Act_MachineNum;
private List<string> genlnkPLCCanvas;
private List<string> genlnkPLCworkingwith;
static private List<string> ListSymbolNoExist;
private string[] ListToPLClnk = {
"GlobalFolder.PMachine[{0}].",
"GlobalFolder.PMachine[{0}].STATE.",
"GlobalFolder.Machine[{0}].",
"GlobalFolder.Machine[{0}].STATE.",
};
public linkToPLC()//ctor
{
genlnkPLCCanvas = new List<string>(ListToPLClnk);
genlnkPLCworkingwith = new List<string>(ListToPLClnk);
ListSymbolNoExist = new List<string>();
Act_MachineNum = MachineNumIS;
}
public Int16 MachineNumIS { get { return (Int16)ReadWriteMachine("data"); } set { ReadWriteMachine("data", value); } }
public string ValueExist(string ValueToreach, bool WorkingDATA = false)
{
if (!WorkingDATA)
{
for (int inc = 0; inc < genlnkPLCworkingwith.Count; inc++)
{
string StrValueToReach = genlnkPLCworkingwith[inc] + ValueToreach;
if (MachineConnexion.SymbolExists(StrValueToReach))
{
ListSymbolNoExist.Clear();
return StrValueToReach;
}
else ListSymbolNoExist.Add(genlnkPLCworkingwith[inc] + ValueToreach);
}
}
else if (WorkingDATA)
{
string StrValueToReach = genlnkPLCworkingwith[10] + ValueToreach;
if (MachineConnexion.SymbolExists(StrValueToReach))
{
ListSymbolNoExist.Clear();
return StrValueToReach;
}
else ListSymbolNoExist.Add(genlnkPLCworkingwith[10] + ValueToreach);
}
if (ListSymbolNoExist.Count != 0)
{
string ErrorList = "";
for (int inc = 0; inc < ListSymbolNoExist.Count; inc++)
{
ErrorList = string.Concat(ErrorList + "Num: " + inc.ToString() + " " + ListSymbolNoExist[inc].ToString() + "\n");
}
Console.WriteLine("Error" + ErrorList);
}
return null;
}
public object ReadWriteMachine(string VariableName, object DataToWrite = null, bool WorkingDATA = false)
{
string valueToFind = "";
if (ValueExist(VariableName) != "FALSE")
{
if (DataToWrite != null) { MachineConnexion.WriteSymbol(valueToFind, DataToWrite); }
return MachineConnexion.ReadSymbol(valueToFind);
}
return VariableName;
}
}
public Machine() //constructor
{
LinkToPLC = new linkToPLC();
}
}
And It doesn't work telling me that the reference object is not defined to an instance of the object..... in the line : Machine() LinkToPLC = new linkToPLC();//REM I found the bug, it was me ;o)) 24112016
//REM 24112016
What are the main differences between those two concept: static Instance and Interface?
Example:
class Program
{
static void Main(string[] args)
{
ITestInterface InterInstance = new TestInterface();
//test Interface
bool value1 = true;
value1 = InterInstance.invert(value1);
InterInstance.print(value1);
//test Instance static
TestStaticInstance staticInstance = new TestStaticInstance();
staticInstance.Instance.invert(value1);
staticInstance.Instance.print(value1);
Console.ReadKey();
}
}
class TestInterface : ITestInterface
{
public bool invert(bool value)
{
return !value;
}
public void print(bool value)
{
Console.WriteLine(value.ToString()+"\n");
}
private void methodX()
{ }
}
interface ITestInterface
{
bool invert(bool value);
void print(bool value);
}
public class TestStaticInstance
{
public TestStaticInstance Instance;
public TestStaticInstance()
{
Instance = this;
}
internal bool invert(bool value)
{
return !value;
}
internal void print(bool value)
{
Console.WriteLine(value.ToString());
}
}
Thanks
Can you structure your other classes to take an instance of the link class? See:
/// <summary>
/// just a stub to demonstrate the model
/// </summary>
internal class Machine
{
public string ReadData() { return "this is data"; }
public void WriteData(string data) { Console.WriteLine(data); }
}
internal interface IMachineDataAccessor
{
string Read();
void Write(string data);
}
class LinkClass : IMachineDataAccessor
{
protected Machine _machine;
public LinkClass(Machine machine)
{
_machine = machine;
}
public void DoMyWork()
{
// insert work somewhere in here.
string dataFromMachine = Read();
Write("outbound data");
}
public string Read()
{
return _machine.ReadData();
}
public void Write(string data)
{
_machine.WriteData(data);
}
}
class PersistentClass
{
IMachineDataAccessor _machineImpl;
public PersistentClass(IMachineDataAccessor machineAccessImplementation)
{
_machineImpl = machineAccessImplementation;
}
public void DoMyWork()
{
string dataFromMachine = _machineImpl.Read();
// insert work here. Or anywhere, actually..
_machineImpl.Write("outbound data");
}
}
class StateClass
{
IMachineDataAccessor _machineImpl;
public StateClass(IMachineDataAccessor machineAccessImplementation)
{
_machineImpl = machineAccessImplementation;
}
public void DoMyWork()
{
string dataFromMachine = _machineImpl.Read();
// insert work here. Or anywhere, actually..
_machineImpl.Write("outbound data");
}
}
static void Main(string[] args)
{
LinkClass link = new LinkClass(new Machine());
PersistentClass persistent = new PersistentClass(link as IMachineDataAccessor);
StateClass state = new StateClass(link as IMachineDataAccessor);
persistent.DoMyWork();
state.DoMyWork();
link.DoMyWork();
}
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; }
}
}
I have a method which has a string type parameter and returns a List where Tasks is a class defined by me.
When I want to JSON serialize the returned List it gives me an Exception:
InvalidOperationException("Json serializable types must have a public, parameterless constructor");
What should I do?
UPDATE: Code sample added
My class looks like this:
public class Tasks
{
public int _taskReprogramat;
public int TaskReprogramat{ get; set; }
public string TaskName {get; set; }
public string Description{get; set; }
public string IntreOrele{get; set; }
public DateTime AssignDate{get; set; }
public string Status{get; set; }
public Tasks() { }
}
and I have the following classes:
public class DataContractJsonSerializerOperationBehavior : DataContractSerializerOperationBehavior
{
public DataContractJsonSerializerOperationBehavior(OperationDescription operation) : base(operation) { }
public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
{
return new DataContractJsonSerializer(type);
}
public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
{
return new DataContractJsonSerializer(type);
}
}
and
public class JsonDataContractBehaviorAttribute : Attribute, IContractBehavior
{
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
this.ReplaceSerializerOperationBehavior(contractDescription);
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.DispatchRuntime dispatchRuntime)
{
this.ReplaceSerializerOperationBehavior(contractDescription);
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
foreach (OperationDescription operation in contractDescription.Operations)
{
foreach (MessageDescription message in operation.Messages)
{
this.ValidateMessagePartDescription(message.Body.ReturnValue);
foreach (MessagePartDescription part in message.Body.Parts)
{
this.ValidateMessagePartDescription(part);
}
foreach (MessageHeaderDescription header in message.Headers)
{
this.ValidateJsonSerializableType(header.Type);
}
}
}
}
private void ReplaceSerializerOperationBehavior(ContractDescription contract)
{
foreach (OperationDescription od in contract.Operations)
{
for (int i = 0; i < od.Behaviors.Count; i++)
{
DataContractSerializerOperationBehavior dcsob = od.Behaviors[i] as DataContractSerializerOperationBehavior;
if (dcsob != null)
{
od.Behaviors[i] = new DataContractJsonSerializerOperationBehavior(od);
}
}
}
}
private void ValidateMessagePartDescription(MessagePartDescription part)
{
if (part != null)
{
this.ValidateJsonSerializableType(part.Type);
}
}
private void ValidateJsonSerializableType(Type type )
{
if (type != typeof(void))
{
if (!type.IsPublic)
{
throw new InvalidOperationException("Json serialization is supported in public types only");
}
ConstructorInfo defaultConstructor = type.GetConstructor(new Type[0]);
if (!type.IsPrimitive && defaultConstructor == null )
{
throw new InvalidOperationException("Json serializable types must have a public, parameterless constructor");
}
}
}
}
and my method in which I am using the Tasks class is:
public List<Tasks> GetTask(string admin, DateTime date)
{
List<Tasks> tasks = new List<Tasks>();
string conn = ConfigurationManager.ConnectionStrings["qtmConnectionString"].ConnectionString;
SqlConnection myConn = new SqlConnection(conn);
myConn.Open();
SqlCommand myCommand = new SqlCommand("tasks", myConn);
myCommand.CommandType = CommandType.StoredProcedure;
myCommand.Parameters.Add("#username", SqlDbType.NChar);
myCommand.Parameters["#username"].Value = admin;
myCommand.Parameters.AddWithValue("#date", date);
SqlDataReader reader = myCommand.ExecuteReader();
string status = null;
string intreOrele = null;
Tasks task = new Tasks();
task.TaskName = reader[1].ToString();
task.Description = reader[2].ToString();
task.IntreOrele = intreOrele;
task.AssignDate = Convert.ToDateTime(reader[5]);
task.Status = status;
tasks.Add(task); return tasks;
}
The problem is the class you are de-serializing does not have a public, parameterless constructor. It might look like this:
public class Person
{
public Person(int id, string name)
{
this.id = id;
this.person = person;
}
private int id;
private string name;
public string Name { get { return this.name; } }
public int Id { get { return this.id; } }
}
The problem is the JSON serializer needs to do this:
var obj = new Person();
obj.Id = jsonParser.Get<int>("id");
obj.Name = jsonParser.Get<string>("name");
It isn't clever enough (or designed ) to go through the constructor like this:
var obj = new Person( jsonParser.Get<int>("id"), jsonParser.Get<string>("name") );
So change your class to do this:
public class Person
{
public Person(int id, string name)
{
this.Id = id;
this.Person = person;
}
// Parameterless constructor here
public Person()
{
}
public string Name { get ; set; }
public int Id { get;set; }
}
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.
I'm trying stuff with db4o since a few days but I got a problem.
Imagine this class just for test:
class Test
{
public string TestString
public int Number;
public Bitmap Bm;
public Test2 T2;
}
I'm saving the entire class and all sub-objects.
But when I load it, I don't want Bm to be loaded (just leave it null). How can I leave it out?
I need to save it because in some cases, I need to load it.
It's a performance thing because the pictures are really large.
Well, the easiest solution (IMHO) is to wrap the BitMap class in another class and use db4o's transparent activation feature:
using System;
using System.IO;
using System.Linq;
using Db4objects.Db4o;
using Db4objects.Db4o.Activation;
using Db4objects.Db4o.TA;
namespace ActivationDepth
{
class Program
{
static void Main(string[] args)
{
var dbFilePath = Path.GetTempFileName();
using (var db = Db4oEmbedded.OpenFile(dbFilePath))
{
db.Store(new C1 { Name = "c1", Huge = new MyHugeClass("I am really huge....")});
}
var config = Db4oEmbedded.NewConfiguration();
config.Common.Add(new TransparentActivationSupport());
config.Common.ActivationDepth = 0;
using (var db = Db4oEmbedded.OpenFile(config, dbFilePath))
{
var item = db.Query<C1>().ElementAt(0);
Console.WriteLine("{0}", db.Ext().IsActive(item));
Console.WriteLine("[Huge] {0} : {1}", db.Ext().IsActive(item.huge), item.huge);
Console.WriteLine("[Huge] {0} : {1}", db.Ext().IsActive(item.Huge), item.Huge);
}
}
}
class C1 : IActivatable
{
public string Name
{
get
{
Activate(ActivationPurpose.Read);
return name;
}
set
{
Activate(ActivationPurpose.Write);
name = value;
}
}
public MyHugeClass Huge
{
get
{
Activate(ActivationPurpose.Read);
return huge;
}
set
{
Activate(ActivationPurpose.Write);
huge = value;
}
}
public override string ToString()
{
Activate(ActivationPurpose.Read);
return string.Format("[{0}] {1}", GetType().Name, name);
}
public void Bind(IActivator activator)
{
if (this.activator != null && activator != null)
{
throw new Exception("activation already set");
}
this.activator = activator;
}
public void Activate(ActivationPurpose purpose)
{
if (activator != null)
{
activator.Activate(purpose);
}
}
public MyHugeClass huge;
private string name;
[NonSerialized]
private IActivator activator;
}
class MyHugeClass : IActivatable
{
public string Name
{
get
{
Activate(ActivationPurpose.Read);
return name;
}
set
{
Activate(ActivationPurpose.Write);
name = value;
}
}
public MyHugeClass(string name)
{
this.name = name;
}
public override string ToString()
{
Activate(ActivationPurpose.Read);
return string.Format("[{0}] {1}", GetType().Name, name);
}
public void Bind(IActivator activator)
{
if (this.activator != null && activator != null)
{
throw new Exception("activation already set");
}
this.activator = activator;
}
public void Activate(ActivationPurpose purpose)
{
if (activator != null)
{
activator.Activate(purpose);
}
}
private string name;
[NonSerialized]
private IActivator activator;
}
}
Note that even though I have implemented the IActivatable interface manually I don't recommend that; you can use db4otool to implement it for you automatically.
Another possible solution is to control activation for your type (when an object is not activated in db4o, its reference is valid but all of its fields will be not initialized taking no space whatsoever).
For instance you can do something like:
using System;
using System.IO;
using System.Linq;
using Db4objects.Db4o;
using Db4objects.Db4o.Events;
namespace ActivationDepth
{
class Program
{
static void Main(string[] args)
{
var dbFilePath = Path.GetTempFileName();
using (var db = Db4oEmbedded.OpenFile(dbFilePath))
{
db.Store(new C1 { name = "c1", c2 = new C2("c2"), huge = new MyHugeClass("I am really huge....")});
}
var config = Db4oEmbedded.NewConfiguration();
using (var db = Db4oEmbedded.OpenFile(config, dbFilePath))
{
var activate = false;
var fac = EventRegistryFactory.ForObjectContainer(db);
fac.Activating += (sender, eventArgs) =>
{
if (!activate && eventArgs.Object.GetType() == typeof(MyHugeClass))
{
Console.WriteLine("[{0}] Ignoring activation.", eventArgs.Object);
eventArgs.Cancel();
}
else
{
Console.WriteLine("[{0}] Activation will proceed.", eventArgs.Object);
}
};
var item = db.Query<C1>().ElementAt(0);
Console.WriteLine("[IsActive] {0}", db.Ext().IsActive(item.huge));
activate = true;
db.Activate(item.huge, 3);
Console.WriteLine("[IsActive] {0}", db.Ext().IsActive(item.huge));
}
}
}
class C1
{
public string name;
public C2 c2;
public MyHugeClass huge;
public override string ToString()
{
return string.Format("[{0}] {1}", GetType().Name, name);
}
}
class C2
{
public string name;
public C2(string name)
{
this.name = name;
}
public override string ToString()
{
return string.Format("[{0}] {1}", GetType().Name, name);
}
}
class MyHugeClass
{
public string text;
public MyHugeClass(string text)
{
this.text = text;
}
public override string ToString()
{
return string.Format("[{0}] {1}", GetType().Name, text);
}
}
}
You can also play with activation depth.
Hope this help.