C#无法反序列化DataTable

use*_*676 2 c# datatable assembly-resolution deserialization binary-deserialization

我有一个客户端/服务器项目,我正在尝试通过套接字从服务器向客户端发送DataTable(从TableAdapter中提取).我的服务器命名空间是srvCentral,我的客户端是appClient.当我尝试在客户端反序列化DataTable时,它会抛出一个序列化异常,说无法找到程序集'srvCentral,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null'我用谷歌搜索并尝试控制AssemblyResolve之类的东西,这使得svchost挂起并强迫我关闭,并使用这样的绑定器:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

那里的例外情况仍然存在......是不是假设DataTable可以在任何地方进行反序列化?我究竟做错了什么?

我的序列化代码是:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

经过一番挖掘,我已经解决了这个问题;

它不是解决问题的更好方法,但却是避免问题的简单方法.对于小东西来说就足够了.如果要仅从表中检索数据以显示内容,请使用此选项.

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;
    }
}
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*ell 14

我究竟做错了什么?

叹; 1:使用DataTable,2:使用BinaryFormatter

让我们先把后者; BinaryFormatter是一种以类型为中心的序列化器.实际上,如果你只是使用DataTable而不是类型化的 DataTable子类,你可能已经侥幸成功,但BinaryFormtter最终想要在每一端都使用完全相同的类型.你没有那个.即使你这样做了,每次你对管道的一端进行版本化时,事情都会变得有点......狡猾(除非你特别注意这一点).

作为临时修复,对于此步骤,只使用DataTable而不是类型化的DataTable子类,它可能会起作用.

然而,DataTable也是一个非常尴尬的事情 - 相当笨拙和目的一般.如果你需要它提供的东西(特别是动态列),那么.... 也许是推动,但在大多数情况下使用基本的POCO/DTO模型将是更好的选择.这在客户端/服务器边界上也更容易表达,包括大多数IPC工具.例如,以下POCO/DTO类(或它们的列表)非常友好:

public class Order {
    public int OrderID {get;set;}
    public string Reference {get;set;}
    ...
}
Run Code Online (Sandbox Code Playgroud)

就个人而言,我强烈建议你看一下更简单的基于类的模型,使用一个对特定类型不挑剔的序列化器; XmlSerializerJavascriptSerializer运作良好.如果您需要小/高效的数据,那么protobuf-net也值得一看.所有这些在套接字上都能很好地工作.

  • @Frans我强烈反驳([与数据](http://pastie.org/pastes/3960841/text))`DataTable`"快得多" - 加载它们需要与POCO相同的时间.是的,`DataTable`有很多功能(投影,过滤器等) - 但(例如)LINQ-to-Objects(至少是单向)也是如此.别忘了,这里的上下文是数据通信,而不是UI问题.当然,"DataTable"确实有一些用途...只是,除非/开发人员能够清楚地说明选择`DataTable`的原因,否则可能会有更多更合适(也更简单)的选择. (2认同)