Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/287.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/google-cloud-platform/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将不可序列化的类转换为字节数组_C#_.net_Serialization_Bytearray_Hashcode - Fatal编程技术网

C# 将不可序列化的类转换为字节数组

C# 将不可序列化的类转换为字节数组,c#,.net,serialization,bytearray,hashcode,C#,.net,Serialization,Bytearray,Hashcode,我有一个场景,在多个非常不同的系统之间同步数据。(数据本身是相似的,但不同系统上的表具有完全不同的格式。)为了帮助进行同步,我有一个数据库表,它存储来自每个系统的对象哈希以及项键和其他相关信息。当来自任一系统的对象的哈希值发生变化时,我会更新另一个系统 我的数据库表看起来像这样 CREATE TABLE [dbo].[SyncHashes]( [SyncHashId] [int] IDENTITY(1,1) NOT NULL, [ObjectName] [nvarchar](50

我有一个场景,在多个非常不同的系统之间同步数据。(数据本身是相似的,但不同系统上的表具有完全不同的格式。)为了帮助进行同步,我有一个数据库表,它存储来自每个系统的对象哈希以及项键和其他相关信息。当来自任一系统的对象的哈希值发生变化时,我会更新另一个系统

我的数据库表看起来像这样

CREATE TABLE [dbo].[SyncHashes](
    [SyncHashId] [int] IDENTITY(1,1) NOT NULL,
    [ObjectName] [nvarchar](50) NULL,
    [MappingTypeValue] [nvarchar](25) NULL,
    [MappingDirectionValue] [nvarchar](25) NULL,
    [SourceSystem] [nvarchar](50) NULL,
    [SourceKey] [nvarchar](200) NULL,
    [SourceHash] [nvarchar](50) NULL,
    [TargetSystem] [nvarchar](50) NULL,
    [TargetKey] [nvarchar](200) NULL,
    [TargetHash] [nvarchar](50) NULL,
    [UpdateNeededValue] [nvarchar](max) NULL,
    [CreatedOn] [datetime] NULL,
    [ModifiedOn] [datetime] NULL,
    [Version] [timestamp] NOT NULL, 
    [IsActive] [bit] NOT NULL,
PRIMARY KEY CLUSTERED 
(
    [SyncHashId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
到目前为止还不错。但是

为了有效地计算一个对象的散列(比如MD5散列(我正在使用它)),您需要能够将其转换为字节数组

而且

似乎为了将对象转换为字节数组,它必须是可序列化的。(至少这是我读到的,我从.NET中得到的错误似乎表明这是真的。)

对于其中一个系统,我能够使我的所有数据库对象都可序列化,所以这很好。哈希得到生成,一切都得到同步,世界是美好的

对于另一个系统来说,情况并不是那么好。从EntityFramework4(代码优先)模型向我传递了一个数据库上下文,并且这些实体没有序列化

当我尝试使用如下内容转换为字节时,.NET会抱怨并发出轻微的愤怒——同时拒绝给我我礼貌地要求的漂亮的小字节数组

foreach(var dataItem in context.TableName)
{
    var byteArray = (byte[]) dataItem;
}
嗯。没问题

我自己有一个很好的小扩展方法,我认为它可能会奏效

public static byte[] ObjectToByteArray<T>(this T obj)
{
    if (obj == null)
        return null;
    BinaryFormatter bf = new BinaryFormatter();
    MemoryStream ms = new MemoryStream();

    bf.Serialize(ms, obj);
    return ms.ToArray();
}
public static string ComputeMD5Hash<T>(this T input)
{
    StringBuilder sb = new StringBuilder();

    Type t = input.GetType();
    PropertyInfo[] properties = t.GetProperties();

    foreach (var property in properties)
    {
        sb.Append(property.Name);
        sb.Append("|");
        object value = property.GetValue(input, null);
        if (value != null)
        {
            sb.Append(value);
        }
        sb.Append("|");
    }

    return MD5HashGenerator.GenerateKey(sb.ToString());
}
公共静态字节[]对象字节数组(此T obj)
{
if(obj==null)
返回null;
BinaryFormatter bf=新的BinaryFormatter();
MemoryStream ms=新的MemoryStream();
bf.序列化(ms,obj);
返回ToArray女士();
}
但是,哦,不!如果对象(实体)不可序列化,这个例程会抛出另一个很好的小异常(完全可以预期)

所以。。。我修改了例程并向方法定义中添加了where子句,如下所示

public static byte[] ObjectToByteArray<T>(this T obj) where T : ISerializable
{
    if (obj == null)
        return null;
    BinaryFormatter bf = new BinaryFormatter();
    MemoryStream ms = new MemoryStream();

    bf.Serialize(ms, obj);
    return ms.ToArray();
}
publicstaticbyte[]ObjectToByteArray(此T obj),其中T:ISerializable
{
if(obj==null)
返回null;
BinaryFormatter bf=新的BinaryFormatter();
MemoryStream ms=新的MemoryStream();
bf.序列化(ms,obj);
返回ToArray女士();
}
唯一的问题是,现在我又回到了原点,我的所有对象都需要序列化才能获得字节数组

嗯,不太好

因此,我编写了一个hack来迭代对象的所有属性,并生成一个字符串表示,从中我可以构建一个字节数组。这是丑陋和低效的,但它有点做的把戏

public static byte[] ObjectToByteArray<T>(this T obj)
{
    if (obj == null)
        return null;
    BinaryFormatter bf = new BinaryFormatter();
    MemoryStream ms = new MemoryStream();

    bf.Serialize(ms, obj);
    return ms.ToArray();
}
public static string ComputeMD5Hash<T>(this T input)
{
    StringBuilder sb = new StringBuilder();

    Type t = input.GetType();
    PropertyInfo[] properties = t.GetProperties();

    foreach (var property in properties)
    {
        sb.Append(property.Name);
        sb.Append("|");
        object value = property.GetValue(input, null);
        if (value != null)
        {
            sb.Append(value);
        }
        sb.Append("|");
    }

    return MD5HashGenerator.GenerateKey(sb.ToString());
}
公共静态字符串ComputeMD5Hash(此T输入)
{
StringBuilder sb=新的StringBuilder();
类型t=input.GetType();
PropertyInfo[]properties=t.GetProperties();
foreach(属性中的var属性)
{
某人追加(财产名称);
某人加上(“|”);
对象值=property.GetValue(输入,null);
if(值!=null)
{
附加(价值);
}
某人加上(“|”);
}
返回MD5HashGenerator.GenerateKey(sb.ToString());
}
但是

在所有这些之后,我仍然希望能够高效、正确地从一个类未标记为可序列化的对象创建一个字节数组。实现这一目标的最佳方式是什么

提前谢谢你

从类未标记为可序列化的对象创建字节数组

您可以使用v2来实现这一点。下载zip文件,然后参考
protobuf-net
程序集

考虑我们要序列化的这个简单类定义:

public class Person
{
    public string Firstname { get; set; }
    public string Surname { get; set; }
    public int Age { get; set; }
}
然后可以将其序列化为字节数组:

var person = new Person {Firstname = "John", Surname = "Smith", Age = 30};
var model = ProtoBuf.Meta.TypeModel.Create();
//add all properties you want to serialize. 
//in this case we just loop over all the public properties of the class
//Order by name so the properties are in a predictable order
var properties = typeof (Person).GetProperties().Select(p => p.Name).OrderBy(name => name).ToArray();
model.Add(typeof(Person), true).Add(properties);

byte[] bytes;

using (var memoryStream = new MemoryStream())
{
    model.Serialize(memoryStream, person);
    bytes = memoryStream.GetBuffer();
}
protobuf网络序列化程序的序列化速度比BinaryFormatter快得多,生成的字节[]数组比BinaryFormatter小得多

注意事项1这将仅(以其当前形式)序列化类的公共属性,看起来适合您的使用。

注意事项2这被认为是脆弱的,因为向
Person
添加新属性可能意味着您无法对使用先前的
TypeModel
序列化的
Person
对象进行反序列化。我忘了提到MD5HashGenerator.GenerateKey(byte[]byteArray)函数将字节数组作为其属性。Wal,这看起来是一个非常好的解决方案。然而,我目前正在处理一个问题。我序列化的对象位于嵌套的层次结构中,Protobuf似乎在处理子对象时遇到了一些困难。(在添加父对象时,似乎不会自动将它们添加到模型中。)接下来,我将使用子对象上的反射自动将它们添加到模型中,然后返回报告。总之,我真的很喜欢这个Protobuf库。它似乎快得惊人。非常感谢您的分享。@Anthony Gatlin您必须在模型中指定要序列化的每种类型。我已经给项目创建者(Marc Gravell)发了一封电子邮件,询问是否有现成的方法,或者是否有其他人已经这样做了。同时,通过循环属性并将每个属性添加到模型中,您的建议听起来是不可能实现的。是的,Protobuf网络非常快。此网站暗中使用它。重要提示:由于
GetProperties()
不能保证订单,因此您应该确保
properties
代表可重复的订单。最简单的方法是只使用
.Select(p=>p.Name).OrderBy(x=>x).ToArray()
,或
Array.Sort(属性)(在LINQ之后)。@Marc,出于好奇,为什么财产顺序很重要?序列化程序在执行转换时是否使用属性位置而不是名称?