2011-03-14 14 views
8

Estoy usando XmlDictionaryWriter para serializar objetos a una base de datos con el serializador de contrato de datos. Funciona de maravilla, tanto el tamaño como la velocidad son 2 veces mejores que el uso de texto/xml.XML binario .NET con diccionario pre-compartido

Sin embargo, voy a tener que lidiar con un enorme recuento de registros en mi base de datos, donde los bytes adicionales se traducen directamente en los gigabytes de tamaño de la base de datos. Es por eso que me encantaría reducir aún más el tamaño, utilizando un diccionario XML.

¿Cómo puedo hacer eso?

veo que método estático XmlDictionaryWriter.CreateBinaryWriter acepta el parámetro 2-nd de tipo IXmlDictionary. MSDN dice "XmlDictionary para usar como diccionario compartido".

En primer lugar he tratado de usar el proporcionado por el sistema aplicación:

XmlDictionary dict = new XmlDictionary(); 
string[] dictEntries = new string[] 
{ 
    "http://schemas.datacontract.org/2004/07/MyContracts", 
    "http://www.w3.org/2001/XMLSchema-instance", 
    "MyElementName1", 
    "MyElementName2", 
    "MyElementName3", 
}; 
foreach (string s in dictEntries) 
     dict.Add(s); 

El resultado es un framework .NET ignora completamente el diccionario, y todavía inserta las cuerdas anteriores como texto plano en lugar de sólo hacer referencia a una correspondiente entrada de diccionario.

Luego he creado mi propia implementación de IXmlDictionary:

class MyDictionary : IXmlDictionary 
{ 
    Dictionary<int, string> values = new Dictionary<int, string>(); 
    Dictionary<string, int> keys = new Dictionary<string, int>(); 

    MyDictionary() 
    { 
     string[] dictEntries = new string[] 
     { 
      "http://schemas.datacontract.org/2004/07/MyContracts", 
      "http://www.w3.org/2001/XMLSchema-instance", 
      "MyElementName1", 
      "MyElementName2", 
      "MyElementName3", 
     }; 

     foreach (var s in dictEntries) 
      this.Add(s); 
    } 

    static IXmlDictionary s_instance = new MyDictionary(); 
    public static IXmlDictionary instance { get { return s_instance; } } 

    void Add(string val) 
    { 
     if (keys.ContainsKey(val)) 
      return; 
     int id = values.Count + 1; 
     values.Add(id, val); 
     keys.Add(val, id); 
    } 

    bool IXmlDictionary.TryLookup(XmlDictionaryString value, out XmlDictionaryString result) 
    { 
     if (value.Dictionary == this) 
     { 
      result = value; 
      return true; 
     } 
     return this.TryLookup(value.Value, out result); 
    } 

    bool IXmlDictionary.TryLookup(int key, out XmlDictionaryString result) 
    { 
     string res; 
     if (!values.TryGetValue(key, out res)) 
     { 
      result = null; 
      return false; 
     } 
     result = new XmlDictionaryString(this, res, key); 
     return true; 
    } 

    public bool /* IXmlDictionary. */ TryLookup(string value, out XmlDictionaryString result) 
    { 
     int key; 
     if (!keys.TryGetValue(value, out key)) 
     { 
      result = null; 
      return false; 
     } 

     result = new XmlDictionaryString(this, value, key); 
     return true; 
    } 
} 

El resultado es - mis métodos TryLookup se llaman bien, sin embargo DataContractSerializer.WriteObject produce un documento vacío.

¿Cómo uso un diccionario pre-compartida?

¡Gracias de antemano!

P.S. No quiero meterme con XmlBinaryReaderSession/XmlBinaryWriterSession: no tengo "sesiones", en cambio tengo una base de datos de 10 GB a la que acceden muchos hilos a la vez. Lo que quiero es solo un diccionario estático predefinido.

Actualización: OK He descubierto que solo necesito llamar a "XmlDictionaryWriter.Flush". La única pregunta que queda es: ¿por qué la implementación de IXmlDictionary suministrada por el sistema no funciona como se esperaba?

Respuesta

0

para el XmlDictionaryWriter que necesita para utilizar la sesión.
example:

private static Stream SerializeBinaryWithDictionary(Person person,DataContractSerializer serializer) 
    { 
     var stream = new MemoryStream(); 
     var dictionary = new XmlDictionary(); 
     var session = new XmlBinaryWriterSession(); 
     var key = 0; 
     session.TryAdd(dictionary.Add("FirstName"), out key); 
     session.TryAdd(dictionary.Add("LastName"), out key); 
     session.TryAdd(dictionary.Add("Birthday"), out key); 
     session.TryAdd(dictionary.Add("Person"), out key); 
     session.TryAdd(dictionary.Add("http://www.friseton.com/Name/2010/06"),out key); 
     session.TryAdd(dictionary.Add("http://www.w3.org/2001/XMLSchema-instance"),out key); 

     var writer = XmlDictionaryWriter.CreateBinaryWriter(stream, dictionary, session); 
     serializer.WriteObject(writer, person); 
     writer.Flush(); 
     return stream; 
    } 
0

La única manera que forma capaz de replicar el problema con el IXmlDictionary no está siendo utilizado fue cuando mi clase no estaba decorado con un atributo DataContract. La siguiente aplicación muestra la diferencia de tamaños con clases decoradas y sin decoración.

using System; 
using System.Runtime.Serialization; 
using System.Xml; 

namespace XmlPresharedDictionary 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Console.WriteLine("Serialized sizes"); 
      Console.WriteLine("-------------------------"); 
      TestSerialization<MyXmlClassUndecorated>("Undecorated: "); 
      TestSerialization<MyXmlClassDecorated>("Decorated: "); 
      Console.ReadLine(); 
     } 

     private static void TestSerialization<T>(string lineComment) where T : new() 
     { 
      XmlDictionary xmlDict = new XmlDictionary(); 
      xmlDict.Add("MyElementName1"); 

      DataContractSerializer serializer = new DataContractSerializer(typeof(T)); 

      using (System.IO.MemoryStream stream = new System.IO.MemoryStream()) 
      using (var writer = XmlDictionaryWriter.CreateBinaryWriter(stream, xmlDict)) 
      { 
       serializer.WriteObject(writer, new T()); 
       writer.Flush(); 
       Console.WriteLine(lineComment + stream.Length.ToString()); 
      } 
     } 
    } 

    //[DataContract] 
    public class MyXmlClassUndecorated 
    { 
     public MyElementName1[] MyElementName1 { get; set; } 

     public MyXmlClassUndecorated() 
     { 
      MyElementName1 = new MyElementName1[] { new MyElementName1("A A A A A"), new MyElementName1("A A A A A") }; 
     } 
    } 

    [DataContract] 
    public class MyXmlClassDecorated 
    { 
     public MyElementName1[] MyElementName1 { get; set; } 

     public MyXmlClassDecorated() 
     { 
      MyElementName1 = new MyElementName1[] { new MyElementName1("A A A A A"), new MyElementName1("A A A A A") }; 
     } 
    } 

    [DataContract] 
    public class MyElementName1 
    { 
     [DataMember] 
     public string Value { get; set; } 

     public MyElementName1(string value) { Value = value; } 
    } 
} 
Cuestiones relacionadas