Различные методы сериализации полей в поле ntext SQL при сохранении обратной совместимости
TL, DR: мы сериализовали некоторые данные в некоторых таблицах нашей базы данных SQL. К сожалению, эта методика сериализации тратит много места для символов разметки. Мы нашли новый, более эффективный способ: сохранить обратную совместимость, безопасно ли придерживаться следующей логики? -> Когда происходит сериализация, она всегда происходит с использованием нового, более эффективного способа. Когда происходит десериализация, мы проверяем, использует ли строка новый формат сериализации или старый -> затем мы десериализовываем с помощью соответствующего метода. Это надежно? Можно ли это использовать в производстве? Нет ли каких-либо тонких проблем с этим подходом?
Привет. Я работаю над приложением, которое взаимодействует с базой данных SQL. Чтобы выполнить конкретные бизнес-требования, мы сериализовали некоторые данные в специальном столбце наших таблиц БД типа ntext. По сути, в каждой ячейке этого столбца мы сериализуем массив объекта "Атрибут", поэтому typeof(T) равен Attributo []:
Определение "Атрибут" выглядит следующим образом:
public class Attributo
{
public virtual String Nome { get; set; }
public virtual String Valore { get; set; }
public virtual String Tipologia { get; set; }
}
- десериализация для считывания фактических значений:
XMLUtilities.Deserialize<Attributo[]>(value));
Сериализация для хранения значений в столбце (для каждой строки..):
XMLUtilities.Serialize (attributes.ToArray ());
И это вспомогательный класс, который использует объект XmlSerializer:
public static class XMLUtilities
{
public static T Deserialize<T>(String xmlString)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (TextReader reader = new StringReader(xmlString))
{
return (T) serializer.Deserialize(reader);
}
}
public static String Serialize<T>(T xmlObject)
{
MemoryStream stream = new MemoryStream();
XmlSerializer serializer = new XmlSerializer(typeof(T));
XmlSerializerNamespaces xmlnsEmpty = new XmlSerializerNamespaces();
xmlnsEmpty.Add("", "");
serializer.Serialize(stream, xmlObject, xmlnsEmpty);
stream.Seek(0, SeekOrigin.Begin);
using (StreamReader reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
}
Теперь проблема с этой техникой заключается в том, что она тратит много места для символов разметки. Это пример строки, которая хранится в БД:
<?xml version="1.0"?>
<ArrayOfAttributo>
<Attributo>
<Nome>Leakage_test_Time_prg1_p1</Nome>
<Valore>4</Valore>
<Tipologia>Single</Tipologia>
</Attributo>
<Attributo>
<Nome>Leakage_air_Volume_p1</Nome>
<Valore>14</Valore>
<Tipologia>Single</Tipologia>
</Attributo>
</ArrayOfAttributo>
Итак, мы нашли более краткий способ сериализации этих атрибутов [], который производит такой вид вывода:
<ArrayOfA xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<A>
<N>Leakage_test_Time_prg1_p1</N>
<V>4</V>
<T>Single</T>
</A>
<A>
<N>Leakage_air_Volume_p1</N>
<V>14</V>
<T>Single</T>
</A>
</ArrayOfA>
затем, чтобы сохранить обратную совместимость, что является основной проблемой, мы реализовали следующую логику:
- Во время сериализации:
мы всегда сериализуем новым, более лаконичным способом
- Во время десериализации:
мы проверяем, начинается ли строка с:
<?xml version="1.0"?>
или нет. Если это так, то это старая запись, поэтому мы десериализовали ее старым способом. В противном случае мы десериализуем с использованием нового формата.
Мы добились этого, украсив "Атрибут" следующим образом:
[DataContract(Name = "A", Namespace= "")]
public class Attributo
{
[DataMember(Name = "N")]
public virtual String Nome { get; set; }
[DataMember(Name = "V")]
public virtual String Valore { get; set; }
[DataMember(Name = "T")]
public virtual String Tipologia { get; set; }
}
и путем внесения следующих изменений в наши методы сериализации / десериализации, которые теперь для нового метода сериализации полагаются на объект DataContractSerializer:
public static T Deserialize<T>(String xmlString)
{
//let's see if it's an old-style entry...
if (xmlString.StartsWith("<?xml version=\"1.0\"?>\r\n<ArrayOfAttributo>"))
{
try
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (TextReader reader = new StringReader(xmlString))
{
return (T)serializer.Deserialize(reader);
}
}
catch { }
}
//..then it must be a new-style one
DataContractSerializer ser = new DataContractSerializer(typeof(T));
using (Stream s = _streamFromString(xmlString))
{
return (T) ser.ReadObject(s);
}
}
public static String Serialize<T>(T xmlObject)
{
MemoryStream stream1 = new MemoryStream();
DataContractSerializer ser = new DataContractSerializer(typeof(T));
ser.WriteObject(stream1, xmlObject);
stream1.Position = 0;
StreamReader sr = new StreamReader(stream1);
string xmlString = sr.ReadToEnd();
return xmlString;
}
private static Stream _streamFromString(string s)
{
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
Кажется, что все работает с этим подходом, но мы хотим оценить все возможные риски, прежде чем продолжить. Это безопасно для использования в производстве?
1 ответ
Еще одна вещь, которую нужно иметь в виду при десериализации старой записи:
- десериализовать старый вход в старом стиле
- сериализовать старую запись в новом стиле
- сохраните новую запись в старом стиле и удалите запись в старом стиле.
Тебе хорошо идти.