Laden...

Plugin soll Features der Host-Anwendung nutzen, z.B. Datenbankzugriff

Erstellt von WPF_Noob vor 10 Jahren Letzter Beitrag vor 10 Jahren 6.681 Views
Hinweis von herbivore vor 10 Jahren

Fürs nächste mal: Bitte immer gleich damit rausrücken, was du eigentlich erreichen willst, dann wäre der Thread nicht so unnötig lang geworden, bis es mal auf den Punkt kommt. Die ersten acht Beiträge (inkl. des Startbeitrag) können übersprungen werden. Richtig los geht es im neunten Beitrag.

W
WPF_Noob Themenstarter:in
49 Beiträge seit 2011
vor 10 Jahren
Plugin soll Features der Host-Anwendung nutzen, z.B. Datenbankzugriff

Hallo,

ist stehe aktuell vor folgendem Problem.
Ich habe zwei Klassen, welche Routinen beschreiben, um bestimmte Vorgänge durch zu führen. Dabei werden auch Events ausgelöst. Beide Klassen sind voneinander unabhänig und sollen als Basis weiterer Klassen dienen.

Jetzt habe ich Klassen (nachfolgend mal Klasse3 genannt), welche von diesen beiden ersten Klassen erben sollen. Dabei kann aber jede erdenkliche Konstellation vorkommen.

Beispiele:
class Klasse3: Klasse1{...}
class Klasse3: Klasse2{...}
class Klasse3: Klasse1, Klasse2{...}

Bei den ersten beiden Fällen habe ich kein Problem, das stellt ja den Standart da. Aber beim 3. Fall wo von beiden Klassen geerbt werden soll, bekomme ich ja Probleme, da C# solche Fälle nicht unterstützt.

Im Netz habe ich folgenden Lösungsansatz gefunden:
Simulated Multiple Inheritance Pattern for C#
Nun wollte ich Nachfragen, ob das der beste Weg ist das Problem an zu gehen, oder ob es da noch bessere Lösungen gibt?

Eine Idee von mir war es die Methoden aus den Klassen 1 und 2 als static zu deklarieren, aber dann sind meine Events nicht sauber umgesetzt.

Es wäre schön, wenn jemand meine oben aufgeführten Ansätze kurz aus fachlicher Sicht bewerten könnte. Oder mir einen ganz anderen Anzatz vorschlägt.

Danke für eure Hilfe.
mfG

4.221 Beiträge seit 2005
vor 10 Jahren

Verwende Interfaces

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

2.298 Beiträge seit 2010
vor 10 Jahren

Hallo,

die Lösung aus dem Artikel geht schon fast in die richtige Richtung. Du solltest allerdings immer vor Augen behalten, dass die Mehrfachvererbung begründet nicht möglich ist. Insofern solltest du nichts daher frickeln.

In der Praxis gibt es keine Objekte die von 2 verschiedenen Objekttypen erben.

Sollen die Methoden bereits in den Basisklassen fertig ausprogrammiert werden? Ansonsten empfehle ich dir beide Basisklassen durch Interfaces zu ersetzen.

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

P
660 Beiträge seit 2008
vor 10 Jahren

hallo,

ich werde gleich bestimmt geschlagen, aber lasse doch klasse 2 von klasse 1 erben und klasse 3 von klasse 2

also folgendes:

public class Klasse1 {}

public class Klasse2 : Klasse1 {}

public class Klasse3 : Klasse2 {}

MfG
ProGamer*Der Sinn Des Lebens Ist Es, Den Sinn Des Lebens Zu Finden! *"Wenn Unrecht zu Recht wird dann wird Widerstand zur Pflicht." *"Ignorance simplifies ANY problem." *"Stoppt die Piraterie der Musikindustrie"

W
WPF_Noob Themenstarter:in
49 Beiträge seit 2011
vor 10 Jahren

Hallo,

danke für eure Anmerkungen.

@inflames2k:
Ja die Methoden in den Basisklassen sind in sich geschlossen fertig programmiert, so das nur die Methode aufgerufen werden soll und fertig. Die geerbte Klasse (Klasse3) soll sich um die Geschehnisse in den Basisklassen nich kümmern müssen.

So könnte ein Beispiel auf dem realen Leben sein.
Ziel soll ein Mensch mit bestimmten Fähigkeiten sein. Somit wird alles biologische zum Menschen in der Klasse Menschen definiert.

class Person:Mensch{}

Dann soll die Person das Talent zum Fußballspielen bekommen (außer acht gelassen, das man als Fußballer auch biologische Aspekte wie Füße braucht)

class Person:Mensch, Fußballer {}

Oder dann soll er autofahren können

class Person:Mensch, Autofahrer {}

oder auch alles zusammen

class Person:Mensch, Fußballer, Autofahrer {}

Ich denke dieses Beispiel zeigt worauf ich hinaus will. In den Basisklassen versteckt sich ausformuliert alles, was man für eine Tätigkeit braucht (Baukastenprinzip)

Der Vorschalg von ProGamer würde sehr viel Overhead bedeuten, da ich für jede Konstellation eine zusammenfassende Klasse mit Eventrouting bräuchte. Das ist mir aber zu unübersichtlich.

656 Beiträge seit 2008
vor 10 Jahren

Schreit aus meiner Sicht eigentlich nach Interfaces, und darin vermutlich Komposition.
Kommt aber drauf an, wofür du das ganze verwenden willst.

Wenn du einen Fußballer hast, dann kommt der vermutlich nur in eine Klasse Stadion rein, die mit Fußballern arbeitet (und nicht mit Personen oder Menschen - bzw. eher mit etwas, dass "Fußball spielen kann" - ICanPlaySoccer). Genauso wird eine Auto-Klasse nur einen Autofahrer (ICanDriveACar oder IGotADrivingLicense) als Fahrer akzeptieren, und nicht jeden x-beliebigen Menschen.

Von demher: Interface; und wenn sich Sachen tatsächlich wiederholen/immer gleich sind: Komposition. Oder, dem Design nach sollte die Logik gar nicht in der Person (bzw. in der Komposition) sein, sondern in der Zielklasse (Stadion, Auto, etc) oder zumindest eher dort in der Nähe als bei der Person.

709 Beiträge seit 2008
vor 10 Jahren

Hallo,
hier kommt es auf den einzelnen Fall an, finde ich.

Da ein Fußballer auch ein Mensch ist, sollte man ihn auch davon ableiten.
Ebenso beim Autofahrer.

Wenn es sich um Fähigkeiten handelt, könnte man ein Interface oder eine Basisklasse für eine solche Fähigkeit erstellen und dann entsprechend eine Collection damit füllen. Deren Inhalt wird dann halt innerhalb der Klasse, die die Collection beinhaltet, ausgewertet und behandelt.

Gruß
pinki

2.298 Beiträge seit 2010
vor 10 Jahren

Hallo WPF_Noob,

die korrekte Vererbungshirarchie in deinem Beispiel wäre aus meiner Sicht:

Person : Mensch
Fußballer : Person
Autofahrer : Person

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

W
WPF_Noob Themenstarter:in
49 Beiträge seit 2011
vor 10 Jahren

Ja nach dem Kommentar von inflames2k muss ich gestehen, das mein Beispiel vielleicht doch nicht so toll war. Dann versuche ich es noch mal mit dem was wirklich passieren soll.

Ich habe eine Klasse, nennen wir sie Plugin1. Dieses Plugin soll mit der Hauptanwendung kommunizieren. Dies habe ich erfolgreich mittels Interface und Events realisiert. Jetzt soll das Plugin in der Lage sein, bestimmte Aufgaben durch zu führen.
Zum Beispiel soll das Plugin Daten einer Datenbank abfragen können. Da ich dem Plugin nicht direkt gestatten möchte, selber eine Datenbankverbindung auf zu bauen, habe ich eine Klasse geschrieben, welche alles mit bringt, um über die Hauptapplikation mit der Datenbank zu kommunizieren. Der Aufwand um die Kommunikation stabiel zwischen Plugin und Hauptanwendung aufzubauen (unteranderem mittels Events), ist recht umfangreich. So war mein Ansatz das Plugin von der DBHelper-Class das Wissen über den Vorgang erben zu lassen. So muss ich im Plugin lediglich die Funktion zum DB-Abruf starten und bekomme meine Daten.

Dieses Vorgehen brauche ich jetzt aber auch noch für zwei andere Fälle, wo das Plugin über die Hauptanwendung Daten/Informationen bestimmen soll.

Es kann natürlich auch sein, das mein Ansatz komplett Falsche ist. Wenn jemand dieser Meinung sein sollte, schreibt es mir bitte ich bin für jeden neuen Vorschlag offen.

S
417 Beiträge seit 2008
vor 10 Jahren

Hallo,

das ist eher ein Fall für Komposition, als für Vererbung: Composition over inheritance

Sprich: Anstelle das Plugin von der Helper-Klasse abzuleiten, sorge lieber dafür dass das Plugin eine Instanz der Helfer-Klasse übergeben bekommt.

W
WPF_Noob Themenstarter:in
49 Beiträge seit 2011
vor 10 Jahren

Wenn ich das jetzt im Netz richtig gelesen habe, dann muss ich aber bei dieser Methode dafür sorgen, das meine Zielkasse alle notwendigen Events implementiert hat und an die Helper-Klasse weiter reicht??????

S
417 Beiträge seit 2008
vor 10 Jahren

Welche Events meinst du denn, bzw. was meinst du mit weiterreichen der Events?

W
WPF_Noob Themenstarter:in
49 Beiträge seit 2011
vor 10 Jahren

Im Beispiel mit der Datenbank hat die DB-Helper Klasse ein Event definiert, welches ausgelöst wird, sobald die Hauptapplikation zwischen dem Plugin und der Datenbank Daten vermittel soll.

Es geht dann folgendes im Datenablauf vor:
1.das Plugin will das Ergebnis der Anweisung "SELECT xy" von der Datenbank haben 1.das Plugin ruft mit Übergabe des SQL-String die Methode aus der DB-Helper Klasse auf, welche die Kommunikation startet 1.Es wird in der DB-Helper Klasse ein Datenaustausch-Item erstellt wo alle wichtigen Daten drin sind. 1.die DB-Helper Klasse löst ein Event aus -> "ich will Daten von der Datenbank" 1.die DB-Helper Klasse wartet jetzt darauf, das die Daten im Datenaustauschitem gefüllt sind und DB-Abfrage fertig ist 1.Die Hauptanwendung führt die SQL-Anweisung aus und füllt das Datenaustauschitem 1.Die DB-Helper Klasse reicht das Ergebnis an das Plugin weiter

Diese Vorgehensweise habe ich im Ansatz irgendwo auf CodeProject gefunden.

16.830 Beiträge seit 2008
vor 10 Jahren

Ich würde dem Plugin einfach ein abgespecktes DB-Repository zur Verfügung stellen, das die Hauptanwendung initialisiert.
Das Repository hat Events, damit die Hauptanwendung weiß, dass mit dem Repository derzeit gearbeitet wird (einfach als GUI Ausgabe), sowas wie
OnQueryStarted
OnQueryFinished

Das Plugin spricht dann direkt mit diesem Repository, statt irgendwelche SELECT Strings rumzuwerfen. Dem Plugin muss wie der gesamten Anwendung völlig egal sein, was für eine DB unter der Anwendung liegt, und wie mit ihr gesprochen wird. Das Repository ist das wichtige.
Wenn das Repository dann mehrere Plugins zeitgleich nutzen sollen, zB um Caching zu realisieren etc, dann musst Du Dich eben noch um die Synchronisation kümmern.

5.658 Beiträge seit 2006
vor 10 Jahren

Hi WPF_Noob,

du solltest deine Schichten besser trennen. SQL-Abfragen sollten nur in der Datanzugriffsschicht vorhanden sein, und ansonsten mit Schnittstellen gearbeitet werden. Das Plugin sollte demnach eine **Methode **des Hauptprogramms aufrufen (ohne SQL-Befahle zu übergeben), um die Daten abzufragen. Das Hauptprogramm reicht die Anfrage an die DAL weiter. Dann sollte das Plugin per **Event **über das Ergebnis benachrichtigt werden. Siehe zu den Plugins auch [FAQ] Eigene Anwendung pluginfähig machen.

Christian

PS: Was mich an deiner Beschreibung am meisten irritiert, ist, daß du ein Event auslöst, um die Daten zu holen, und nicht, um über den Erfolg benachrichtigt zu werden.

Hinweis von herbivore vor 10 Jahren

Das Plugin sollte das Hauptprogramm nicht kennen. Das Hauptprogramm kann und wird das Plugin kennen.

Aber im Sinne der angesprochenen Schichtentrennung kann es natürlich eine Schicht geben, die vom Programmierer des Hauptprogramms erstellt wurde, die unterhalb der Plugins liegt und deshalb von diesen benutzt werden darf.

Events benötigt man nur in der Richtung von bekannt (unten) zu unbekannt (oben). Für die Richtung unbekannt (oben) zu bekannt (unten), kann man einfach eine Methode aufrufen.

Weeks of programming can save you hours of planning

2.298 Beiträge seit 2010
vor 10 Jahren

@MrSparkle

Der richtige Ansatz ist doch aber, dass das Plugin die Hauptanwendung nicht kennt. Insofern ist doch der Ansatz von Abt der bessere.

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

16.830 Beiträge seit 2008
vor 10 Jahren

Naja im Endeffekt kennt das Plugin nur ein Interface.
Ob das jetzt direkt ein DB-Interface eines abgespeckten Repositories ist oder ein Interface, dass ein paar mehr Dinge kann (wie in MrSparkles Fall), ist egal und manchmal auch einfach nur Geschmackssache.
Ich geb lieber 2-3 verschiedene Interfaces vor, sodass nicht jedes Plugin zwangsläufig die DB kennt, wenn sie sowieso nicht gebraucht wird.

W
WPF_Noob Themenstarter:in
49 Beiträge seit 2011
vor 10 Jahren

Die Plugins habe ich nach folgendem Vorbild entwickelt Plug-ins in C#

@Abt:
da ich mir deinen Ansatz noch nicht ganz vorstellen kann, hast du da zufällig ein Online-Artikel zur Hand, wo ich mich zur Thematik mal belesen kann?

ps: als Datenquelle kommen je nach Aufgabe nicht nur Datenbanken zum Einsatz. Es kann auch eine Berechnung aus der Main-Applikation oder ähnliches sein.

16.830 Beiträge seit 2008
vor 10 Jahren

Der wurde Dir mit [FAQ] Eigene Anwendung pluginfähig machen schon gegeben. MEF macht das so (und viele andere Ansätze ebenfalls).

W
WPF_Noob Themenstarter:in
49 Beiträge seit 2011
vor 10 Jahren

Hallo Abt,

ich habe versucht mich seit gestern Nachmittag mit dem Thema Repositories zu beschäftigen um mir einen ersten groben Überblick zu verschaffen (mich im Detail damit zu beschäftigen wird jetzt in den nächsten Tagen folgen). Mir fehlt jetzt allerdings das Verständnis, wie das Plugin die Anfrage an die Hauptanwendung weiterleiten kann, wenn das Plugin die Hauptanwendungn nicht kennt. Wäre bitte einer so nett, mir diesen Punkt vorab schon mal zu erklären?

Danke

742 Beiträge seit 2005
vor 10 Jahren

Du hast im prinzip 3 DLLs:

  1. Shared/Common - wie auch immer du das nennen willst.
    Hier liegen nur Interfaces von Komponenten, die das Plugin verwenden darf. Dazu Typen für die Plugin-Entwiclung und Datenbank-Objekte.

public interface IProductRepository
{
   List<Product> QueryAll();
}

public class Product {}

public interface IPlugin
{
   void Setup(IPluginContext context);
}

public interface IPluginContext
{
    T GetService<T>();
}

  1. Die Hauptanwendung:
    In der Hauptanwendung wird das Product-Interface implementiert, das kann natürlich auch in eine extra Assembly ausgelagert werden. Außerdem gibt es hier eine Implementierung des IPluginContext-Interfaces. das alle implementierten Komponenten/Repositories zurückliefern kann auf die das Plugin mit dem jeweiligen Interface zugreifen kann.

  2. Das Plugin
    Kennt nur die Interfaces und holt sich über GetService eine Komponente, z.B. das Repository und kann hier ganze normale Methoden ausführen. Events etc. sind im Prinzip gar nicht nötig.

Zu Repositories: Repositories sind super, wenn du alle Tabellen kennst. Soll ein Plugin neue Tabellen selbstständig hinzufügen können (was wohl in 95% der CMS-Lösungen z.B. so ist) musst du dir eben eine andere Komponente überlegen, das kann aber auch kombiniert werden indem z.B. teilweise Repositories bereitgestellt werden oder auch ein IDbConnectionManager für flexibleren Zugriff auf die Datenbank.

W
WPF_Noob Themenstarter:in
49 Beiträge seit 2011
vor 10 Jahren

Hallo malignate,

ich habe mir deine Ausführungen jetzt immer wieder durch den Kopf gehen lassen und hänge an dem Punkt, wo das Plugin über GetService auf die Hauptanwendung zugreifen soll. In meinem Verständnis muss doch das Plugin die Instanz der Hauptanwendung kennen, damit es über das Interface auf GetService zugreifen kann.
Muss ich an dieser Stelle dem Plugin die Eigenschaft "Parent" implementieren und dann bei der Plugininitalisierung Parent mit "this" füllen?

16.830 Beiträge seit 2008
vor 10 Jahren

wo das Plugin über GetService auf die Hauptanwendung zugreifen soll.

Diesen Gedanken musst Du aus Deinem Kopf kriegen.

Das Plugin kennt nur das, was man ihm gibt. Und Du bestimmst das, was Du ihm gibst: und das ist so abstrakt und wenig wie auch nur möglich.
Du stellst dem Plugin ein Interface zur Verfügung gegen das es Arbeiten kann. Was dahinter steckt ist völlig egal.

Wo genau hängst Du jetzt?

W
WPF_Noob Themenstarter:in
49 Beiträge seit 2011
vor 10 Jahren

Naja in meinen Vorstellungen müsste das jetzt so aussehen, aber ich denke das ist nicht korrekt ...

public interface IProductRepository
{
   List<Product> QueryAll();
}

public class Product {}

public interface IPlugin
{
   void Setup(IPluginContext context);
}

public interface IPluginContext
{
    T GetService<T>();
}

public class Plugin1:IPlugin
{
  private IPluginContext _context;
  public void Setup(IPluginContext context)
  {
    // init
    this._context = context;
  }
  
  public void machwas()
  {
    string restult = this._context.GetService<string>();
  }
}


public class Hauptprogramm: IProductRepository, IPluginContext
{

  private List<IPlugin> _pList;
  
  public Hauptprogramm(){ 
    this._pList = new List<IPlugin>();
    this._pList.Add(new Plugin1());
    this._pList[0].Setup(this);
  }
  
  public List<Product> QueryAll()
  {
    ...
  }
  
  public T GetService<T>()
  {
    ...
  }
}
16.830 Beiträge seit 2008
vor 10 Jahren

Nimm Dir doch bitte die Zeit und schaut Dir MEF genau an. Sonst wird das nichts.
Beispiel: MEF Simple Application in C# oder Simple MEF Application for Beginners

49.485 Beiträge seit 2005
vor 10 Jahren

Hallo WPF_Noob,

das Plugin kennt die Hauptanwendung nicht. Punkt. Aber es kann natürlich Objekte kenne, die in einer extra Schicht ganz unten implementiert sind und die es vom Hauptprogramm übergeben bekommt. Hauptprogramm und Plugin kennen dann beide die ganz unten liegende Schicht, welche natürlich vom gleichen Programmierer geschrieben wurde wie das Hauptprogramm.

Das ist nun wirklich nicht so schwer zu verstehen. Falls doch, dann beachte vor weiteren Nachfragen bitte unbedingt [Hinweis] Wie poste ich richtig? Punkt 1.1.1.

herbivore

W
WPF_Noob Themenstarter:in
49 Beiträge seit 2011
vor 10 Jahren

Hallo,

ich habe mir die Beispiele von Abt nun angeschaut und im Debuggingmode auch ausprobiert. Da mir aber immer noch nicht einleuchtet, wie ich das auf mein Datenmodel anwenden kann, muss ich mich erst noch viel mehr damit beschäftigen. Denn egal wie ich es drehe und wende, ich finde mich und mein Problem da nicht wieder.

Somit werde ich nach dem Verweis von herbivore auf Punkt 1.1.1 des [Hinweis] Wie poste ich richtig? diesen Thread schließen, denn ich glaube wir drehen uns im Kreis. (Nicht zuletzt deswegen, weil ich gedanklich immer wieder auf meine ursprüngliche Vorstelle zurück komme)

Ich möchte mich bei allen bedanken, die mir mit Ihrem Wissen versucht haben mich weiter zu brigen. Ich werden mein Konzept komplett in die Tonne werfen und mit euren Ansätzen neu anfangen.
**
Danke**