2

I have a client/server project and I'm trying to send via socket a DataTable(extracted from a TableAdapter) from server to client. My server namespace is srvCentral and my client is appClient. When I try to deserialize DataTable in client it throws me an Serialize Exception saying Unable to find assembly 'srvCentral, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' I've googled and tryed things like controlling the AssemblyResolve, that makes svchost hang and force me to close, and using a binder like this:

sealed class AllowAllAssemblyVersionsDeserializationBinder : SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        Type typeToDeserialize = null;

        String currentAssembly = Assembly.GetExecutingAssembly().FullName;

        // In this case we are always using the current assembly
        assemblyName = currentAssembly;

        // Get the type using the typeName and assemblyName
        typeToDeserialize = Type.GetType(String.Format("{0}, {1}",
                                         typeName, assemblyName));

        return typeToDeserialize;
    }
}

And the exception stills there... Wasn't it suppose to DataTable be Deserializable anywhere? What am I doing wrong?

My Serialization code is:

public byte[] Serializar(Object item)
{
    BinaryFormatter formatter = new BinaryFormatter();
    MemoryStream ms = new MemoryStream();
    formatter.Serialize(ms, item);
    return ms.ToArray();
}

public Object Deserializar(byte[] buffer)
{
    BinaryFormatter formatter = new BinaryFormatter();
    MemoryStream ms = new MemoryStream(buffer);

    formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;

    formatter.Binder = new AllowAllAssemblyVersionsDeserializationBinder();

    Object a = formatter.Deserialize(ms);

    return a;
}

sealed class AllowAllAssemblyVersionsDeserializationBinder : SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        Type typeToDeserialize = null;

        String currentAssembly = Assembly.GetExecutingAssembly().FullName;

        // In this case we are always using the current assembly
        assemblyName = currentAssembly;

        // Get the type using the typeName and assemblyName
        typeToDeserialize = Type.GetType(String.Format("{0}, {1}",
                                         typeName, assemblyName));

        return typeToDeserialize;
    }
}

After some digging I've solve the problem with this;

It isn't the better way to solve it but is a easy way to avoid the problem. For small things it's enough. Use this if you want to retrieve data from table for show content only.

namespace YourLibrary
{
[Serializable]
public class Tabela: ISerializable
{
    protected ArrayList colNames;
    protected ArrayList colTypes;
    protected ArrayList dataRows;

    public Tabela()
    {

    }

    public Tabela (DataTable dt)
    {
        colNames = new ArrayList();
        colTypes = new ArrayList();
        dataRows = new ArrayList();
        // Insert column information (names and types)
        foreach(DataColumn col in dt.Columns)
        {
            colNames.Add(col.ColumnName); 
            colTypes.Add(col.DataType.FullName);   
        }

        // Insert rows information
        foreach(DataRow row in dt.Rows)
            dataRows.Add(row.ItemArray);
    }

    public Tabela(SerializationInfo info, StreamingContext context)
    {
        colNames = (ArrayList)info.GetValue("colNames",typeof(ArrayList));
        colTypes = (ArrayList)info.GetValue("colTypes",typeof(ArrayList));
        dataRows = (ArrayList)info.GetValue("dataRows",typeof(ArrayList));

    }

    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("colNames", colNames);
        info.AddValue("colTypes", colTypes);
        info.AddValue("dataRows", dataRows);
    }

    public DataTable GenerateDataTable()
    {
        DataTable dt = new DataTable();

        // Add columns
        for(int i=0; i<colNames.Count; i++)
        {
            DataColumn col = new DataColumn(colNames[i].ToString(), 
                Type.GetType(colTypes[i].ToString() ));     
            dt.Columns.Add(col);
        }

        // Add rows
        for(int i=0; i<dataRows.Count; i++)
        {
            DataRow row = dt.NewRow();
            row.ItemArray = (object[]) dataRows[i];
            dt.Rows.Add(row);
        }

        dt.AcceptChanges();
        return dt;
    }
}
}
user1414676
  • 21
  • 1
  • 3

1 Answers1

13

What am I doing wrong?

sigh; 1: using DataTable, and 2: using BinaryFormatter

let's take the latter first; BinaryFormatter is a type-centric serializer. Actually, you would probably have got away with this if you were just using DataTable rather than a typed DataTable subclass, but BinaryFormtter ultimately wants exactly the same types at each end. And you don't have that. And even if you did, every time you version one end of the pipe, things could get a bit... dodgy (unless you invest extra care into this).

As a temporary fix, for this step, just use DataTable rather than the typed DataTable subclass, and it will probably work.

However, DataTable is also a pretty awkward thing to throw around - fairly clumsy and so general in purpose. If you need what it offers (in particular, dynamic columns) then .... maybe at a push, but in most cases using a basic POCO / DTO model would be far preferable. This is also easier to express on a client/server boundary, including most IPC tools. For example, the following POCO / DTO class (or a list of them) is very friendly:

public class Order {
    public int OrderID {get;set;}
    public string Reference {get;set;}
    ...
}

Personally, I strongly suggest that you look at switching to a simpler, class-based model, using a serializer that isn't fussy about particular types; XmlSerializer or JavascriptSerializer work well. If you need small / efficient data, then protobuf-net would be worth a look too. All of these work very well over sockets, too.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Datatables aren't a bad datastructure: they're very good in tabular data, and much faster with tabular data than a list of poco's, as their internal structures is a list of object[]. Everything has its purpose: if one wants tabular data, e.g. with optional views with filters on them, a datatable is a solid choice. Ignoring that is IMHO simply dumb. If the purpose of the data is a list of objects, a datatable is indeed a dumb choice (as it's about tabular data). However writing it off for all cases is IMHO shortsighted. – Frans Bouma May 24 '12 at 11:27
  • Great answer Marc, out of curiosity... could the version problem be overcome by exposing the object via an interface? – Doctor Jones May 24 '12 at 11:56
  • @DoctaJonez no; what matters is what the object ***is***, not what it looks like – Marc Gravell May 24 '12 at 12:51
  • 2
    @Frans I strongly refute ([with data](http://pastie.org/pastes/3960841/text)) that a `DataTable` is "much faster" - loading them takes the same time as POCO. Yes, `DataTable` has a lot of features (projections, filters, etc) - but so does (for example) LINQ-to-Objects (one-way, at least). Don't forget, the context here is data comms, not UI concerns. And sure, `DataTable` does have some uses... just, unless/until the dev can articulate the reasons for choosing `DataTable`, there may have been more more suitable (and simpler) alternatives. – Marc Gravell May 24 '12 at 12:54
  • Thank you all for the help. Marc thanks by the explanation, I've already solved it creating a ghost table class and put it on the dll. I'm passing the data just to output it or execute a select and then output, so this method is enough I think. I will see what is a POCO or DTO model anyway. – user1414676 May 24 '12 at 13:25
  • I wish SqlBulkCopy could use linq-to-objects. – Andreas Ågren May 24 '12 at 15:37
  • @MarcGravell Yes it's possible to work around, but I wish it supported it out of the box. – Andreas Ågren May 25 '12 at 06:45