none
Reflection et classe héritant de ConfigurationSection RRS feed

  • Question

  • J'ai besoin de faire une application modulaire dont un des module stocke ses propriétés dans le app.config. J'ai créé une dll contenant ce fameux module. Il contient uniquement une classe héritant de ConfigurationSection et implémentant une interface générique de mon cru.

    Le noyau implémentant les modules n'a connaissance que de mon interface générique, les dll étant gérées par pluggin. J'utilise donc la reflection pour instancier ma classe contenue dans la dll.

    J'ai fait un test direct de ma dll sans passer par la reflection et mon objet charge bien ses propriétés depuis le app.config, en utilisant ce code :

     

    using System;
    using System.Linq;
    using System.Configuration;
    using System.IO;
    using Copilote.CanalUNC;
    using Copilote.CanalGenerique;
    
    namespace ConsoleApplication1
    {
     class Program
     {
     static void Main()
     {
      CanalUNC section = (CanalUNC)ConfigurationManager.GetSection("CanalUNC");
      Console.WriteLine("{0} ; {1}", section.DirectoryAsc, section.DirectoryAscSave);
      Console.ReadLine();
     }
     }
    }
    
    
    

     

    Par contre, je ne trouve pas de moyen pour le faire via la reflection ; mon code dans ce cas :

     

    using System;
    using System.Collections.Specialized;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Copilote.GestionLog;
    using Copilote.CanalGenerique;
    using System.IO;
    using System.Reflection;
    using System.Configuration;
    
    namespace TestEchanges
    {
     class Program
     {
     static void Main(string[] args)
     {
     LogSingleton.Instance.IsChronodate = true;
     LogSingleton.Instance.AddEntry(null, new LogEventArgs(TypeLog.Noyau, TypeErreur.Information, "Démarrage de l'application."));
     NameValueCollection appSettings = ConfigurationManager.AppSettings;
     NameValueCollection configSettings = null;
     for (int i = 0; i < appSettings.Count; i++)
     {
     if (appSettings.GetKey(i) == "Config")
      configSettings = (NameValueCollection)ConfigurationManager.GetSection(appSettings[appSettings.GetKey(i)]);
     }
     Assembly monAssembly = Assembly.LoadFrom(Path.Combine(Environment.CurrentDirectory, configSettings["Canal"]));
     // Introspection des types
     ICanalGenerique monCanal;
     foreach (Type type in monAssembly.GetTypes())
     {
     if (type.GetInterface(typeof(ICanalGenerique).FullName) != null)
     {
      // Un type correspond, création d'un objet
      monCanal = (ICanalGenerique)Activator.CreateInstance(type, ConfigurationManager.GetSection("CanalUNC"));
      // Liaison de l'événement à la fonction de traçage
      monCanal.RaiseLogEvent += LogSingleton.Instance.AddEntry;
    
      // Tentative de lecture
      MemoryStream monStream = new MemoryStream();
      monCanal.GetData(ref monStream);
    
      byte[] byteArray = new byte[monStream.Length];
      var count = monStream.Read(byteArray, 0, (int)monStream.Length);
    
      Console.WriteLine(Encoding.Default.GetString(byteArray));
     }
     }
    
     // On attend...
     Console.ReadLine();
     }
     }
    }
    
    

     

    Qu'est ce que je fait de travers ?

    Merci d'avance !




    • Type modifié Ciprian Duduiala mercredi 27 avril 2011 13:14 pas de réponse de l'utilisateur qui a lancé la question
    • Type modifié Ciprian Duduiala mercredi 4 mai 2011 13:04 solution trouvée
    • Modifié Julien BRUNEAU mercredi 11 mai 2011 14:03
    mercredi 20 avril 2011 09:40

Réponses

  • Bonjour,

    J'ai contourné mon problème en partant d'un objet déclaré en ConfigurationSection et en appelant mes méthodes interfacées via le InvokeMember...

          // Introspection des types
          foreach (Type monType in canalAssembly.GetTypes())
          {
            // Si la classe présente dans l'assembly implémente une ICanalGenerique, il s'agit d'un canal
            if (monType.GetInterface(typeof(ICanalGenerique).FullName) != null)
            {
              // On part du principe que tout objet géré par le noyau hérite de ConfigurationSection, ce qui permet de sauvegarder les propriétés dans le app.config
              ConfigurationSection monCanal = null;
              // Un type correspond, création d'un objet
              monCanal = (ConfigurationSection)Activator.CreateInstance(monType);
              // On lui colle la section du app.config correspondante
              monCanal = (ConfigurationSection)ConfigurationManager.GetSection(monType.Name);
    
              try
              {
                // Liaison de l'événement à la fonction de traçage
                // On fait un appel du style monCanal.RaiseLogEvent += LogSingleton.Instance.AddEntry; mais en utilisant la reflection
                // On récupère un pointeur sur l'événement
                EventInfo evClick = monType.GetEvent("RaiseLogEvent");
                // Puis son type
                Type tDelegate = evClick.EventHandlerType;
                // On fabrique un handler sur la fonction à lier
                MethodInfo myHandler = typeof(GestionLog).GetMethod("AddEntry", BindingFlags.Public | BindingFlags.Instance);
                // On crée le délégué sur la fonction à lier
                Delegate d = Delegate.CreateDelegate(tDelegate, GestionLog.Instance, myHandler);
                // On récupère le pointeur sur la fonction
                MethodInfo addHandler = evClick.GetAddMethod();
                // Par reflection, on lie le bazard
                addHandler.Invoke(monCanal, new Object[] { d });
              }
              catch
              {
                GestionLog.Instance.AddEntry(null, new LogEventArgs(TypeLog.Noyau, TypeErreur.Information, "Erreur de liaison du Log au Canal."));
              }
    
              // Tentative de lecture
              monStream = new MemoryStream();
              // Puisqu'on sait qu'il s'agit d'un canal, l'objet doit avoir la methode 'GetData'
              // On fait un appel du style monCanal.GetData(ref monStream); mais en utilisant la reflection
              bool Result = false;
              try
              {
                Result = (bool)monCanal.GetType().InvokeMember("GetData", BindingFlags.Default | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, monCanal, new object[] { monStream });
              }
              catch (MissingMethodException mme)
              {
                // Dans le cas ou la méthode n'est pas un membre de ce type ...
                Console.WriteLine("Méthode 'GetData' introuvable : " + mme.Message);
              }
    
    
    Et c'est opérationnel...

    mercredi 4 mai 2011 13:02

Toutes les réponses

  • Bonjour,

    Et que se passe t'il à l'exécution ? Savoir si l'assembly ne se charge pas, si on ne trouve pas l'interface, si l'objet ne s'instancie pas où si il n'arrive pas à lire le fichier de config une fois instancié aiderait à mieux savoir où diriger son attention.

    Si c'est l'interface qu'on ne trouve pas, déjà commencer par lister le nom complet des interfaces pour bien vérifier que c'est la même (une erreur classique est souvent de penser qu'un type est le même parce qu'il a le même nom même si il est dans deux DLLs différentes).


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".
    mercredi 20 avril 2011 13:59
    Modérateur
  • Bonjour, Julien,

    Avez-vous réussi à résoudre votre problème ? Merci pour partager les résultats avec nous, afin que d'autres personnes avec le même problème puissent profiter de ces solutions.

    Cordialement,

    Cipri
    Suivez MSDN sur Twitter   Suivez MSDN sur Facebook


    Ciprian DUDUIALA, MSFT  
    •Nous vous prions de considérer que dans le cadre de ce forum on n’offre pas de support technique et aucune garantie de la part de Microsoft ne peut être offerte.

    mardi 26 avril 2011 06:23
  • Bonjour, je me trompe peut-être mais Activator.CreateInstance attend en paramètre en plus du type, un tableau d'objet.

     

    Object[] param = new object[] { ConfigurationManager.GetSection("CanalUNC") };
          monCanal = (ICanalGenerique)Activator.CreateInstance(type, param);
    

     

    Et un complément sur votre message d'erreur pourrait nous aider.


    Cordialement, Pascal.

    Développeur Wpf/SilverLight/WinPhone7

    mardi 26 avril 2011 18:07
  • Bonjour,

    J'ai contourné mon problème en partant d'un objet déclaré en ConfigurationSection et en appelant mes méthodes interfacées via le InvokeMember...

          // Introspection des types
          foreach (Type monType in canalAssembly.GetTypes())
          {
            // Si la classe présente dans l'assembly implémente une ICanalGenerique, il s'agit d'un canal
            if (monType.GetInterface(typeof(ICanalGenerique).FullName) != null)
            {
              // On part du principe que tout objet géré par le noyau hérite de ConfigurationSection, ce qui permet de sauvegarder les propriétés dans le app.config
              ConfigurationSection monCanal = null;
              // Un type correspond, création d'un objet
              monCanal = (ConfigurationSection)Activator.CreateInstance(monType);
              // On lui colle la section du app.config correspondante
              monCanal = (ConfigurationSection)ConfigurationManager.GetSection(monType.Name);
    
              try
              {
                // Liaison de l'événement à la fonction de traçage
                // On fait un appel du style monCanal.RaiseLogEvent += LogSingleton.Instance.AddEntry; mais en utilisant la reflection
                // On récupère un pointeur sur l'événement
                EventInfo evClick = monType.GetEvent("RaiseLogEvent");
                // Puis son type
                Type tDelegate = evClick.EventHandlerType;
                // On fabrique un handler sur la fonction à lier
                MethodInfo myHandler = typeof(GestionLog).GetMethod("AddEntry", BindingFlags.Public | BindingFlags.Instance);
                // On crée le délégué sur la fonction à lier
                Delegate d = Delegate.CreateDelegate(tDelegate, GestionLog.Instance, myHandler);
                // On récupère le pointeur sur la fonction
                MethodInfo addHandler = evClick.GetAddMethod();
                // Par reflection, on lie le bazard
                addHandler.Invoke(monCanal, new Object[] { d });
              }
              catch
              {
                GestionLog.Instance.AddEntry(null, new LogEventArgs(TypeLog.Noyau, TypeErreur.Information, "Erreur de liaison du Log au Canal."));
              }
    
              // Tentative de lecture
              monStream = new MemoryStream();
              // Puisqu'on sait qu'il s'agit d'un canal, l'objet doit avoir la methode 'GetData'
              // On fait un appel du style monCanal.GetData(ref monStream); mais en utilisant la reflection
              bool Result = false;
              try
              {
                Result = (bool)monCanal.GetType().InvokeMember("GetData", BindingFlags.Default | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, monCanal, new object[] { monStream });
              }
              catch (MissingMethodException mme)
              {
                // Dans le cas ou la méthode n'est pas un membre de ce type ...
                Console.WriteLine("Méthode 'GetData' introuvable : " + mme.Message);
              }
    
    
    Et c'est opérationnel...

    mercredi 4 mai 2011 13:02