2010-09-26 4 views
6

Sto riscontrando un problema con la deserializzazione XML che mi sconcerta.Perché XmlSerializer.Deserialize genera un System.IO.FileLoadException?

Sto costruendo un'applicazione che supporta la personalizzazione locale di vari servizi che utilizza. Ho implementato una classe astratta ServiceLocator i cui metodi restituiscono vari oggetti. Ogni installazione personalizzata è responsabile dell'implementazione di una sottoclasse di questo e fornisce implementazioni di tali metodi. La carne di questa classe si presenta così:

public abstract class ServiceLocator 
{ 
    public static void Initialize(string customFeaturesPath) 
    { 
     Assembly a = Assembly.LoadFrom(customFeaturesPath); 
     Type t = a.GetExportedTypes() 
      .AsEnumerable() 
      .Where(x => x.IsSubclassOf(typeof (ServiceLocator))) 
      .First(); 
     Default = (ServiceLocator)a.CreateInstance(t.FullName); 
    } 

    public static ServiceLocator Default { get; private set; } 

    public abstract DefaultValuesContainer CreateDefaultValuesContainer(); 
} 

Questo funziona bene: ho il percorso per il costume presenta assembly dal file di configurazione dell'applicazione, il programma chiama Initialize, e quindi l'applicazione può chiamare i vari metodi su ServiceLocator.Default e restituiscono le implementazioni personalizzate appropriate dei servizi.

Uno di questi servizi è uno DefaultValuesContainer. Questo è un oggetto semplice che espone le proprietà i cui valori devono essere mantenuti in un file di impostazioni utente. L'idea è che posso serializzare questo oggetto in una singola impostazione utente di tipo string. È un file di impostazioni utente che non si desidera modificare manualmente, ma mi piace.

Ecco una concreta attuazione di ServiceLocator.CreateDefaultValuesContainer:

protected override DefaultValuesContainer CreateDefaultValuesContainer(string serializedXml) 
{ 
    DefaultValuesContainer c = new ClientDefaultValuesContainer(); 

    if (string.IsNullOrEmpty(serializedXml)) 
    { 
     return c; 
    } 
    XmlSerializer x = new XmlSerializer(c.GetType()); 
    return (DefaultValuesContainer) x.Deserialize(new StringReader(serializedXml)); 
} 

Ora ecco la cosa.

Ho realizzato test di unità per questo utilizzando NUnit. Quando eseguo i test nella classe di test che esercita le funzioni personalizzate del client, funzionano. Quando eseguo l'intera suite di test, l'ultima riga del metodo di cui sopra genera questa eccezione:

System.InvalidOperationException : There is an error in XML document (0, 0). 
    ----> System.IO.FileLoadException : Could not load file or assembly 'ClientCustomFeatures, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. Invalid pointer (Exception from HRESULT: 0x80004003 (E_POINTER)) 
    ----> System.ArgumentNullException : Value cannot be null. 
Parameter name: path1 

Sono un po 'perplesso sul perché. Il metodo SetUp viene ancora eseguito e ServiceLocator.Default restituisce ancora un oggetto di tipo ClientServiceLocator, che significa che ha caricato l'assembly ClientCustomFeatures. In effetti, lo stesso metodo che lancia l'eccezione è nell'assembly che mi viene detto che non può essere caricato.

Che cosa sta cercando di fare il XmlSerializer qui? Perché sta tentando di caricare un assembly già caricato? Cosa diavolo significa "Puntatore non valido"? E soprattutto, come dovrei fare il debug di qualcosa del genere?

+2

Non so che errore specifico, ma si noti che XmlSerializer utilizza la generazione di codice e la compilazione dinamica, quindi sta tentando di creare un * nuovo * assembly che fa riferimento a quelli di cui ha bisogno. Sembra che questo nuovo assembly (generato) non sia soddisfatto del riferimento. Sono (per esempio) tutti i tipi necessari coinvolti 'pubblico'? –

+3

È sicuramente un problema di riferimento. Apri ClientCustomFeatures.dll in reflector e cerca eventuali riferimenti che potresti non avere. –

risposta

0

Ho riscontrato problemi con il caricatore di assiemi (Fusion?) Quando un assieme carica un altro assieme con riferimenti (non GAC). YourDLL.XmlSerializers.dll potrebbe essere uno di questi assemblaggi. Prova a disattivare l'opzione di Visual Studio per generare automaticamente un assembly di serializzazione XML (Opzioni del progetto): rimuoverà l'assembly aggiuntivo (e quindi la dipendenza da esso).

0

Fusion Log Viewer

per diagnosticare eventuali problemi di assemblaggio di carico come questi, date un'occhiata alla Fusion Log Viewer (AKA Fuslogvw.exe).

Fusion == il componente .NET che individua e carica gli assembly.

0

provare a sostituire la riga:

XmlSerializer x = new XmlSerializer(c.GetType()); 

con:

XmlSerializer x = new XmlSerializer(c.GetType(), new Type[] { typeof(DefaultValuesContainer), typeof(ClientDefaultValuesContainer) }); 
1

Se l'assembly personalizzato non sa dove caricare l'assembly che contiene la classe ClientCustomFeatures, questo accadrà. Ciò si verifica quando l'assembly personalizzato è stato distribuito in una posizione che non si trova nel percorso dell'assembly principale e l'assembly principale non è nel gac. Quindi, se le tue asserzioni personalizzate sono caricate dalle sottodirectory del tuo assembly principale questo dovrebbe andare via. Tuttavia, se si trovano in luoghi arbitrari, si verificherà un problema perché è necessario caricare l'assembly principale in quanto devono accedere al tipo ClientCustomFeatures.

+0

Bene, tutti gli assembly sono nella stessa directory, quindi non penso che sia il caso qui. –

+1

Ciao Robert, dai un'occhiata a questo sito: http://msdn.microsoft.com/en-us/library/aa302290.aspx Ha un numero di ottime idee per la risoluzione dei problemi che si riferiscono al problema che stai ricevendo e ai motivi per cui potrebbe essere in corso. C'è anche un link a uno strumento che dovrebbe aiutarti a risolvere questo problema. Dato che non abbiamo accesso a tutto il tuo codice, non possiamo farlo per te, ma sarei molto interessante sapere qual è il risultato di questa piccola avventura :) Dovresti dare un'occhiata alle "Eccezioni nel costruttore" "sezione – Kell

+0

Appena pensato a qualcosa: NUnit viene eseguito in una posizione temporanea della cache e potrebbe non disporre dei privilegi appropriati per caricare tutti gli assembly e generare classi. Ma leggi l'URL di cui sopra, ha molte più vie di indagine o tu. – Kell