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

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

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

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得到的错误似乎表明这是真的。)

对于其中一个系统,我能够使我的所有数据库对象都可序列化,因此非常棒。 哈希生成,一切都同步,世界是美丽的!

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

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

 foreach(var dataItem in context.TableName) { var byteArray = (byte[]) dataItem; } 

好。 没问题。

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

 public static byte[] ObjectToByteArray(this T obj) { if (obj == null) return null; BinaryFormatter bf = new BinaryFormatter(); MemoryStream ms = new MemoryStream(); bf.Serialize(ms, obj); return ms.ToArray(); } 

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

所以……我修改了例程,并在方法定义中添加了where子句,就像这样。

 public static byte[] ObjectToByteArray(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(); } 

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

嗯。 不好。

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

 public static string ComputeMD5Hash(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()); } 

但…

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

先感谢您!

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

您可以使用protobuf-net 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-net序列化器将比BinaryFormatter更快地序列化并产生更小的byte []数组

警告1这只会(以其当前forms)序列化您的类的公共属性,这对您的使用来说是好的。
警告2这被认为是脆弱的,因为向Person添加新属性可能意味着您无法反序列化使用先前TypeModel序列化的Person对象。