У меня есть сценарий, в котором я синхронизирую данные между несколькими ОЧЕНЬ разными системами. (Сами данные похожи, но таблицы в разных системах имеют совершенно разные форматы.) Чтобы облегчить эту синхронизацию, у меня есть таблица базы данных, в которой хранятся хэши объектов из каждой из систем вместе с ключами элементов и другой соответствующей информацией. Когда хеш объекта из одной системы изменяется, я обновляю другой.
Моя таблица базы данных выглядит примерно так.
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, похоже, указывают на то, что это правда.)
Для одной из систем у меня есть возможность сделать все объекты моей базы данных сериализуемыми, так что это здорово. . Генерируются хэши, все синхронизируется, и мир прекрасен!
Для другой системы дела не так хороши . Мне передан контекст базы данных из модели платформы 4 (сначала код), и объекты НЕ сериализованы .
Когда я пытаюсь преобразовать в байты, используя что-то вроде следующего, .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();
}
О нет! Если объект (сущность) не сериализуем, эта процедура выдает мне еще одно приятное маленькое (и полностью ожидаемое) исключение.
Итак ... я модифицирую подпрограмму и добавляю предложение 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());
}
Но ...
После всего этого, я все еще хотел бы иметь возможность эффективно и правильно создавать байтовый массив из объекта, класс которого не помечен как сериализуемый . Как лучше всего этого добиться?
Заранее благодарю!