tags:

views:

120

answers:

3

Hi

I have to write an object in to binary file.My struct looks like this.

   Struct Company
    {
       int numberofemployees
       list of Struct Employee.
    }

    Struct Employee
    {
       string EmployeeName;
       string Designation;
    }

What is the best way to do the above operation? Regards Raju

+6  A: 

In my understanding, BinaryFormatter is the tool for this job.

Edit: As Marc explains in the comments, BinaryFormatter has certain disadvantages. He recommends protobuf-net in his blog.

Jens
+1 Jens, that looks better than simply writing to file yourself.
Lirik
I have tried using Binary Formatter. It is adding some header before writing first object in to the file. I don't want any header data.
Yes you do, it gives you a fighting chance to read the data back properly some day.
Hans Passant
Honestly, `BinaryFormatter` is generally a **really** bad choice for persistence....
Marc Gravell
@Marc: Can you explain a little? I am using BinaryFormatter for something like that, and I'd be interested to know if (and why) I've made a bad choice. =)
Jens
It is hugely brittle; it is fine as long as you never change runtime, never change your assemblies, never obfuscate, and never change the code (for example auto-props). Additionally, it is *completely* .NET specific (non-portable). See http://marcgravell.blogspot.com/2009/03/obfuscation-serialization-and.html
Marc Gravell
Oh, and it is unnecessarily verbose and CPU intensive. And consider that alternatives exist that are *not* brittle (they are contract-based rather than metadata-based), take fewer bytes, less CPU *and* are portable between different platforms...
Marc Gravell
@Marc: Thanks! =)
Jens
A: 

Here is an example how you can read/write a binary file:

using System;
using System.IO;

public class BinaryFileTest {

    private static void Main() {
        FileStream fs = new FileStream("test.dat", FileMode.Create);
        BinaryWriter w = new BinaryWriter(fs);

        w.Write(1.2M);
        w.Write("string");
        w.Write("string 2");
        w.Write('!');
        w.Flush();
        w.Close();
        fs.Close();

        fs = new FileStream("test.dat", FileMode.Open);
        StreamReader sr = new StreamReader(fs);
        Console.WriteLine(sr.ReadToEnd());
        fs.Position = 0;
        BinaryReader br = new BinaryReader(fs);
        Console.WriteLine(br.ReadDecimal());
        Console.WriteLine(br.ReadString());
        Console.WriteLine(br.ReadString());
        Console.WriteLine(br.ReadChar());
        fs.Close();

    }
}
Lirik
+3  A: 

What exactly do you want the output to look like? You can write it manually (see Lirik's answer), or if you want runtime support, perhaps something like protobuf-net.

This would be trivial to do if you were using classes (which I expect you actually should be), but additionally protobuf-net v2 (only available as source at the moment) should work with that "as is".

For info, here is how I would do it as classes:

    public class Company
    {
        private readonly List<Employee> employees = new List<Employee>();
        public List<Employee> Employees { get { return employees;}}
    }

    public class Employee
    {
        public string EmployeeName {get;set;}
        public string Designation {get;set;}
    }

This could be decorated with serialization attributes, or (again, using protobuf-net v2) something like this test (which passes):

    [Test]
    public void CanSerializeCompany()
    {
        var model = TypeModel.Create();
        model.Add(typeof(Company), false).Add("Employees");
        model.Add(typeof(Employee), false).Add("EmployeeName", "Designation");
        model.CompileInPlace();

        Company comp = new Company {
            Employees = {
                new Employee { Designation = "Boss", EmployeeName = "Fred"},
                new Employee { Designation = "Grunt", EmployeeName = "Jo"},
                new Employee { Designation = "Scapegoat", EmployeeName = "Alex"}}
        }, clone;
        using(var ms = new MemoryStream()) {
            model.Serialize(ms, comp);
            ms.Position = 0;
            Console.WriteLine("Bytes: " + ms.Length);
            clone = (Company) model.Deserialize(ms, null, typeof(Company));
        }
        Assert.AreEqual(3, clone.Employees.Count);
        Assert.AreEqual("Boss", clone.Employees[0].Designation);
        Assert.AreEqual("Alex", clone.Employees[2].EmployeeName);
    }

(and writes 46 bytes)

It should work with private fields, structs, etc - I'd have to take a look...

If you are able to add attributes, then you don't need to set up the model manually (the first 4 lines). The rest of the code is just showing full round-trip usage.

Marc Gravell