MetaTips

Le blog de l'edition logiciel en ligne

Articles

Les collections de type dictionnaires generiques serialisables

Posted by Allouche Mathieu on December 2, 2009 at 4:40 AM

Les

Les "Generics" est l'une des fonctionnalités les plus marquantes depuis l'apparition des technologies ASP.Net. Les Generics apportent la capacité de réutilisation générique des algorithmes. Couramment utilisé en C++ comme Template, l’absence des Générics sous C# fut un reproche des utilisateurs auprès de Microsoft.

Type : Correction du framework 2.0 - Code complet fourni (Syntaxe 100% compatible, ne changez rien)

Niveau : 3/5

Compatibilité / s'applique à :

· Visual studio 2005, 2008, 2010

· C#

· .NET Framework 4.0, 3.5, 3.0, 2.0

Au delà des aspects de générisation, les Generics ont apportés aussi la possibilité de gérer des collections typées notamment au travers des classes Dictionary et SortedList, permettant ainsi d’éviter les problématiques le boxing et de cast de chacun des éléments, et ainsi améliorer les performances des énumérateurs. Le seul problème, c’est que Microsoft, n’a jamais fournit de solutions simples, permettant la sérialisation des classes qui implémentent l’interface IDictionary.

Voici un exemple de code qui ne fonctionne pas :

SortedList<string, string> MyList = new SortedList<string, string>();

XmlSerializer serializer = new XmlSerializer(MyList.GetType());

StringBuilder sb = new StringBuilder();

StringWriter writer = new StringWriter(sb);

serializer.Serialize(writer, MyList);

writer.Close();

string XML = sb.ToString();

Le message d'erreur :

Le type System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] n'est pas pris en charge, car il implémente IDictionary.

Comment résoudre :

En théorie, pour remédier au problème, la solution parait très simple. Il suffit de ré-implémenter ISerializable,IXmlSerializable, et de sérialiser et/ou désérialiser chacun des éléments DictionaryEntry<, >. Le premier problème que l’on rencontre vient de la classe générique DictionaryEntry<, >, que l’on doit réécrire, car il manque les directives nécessaires à la sérialisation sur celles fournit par Microsoft. Le deuxième problème vient d’un manque d’accessibilité de certains objets de la classe Dictionary, compliquant ainsi le code, et en diminuant d’autant les performances.

Parmi les autres impératifs que je me suis imposé, je souhaitais que la solution n’impose pas le type de la clé afin de rester complément générique. D'autre part il est primordial que la solution puisse générer des XML compatible avec les conventions usuelles de sérialisation comme le font d’autre classe. Et enfin, certain d’entres-nous ont surement migrés leurs projets depuis une version du framework, et ont fait des XSL / XSLT que vous ne souhaitez pas modifier. Pour cela, la solution proposée devra posséder 2 niveaux de sérialisation xml.

Exemple d'un XML non conventionnel :

<Cars>

<DictionaryEntry>

<Key>

<Int64>633905104652725254</Int64>

</Key>

<Value>

<Car>

<Name>Car 1</Name>

<Color>Red</Color>

</Car>

</Value>

</DictionaryEntry>

<DictionaryEntry>

<Key>

<Int64>633905104652775039</Int64>

</Key>

<Value>

<Car>

<Name>Car 2</Name>

<Color>Blue</Color>

</Car>

</Value>

</DictionaryEntry>

</Cars>

Exemple de XMLs que l'on attendrait :

Version Framework 1.1

<Cars>

<DictionaryEntry>

<Key xsi:type="xsd:long">633905102626663582</Key>

<Value xsi:type="Car">

<Name>Car 1</Name>

<Color>Red</Color>

</Value>

</DictionaryEntry>

<DictionaryEntry>

<Key xsi:type="xsd:long">633905102626993615</Key>

<Value xsi:type="Car">

<Name>Car 2</Name>

<Color>Blue</Color>

</Value>

</DictionaryEntry>

</Cars>

Version Framework 2.0 et supérieure

<DictionaryOfInt64Car>

<DictionaryEntry>

<Key>633905104652725254</Key>

<Value>

<Name>Car 1</Name>

<Color>Red</Color>

</Value>

</DictionaryEntry>

<DictionaryEntry>

<Key>633905104652775039</Key>

<Value>

<Name>Car 2</Name>

<Color>Blue</Color>

</Value>

</DictionaryEntry>

</DictionaryOfInt64Car>

Solution :

Voici un exemple de classe permettant la sérialisation binaire et la sérialisation XML, de n’importe quel type de données. Cerise sur le gâteau, elle se montre bien plus véloce que certaine autre classe du framework.

Exemple d'utilisation

Le code :

using System;

using System.Linq;

using System.Text;

using System.Xml.Serialization;

using System.IO;

using ASProjects.BC30.Commons.DataType.Generics;

namespace ASProjects.BC30.App.Demo

{

/// <summary>

/// This

/// </summary>

/// <remarks>

/// <pre>

/// Date: 17/07/2007

///

/// Author: BBSonic

///

/// Description:

///

/// Version:

/// 1.0 17/07/2007 - Initial version BBSonic

/// </pre>

/// </remarks>

public class Program

{

/// <summary>

/// Classe qui servira de clé

/// </summary>

public class Identity

{

public long Serial { get; set; }

public Identity()

{

Serial = DateTime.Now.Ticks;

}

}

/// <summary>

/// Classe qui servira de valeur

/// </summary>

public class Car

{

public string Name { get; set; }

public string Color { get; set; }

}

static void Main(string[] args)

{

// Creation de la collection

Dictionary<Identity, Car> Cars = new Dictionary<Identity, Car>();

// Remplissage de la collection

Cars.Add(new Identity(), new Car { Name = "Car 1", Color = "Red" });

Cars.Add(new Identity(), new Car { Name = "Car 2", Color = "Blue" });

// Serialisation

XmlSerializer serializer = new XmlSerializer(Cars.GetType());

StringBuilder sb = new StringBuilder();

StringWriter writer = new StringWriter(sb);

serializer.Serialize(writer, Cars);

writer.Close();

string XML = sb.ToString(); //Contient le XML

}

}

}

Le XML :

<DictionaryOfIdentityCar>

<DictionaryEntry>

<Key>

<Serial>633905146780954358</Serial>

</Key>

<Value>

<Name>Car 1</Name>

<Color>Red</Color>

</Value>

</DictionaryEntry>

<DictionaryEntry>

<Key>

<Serial>633905146780974412</Serial>

</Key>

<Value>

<Name>Car 2</Name>

<Color>Blue</Color>

</Value>

</DictionaryEntry>

</DictionaryOfIdentityCar>

Remarque : Lorsque vous activez la serialisation maximum, le XML devient très verbeux. Les performances se dégradent rapidement, si le nombre d’éléments dans la collection est important, comme c’était déjà le cas avec laHashtable du framework 1.x.

Dictionary<Identity, Car> Cars = new Dictionary<Identity, Car>(Dictionary<Identity,Car>.SerializationLevel.Full);

Code source de la classe

using System;

using System.Collections;

using System.Runtime.Serialization;

using System.Security.Permissions;

using System.Xml.Serialization;

[assembly: SecurityPermission(SecurityAction.RequestMinimum, Execution = true)]

namespace ASProjects.BC30.Commons.DataType.Generics

{

/// <summary>

/// This classe represent an generic serializable element

/// </summary>

/// <remarks>

/// <pre>

/// Date: 17/07/2008

///

/// Author: BBSonic

///

/// Description:

///

/// Version:

/// 1.0 17/07/2008 - Initial version BBSonic

/// </pre>

/// </remarks>

[Serializable]

[XmlRoot("DictionaryEntry")]

public class DictionaryEntry<K, V> : ISerializable

{

#region FIELDS

public K Key { get; set; }

public V Value { get; set; }

#endregion

#region Constructors

public DictionaryEntry() { }

public DictionaryEntry(K key, V value)

{

this.Key = key;

this.Value = value;

}

#endregion

#region ISerializable Members

/// <summary>

/// DictionaryEntry Constructor

/// </summary>

/// <param name="info">SerializationInfo</param>

/// <param name="context">StreamingContext</param>

public DictionaryEntry(SerializationInfo info, StreamingContext context)

Categories: Developpements

Post a Comment

Oops!

Oops, you forgot something.

Oops!

The words you entered did not match the given text. Please try again.

Already a member? Sign In

0 Comments