none
Wie greife ich auf eine Methode einer "anderen" Dll zu RRS feed

  • Frage

  • Hallo zusammen,

    jetzt bin auch ich soweit meine erste Frage im Forum zu stellen und hoffe wie die meisten anderen nicht eine Lösung übersehen zu haben ;)

    Mein Problem ist folgendes.

    Ich habe mir eine Dll geschrieben die ich für Logging / Reporting Zwecke verwende.

    namespace MyCompany.LogProvider
    {
      public class Reporting    
      {...}
    }

    Diese habe ich bereits in verschiedenen kleinen Projekten erfolgreich verwendet. Hierzu habe ich bisher in meinen Anwendungen (Exe) einen Verweis auf die Dll und eine Using Direktive hinzugefügt.

    ...
    using MyCompany.LogProvider
    
    namespace MyCurrentApp
    {
      public class MyCurrentAppForm : Form
      {
        MyCompany.LogProvider.Reporting logging;
        public MyCurrentAppForm()
        {
          Logging = new MyCompany.LogProvider.Reporting();
          Logging.Report("Diese Information wird geloggt");
        }
      }
    }

    Soweit passt alles. Jetzt habe ich aber ein größeres Projekt angefangen welches sich auf mehrere Dlls verteilen wird. Ich möchte aus diesen anderen Dlls auch Logs generieren können. Diese sollen in das Logfile der jeweiligen Anwendung schreiben (insofern diese ein Logging instanziiert hat). Also in etwa so.

    Namespace MyCompany.AnotherDll
    {
      public class AnotherDllClass
      {
        public void Report(string strReport)
        {
          if (MethodPointer != null)
            MethodPointer(strReport);
        }
      }
    }

    Hierfür habe ich bisher keine Möglichkeit gefunden. Die Delegate Beispiele, die ich mir bisher angeschaut habe waren immer innerhalb eines Namespaces. Mein Fall hat im Prinzip mindestens 3. Das Logging selbst ist in einem Namespace (dll) implementiert und wird in einem zweiten Namespace (MyCurrentApp) verwendet. Wenn diese CurrentApp weitere Dlls läd, so sollen diese aber auch auf das Logging zugreifen können.

    Da die Dlls von verschiedenen Anwendungen verwendet werden können ist der Name der Klasse "MyCurrentApp" auch nicht fix. Sonst könnte ich vermutlich nach der Methode über Reflection "suchen".

    Ich hoffe, dass ich das Problem verständlich erklärt habe und das sich jemand findet, der mir hier weiterhelfen kann :)

    Vielen Dank schon im Voraus

    Alex

    Dienstag, 30. Juli 2013 23:19

Antworten

  • Hallo Alex,

    bei sowas bietet sich Dependency Injection an : Du hast nur noch ein Interface, welches eine konkrete Implementierung des Loggers von "extern" zugeschoben bekommt.

    Ein gutes beispiel ist zB das hier : LINK

    Grüße

    Mittwoch, 31. Juli 2013 06:13
  • Hi Alexander,

    die Idee war jetzt auch nicht den Logger zu verwenden, sondern mal zu schauen wie die es gelöst haben.

    Ich denk mal du wird ähnliche Anforderungen haben. ;)

    Zu deinem Problem. Du solltest die Basis Klasse deines Loggers Referenzieren und das Interface verwenden. Sollte bei der Verwendung etwa so aussehen.

    1.  Namespace MyCompany.AnotherDll
    2.  {
    3.    public class AnotherDllClass
    4.    {
    5.      Ideri.LogProvider.IReporting _logger;
    6.      public void Report(string strReport)
    7.      {
    8.        if (logger != null)
    9.          logger.Report(strReport);
    10.     }
    11.     public int
    12. Initialize( Ideri.LogProvider.IReporting logger)
    13.     {
    14.        ...
    15.     }
    16.   }
    17. }

    Ich hab jetzt nur den Text Kopiert und die Stellen angepasst, ob es so Funktioniert hab ich jetzt nicht probiert.

    Grundlegend würde ich aber überlegen es nicht mit DI zu machen sondern über das Factory Pattern (hier mal für einen Logger)  und den Logger dann als Static (SingelTon) zu implementieren. Das hat den Vorteil, das die Klassen nach außen keine Schnittstelle mit ILogger haben und du den Logger nicht gegebenen falls durch die ganze Anwendung durch reichen musst.

    MEF wäre auch noch eine Lösung.


    MFG

    Björn
    Mittwoch, 31. Juli 2013 14:52

Alle Antworten

  • Hallo Alex,

    bei sowas bietet sich Dependency Injection an : Du hast nur noch ein Interface, welches eine konkrete Implementierung des Loggers von "extern" zugeschoben bekommt.

    Ein gutes beispiel ist zB das hier : LINK

    Grüße

    Mittwoch, 31. Juli 2013 06:13
  • Hallo Pawel,

    danke für den Tipp. Leider komme ich damit nicht viel weiter. Ich bin nicht sicher, in wie weit das mein Problem des "delegaten in einen anderen Namespace" löst, zumindest habe ich aber folgendes probiert und weiß nun nicht, was ich falsch gemacht habe.

    Meine LogProvider.dll habe ich um das Interface erweitert

    namespace Ideri.LogProvider
    {
        public class Reporting : IReporting
        {
          void Report(int iLevel, string strReportText)
          {...}
        }
        public interface IReporting
        {
          void Report(int iLevel, string strReportText);
        }
    }

    In meiner Anwendung habe ich folgendes implementiert

    ...
    using MyCompany.LogProvider
    
    namespace MyCurrentApp
    {
      public class MyCurrentAppForm : Form
      {
        MyCompany.LogProvider.Reporting logger;
        MyCompany.Anotherdll dll;
        public MyCurrentAppForm()
        {
          logger = new MyCompany.LogProvider.Reporting();
          logger.Report("Diese Information wird geloggt");
        
          dll=new MyCompany.Anotherdll();
          dll.Initialize(logger)
        }
      }
    }


    Und in der Dll habe ich folgendes

    Namespace MyCompany.AnotherDll
    {
      public class AnotherDllClass
      {
        MyCompany.LogProvider.Reporting _logger;
        public void Report(string strReport)
        {
          if (logger != null)
            logger.Report(strReport);
        }
        public int Initialize(MyCompany.LogProvider.Reporting logger)
        {
           ...
        }
      }
    }

    Der Compiler liefert mir aber einen Fehler er könne MyCompany.LogProvider.IReporting nicht in MCompany.LogProvider.Reporting konvertieren.

    Was mache ich falsch?

    Mittwoch, 31. Juli 2013 08:24
  • Hi Alexander,

    du scheinst der Klasse ein Interface zuordnen zu wollen.

    z.B.

    ITest bla1 = new Test(); funktioniert.

    Test bla2 = bla2; funktioniert nicht.

    MFG

    Björn

    p.s. du willst ja einen Logger schreiben. Schau mal hier und der Code dazu gibt es hier.

    Mittwoch, 31. Juli 2013 08:49
  • Nutze in deiner DLL nicht den Konkreten Logger sondern NUR das Interface und injiziere es per Konstruktor.

    Dann klappt es ;)

    Mittwoch, 31. Juli 2013 11:10
  • Hi Bjorn,

    richtig, tatsächlich kämpfe ich gerade mit einem logger. Der funktioniert aber schon. das jetzt durch log4net auszutauschen würde momentan erst mal nicht mein Problem lösen. Mir geht es darum, dass mein Logger in meiner Anwendung instanziert wird und ich gerne auch aus verschiedenen Dlls drauf zugreifen können will. Und das schaffe ich irgendwie nicht. Quasi ein Delegate an beliebige Dlls zur Verfügung zu übergeben.

    Hi Pawel,

    tut mir leid, aber ich stehe auf dem Schlauch

    1.  Namespace MyCompany.AnotherDll
    2.  {
    3.    public class AnotherDllClass
    4.    {
    5.      MyCompany.LogProvider.Reporting _logger;
    6.      public void Report(string strReport)
    7.      {
    8.        if (logger != null)
    9.          logger.Report(strReport);
    10.     }
    11.     public int
    12. Initialize(MyCompany.LogProvider.Reporting logger)
    13.     {
    14.        ...
    15.     }
    16.   }
    17. }

    Ich habe mal mein CodeFragement mit Zeilennummern versehen. Kannst du mir sagen welche Zeile falsch ist?

    Ich wollte den "Zeiger" auf die Methode bzw. auf die Instanz "logger" in Initialize übergeben. Das im Konstruktor zu machen hätte ich auch kein Problem. Aber das würde mein Problem wohl nicht lösen, oder? Kannst du mir sagen in welcher Zeile ich den "konkreten Logger" nutze und WIE ich NUR das Inteface nutzen kann/soll und dann ijiziere?

    Danke

    Gruß

    Mittwoch, 31. Juli 2013 12:54
  • Hi Alexander,

    die Idee war jetzt auch nicht den Logger zu verwenden, sondern mal zu schauen wie die es gelöst haben.

    Ich denk mal du wird ähnliche Anforderungen haben. ;)

    Zu deinem Problem. Du solltest die Basis Klasse deines Loggers Referenzieren und das Interface verwenden. Sollte bei der Verwendung etwa so aussehen.

    1.  Namespace MyCompany.AnotherDll
    2.  {
    3.    public class AnotherDllClass
    4.    {
    5.      Ideri.LogProvider.IReporting _logger;
    6.      public void Report(string strReport)
    7.      {
    8.        if (logger != null)
    9.          logger.Report(strReport);
    10.     }
    11.     public int
    12. Initialize( Ideri.LogProvider.IReporting logger)
    13.     {
    14.        ...
    15.     }
    16.   }
    17. }

    Ich hab jetzt nur den Text Kopiert und die Stellen angepasst, ob es so Funktioniert hab ich jetzt nicht probiert.

    Grundlegend würde ich aber überlegen es nicht mit DI zu machen sondern über das Factory Pattern (hier mal für einen Logger)  und den Logger dann als Static (SingelTon) zu implementieren. Das hat den Vorteil, das die Klassen nach außen keine Schnittstelle mit ILogger haben und du den Logger nicht gegebenen falls durch die ganze Anwendung durch reichen musst.

    MEF wäre auch noch eine Lösung.


    MFG

    Björn
    Mittwoch, 31. Juli 2013 14:52
  • Hi Björn,

    so langsam verzweifel ich (die neuen Ideen werde ich mir anschauen, aber zunächst will ich es einfach mal hinkriegen).

    1.  Namespace MyCompany.AnotherDll
    2.  {
    3.    public class AnotherDllClass
    4.    {
    5.      Ideri.LogProvider.IReporting _logger;
    6.      public void Report(string strReport)
    7.      {
    8.        if (logger != null)
    9.          logger.Report(strReport);
    10.     }
    11.     public int
    12. Initialize( Ideri.LogProvider.IReporting logger)
    13.     {
    14.        _logger = logger
    15.     }
    16.   }
    17. }

    Ich denke, dass müsste jetzt so eigentlich richtig sein. Mein Problem ist aber, dass der Compiler "IReporting" nicht kennt. "Reporting" hingegen kennt er!?

    Was kann denn hier überhaupt falsch laufen. In der App (Exe) kennt der Compiler beides, in der DLL kennt er nur die Klasse Reporting. Das Interface IReporting ist in der gleichen CS Datei implementiert wie Reporting. Warum ist Reporting bekannt und IReporting nicht?

    Gruß

    Alex

    Mittwoch, 31. Juli 2013 18:18
  • Hi

    hast du einen Verweis auf die Dll hinzugefügt und ein using?

    MFG

    Björn

    Mittwoch, 31. Juli 2013 18:26
  • Hi,

    ja, das habe ich. Wie gesagt, die Klasse Reporting wird ja erkannt.

    Aber IReporting, d.h. das Interface selbst nicht.

    Wie schon oben gepostet sieht das so aus.

    namespace MyCompany.LogProvider
    {
        public class Reporting : IReporting
        {
          void Report(int iLevel, string strReportText)
          {...}
        }
        public interface IReporting
        {
          void Report(int iLevel, string strReportText);
        }
    }

    In der anderen Dll kann ich auf Reporting zugreifen, auf IReporting nicht. Aus der Hauptanwendung (Exe) geht beides. Die Verweise sind sowohl in der Exe als auch in der Dll die gleichen. Das gilt auch für das Using. Hat ein Interface eine andere Sichtbarkeit oder woran könnte es liegen?

    Gruß

    Alex

    Mittwoch, 31. Juli 2013 19:00
  • Hi

    hast du Klassen die in Unterschiedlichen Namespaces gleich heißen und hast ?

    Ansonsten poste mal die ganzen Klassen samt Using und Namespace oder stell den Code Online (wenn komprimiert bitte Zip).

    MFG

    Björn

    Mittwoch, 31. Juli 2013 19:21
  • Hi Björn,

    ich habe mir eine kleine Testapp mit 2 dlls erstellt und da hat es funktioniert.

    dann habe ich einfach noch mal an meinem Code rumgemacht und irgendwann hat es getan. Vermutlich habe ich an einer Stelle den Klassennamen statt InterfaceNamen oder andersrum verwendet. Wie auch immer, weiss es nicht genau. Damit ist das Thema für mich jetzt zumindest positiv beendet.

    Ich gehe davon aus, dass man die Injection beliebig tief treiben kann.

    Wie funktioniert das den mit den "Als Antwort markieren"? kriegt ihr dafür Punkte? Eigentlich war ja bereits der erste Tipp von Pawel richtig, allerdings hätte ich es wahrscheinlich ohne weitere Unterstützung doch nicht so richtig hingekriegt, wobei der Rest quasi dann eher an mir lag...

    Gruß und Danke noch mal

    Donnerstag, 1. August 2013 13:49
  • Hi Alex,

    du kannst mehrere Antworten als Antwort markieren und/oder an der Seite Punkte verteilen wenn ein Beitrag hilfreich war. Das soll anderen Leuten dann eine Hilfestellung geben, wenn sie ein Ähnliches Problem haben.

    Mir sind Punkte ehrlich gesagt nicht wichtig und Pawels Antwort war sicher richtig.

    MFG
    Björn

     

    Donnerstag, 1. August 2013 14:01