Stanotte ci ho sbattuto la testa per un paio di ore e alla fine mi sono convinto che la serializzazione delle entities di Linq To SQL sostanzialmente... non è possibile! Il mio primo tentativo banale è stato di serializzarle in binario ma immediatamente mi sono reso conto che vengono tutte generate senza l'attributo [Serializable]. Perciò ho provato a scrivere le partial class e metterci l'attributo ma alla fine mi sono dovuto arrendere per il semplice fatto che EntitySet e EntityRef non sono serializzabili a loro volta. A questo punto ho deciso che a me bastava serializzare e quindi anche Xml sarebbe andato bene ma... XmlSerializer non ci riesce, perchè da un errore relativo una circular reference.

Il fatto è che se curiosate nel designer di Visual Studio 2008 scoprirete in fretta una proprietà "Serialization Mode" che può assumere i valori None e Unidirectional. Assegnando Unidirectional la classe generata verrà decorata con gli attributi DataContract e DataMember (e meno male!) e quindi diventerà serializzabile per mezzo del DataContractSerializer o del NetDataContractSerializer. Ecco un esempio di come fare espresso sotto forma di 2 extension methods:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using System.IO;
   6: using System.Runtime.Serialization;
   7: using System.Xml.Serialization;
   8:  
   9: namespace Elite.Linq2SQL
  10: {
  11:     public static class Extensions
  12:     {
  13:         /// <summary>
  14:         /// Serializes the specified obj.
  15:         /// </summary>
  16:         /// <param name="obj">The obj.</param>
  17:         /// <returns></returns>
  18:         public static string ToXml<T>(this T obj)
  19:         {
  20:             using (MemoryStream stream = new MemoryStream())
  21:             {
  22:                 NetDataContractSerializer serializer = new NetDataContractSerializer();
  23:                 serializer.Serialize(stream, obj);
  24:                 return Encoding.UTF8.GetString(stream.ToArray());
  25:             }
  26:         }
  27:  
  28:         /// <summary>
  29:         /// Deserializes the specified data.
  30:         /// </summary>
  31:         /// <typeparam name="T"></typeparam>
  32:         /// <param name="data">The data.</param>
  33:         /// <returns></returns>
  34:         public static T FromXml<T>(this string xml)
  35:         {
  36:             using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
  37:             {
  38:                 NetDataContractSerializer serializer = new NetDataContractSerializer();
  39:                 return (T)serializer.Deserialize(stream);
  40:             }
  41:         }
  42:     }
  43: }

Detto questo può sembrare che bene o male (io propendo per il male...) si sia risolta la questione ma prima di cantare vittoria è opportuno ragionare sul significato di Unidirectional. In breve questo termine sottindente il fatto che la serializzazione comprende esclusivamente l'oggetto radice della gerarchia ed esclude qualunque oggetto che crei un riferimento esterno. Perciò se avete una proprietà collection popolata di oggetti questa non verrà nemmeno presa in considerazione. Il motivo è semplice: così non ci saranno mai circular references.

Quindi tirate le somme si può dire che è meglio togliersi subito dalla testa l'idea di serializzare le entities di Linq To SQL a meno che non vi vogliate impegnare ad implementare ISerializable, che davvero non è la cosa più semplice e immediata che mi venga in mente. Questo, almeno dal mio punto di vista di programmatore ASP.NET significa che le entities non potranno mai essere messe nella ViewState, oppure che se decido inopinatamente di tenerne anche una sola in Session, l'applicazione non potrà mai utilizzare lo State Server di ASP.NET perchè il suo uso implica la serializzazione degli oggetti...

La morale è quindi: occhio a quello che fate!


Commenti (1) -

# | Federico | 20.08.2009 - 19.52

Ciao Andrea,

ti ringrazio per il post: è praticamente un'ora che sto cercando (invano) di serializzare delle entities LINQ2SQL nel viewstate di un web user control ma credo che desisterò ed utilizzero la session per memorizzare le entities più "critiche".

Ciao
Federico

Aggiungi Commento