У меня есть сценарий, в котором я синхронизирую данные между несколькими ОЧЕНЬ разными системами. (Сами данные похожи, но таблицы в разных системах имеют совершенно разные форматы.) Чтобы помочь с этой синхронизацией, у меня есть таблица базы данных, в которой хранятся хеши объектов из каждой системы, а также ключи элементов и другая соответствующая информация. Когда хэш объекта из одной системы изменяется, я обновляю другой.
Моя таблица базы данных выглядит примерно так.
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 хеш (что я и использую)) для объекта, вы должны иметь возможность преобразовать его в байтовый массив .
И ...
Похоже, что для преобразования объекта в байтовый массив он должен быть serializable . (По крайней мере, это то, что я прочитал, и ошибки, которые я получаю от .NET, похоже, указывают на то, что это правда.)
Для одной из систем у меня есть возможность сделать все мои объекты базы данных сериализуемыми, так что это здорово. Генерируются хеши, все синхронизируется, и мир прекрасен!
Для другой системы все не так здорово . Мне передали контекст базы данных из модели сущностей 4 (сначала код) и объекты НЕ сериализованы .
Когда я пытаюсь преобразовать как байт, используя что-то вроде следующего, .NET жалуется и бросает небольшую истерику - все время отказываясь давать мне симпатичный маленький байтовый массив, который я так вежливо просил.
foreach(var dataItem in context.TableName)
{
var byteArray = (byte[]) dataItem;
}
Ok. Нет проблем.
У меня есть хороший метод расширения, который, я думал, может сработать.
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();
}
Но нет! Если объект (Entity) не сериализуем, эта процедура вызывает у меня еще одно приятное (и вполне ожидаемое) исключение.
Итак ... Я изменяю подпрограмму и добавляю предложение 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();
}
Единственная проблема в том, что теперь я вернулся к исходной точке, где все мои объекты должны быть сериализуемыми, чтобы получить байтовый массив.
Хм. Не хорошо.
Итак, я собрал хак, чтобы перебрать все свойства объекта и сгенерировать строковое представление, из которого я мог бы построить байтовый массив. Это было некрасиво и неуместно, но это отчасти помогло.
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());
}
Но ...
В конце концов, то, что я все еще действительно хотел бы иметь, - это эффективно и правильно создавать байтовый массив из объекта, класс которого не помечен как сериализуемый . Каков наилучший способ сделать это?
Заранее спасибо!