将非可序列化类转换为字节数组

Ant*_*lin 6 .net c# serialization bytearray hashcode

我有一个场景,我在多个非常不同的系统之间同步数据.(数据本身很相似,但不同系统上的表格格式完全不同.)为了协助这种同步,我有一个数据库表,它存储来自每个系统的对象哈希值以及项目键和其他相关信息.当来自任一系统的对象的散列改变时,我更新另一个.

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

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

到现在为止还挺好.但...

为了有效地计算对象的哈希(例如MD5哈希(我正在使用的)),您需要能够将其转换为字节数组.

和...

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

对于其中一个系统,我能够将所有数据库对象都可序列化,这样才能实现.哈希生成,一切都同步,世界是美丽的!

对于另一个系统,事情并不是那么好.我从实体框架4(代码优先)模型传递数据库上下文,并且实体未被序列化.

当我尝试使用类似下面的内容转换为字节时,.NET抱怨并引发一个小小的发脾气 - 一直拒绝给我一个我礼貌要求的漂亮的小字节数组.

foreach(var dataItem in context.TableName)
{
    var byteArray = (byte[]) dataItem;
}
Run Code Online (Sandbox Code Playgroud)

好.没问题.

我有一个很好的小扩展方法,我认为可以做到这一点.

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

但是哦,不!如果对象(实体)不可序列化,则此例程会抛出另一个不错的小(并且完全预期)异常.

所以......我修改了例程,并在方法定义中添加了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();
}
Run Code Online (Sandbox Code Playgroud)

唯一的问题是,现在我回到原点,我的所有对象都需要可序列化以获得字节数组.

嗯.不好.

所以我整理了一个hack来遍历所有对象的属性并生成一个字符串表示,我可以从中构建一个字节数组.它是UGLY和INEFFICIENT但它有点像诀窍.

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

但...

毕竟,我仍然希望能够有效地从一个类未被标记为可序列化的对象创建一个字节数组.完成此任务的最佳方法是什么?

先感谢您!

wal*_*wal 8

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

您可以使用protobuf-net v2来完成此任务.下载zip然后引用protobuf-net程序集.

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

public class Person
{
    public string Firstname { get; set; }
    public string Surname { get; set; }
    public int Age { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以将其序列化为字节数组:

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

protobuf-net序列化器将序列化更快,并产生比...更小的byte []数组BinaryFormatter

警告1这只会(以其当前形式)序列化您的类的公共属性,这对您的使用看起来没问题.
警告2这被认为是脆弱的,因为添加新属性Person可能意味着您无法反Person序列化使用先前序列化的对象TypeModel.