Laden...
D
Benutzerbeschreibung

Forenbeiträge von Daedalus Ingesamt 8 Beiträge

21.06.2006 - 11:02 Uhr

Grrr... nach langem Suchen hatte ich nun keine Lust mehr und habe mir einfach ein kleines Workaround für das Problem überlegt.

War gar nicht so schwer. Mittels XmlTextWriter schreibe ich das XmlDocument Stück für Stück in die Datei. Vielleicht hilft's ja jemandem.


/// <summary>
/// Schreibt das angegebene XmlDocument in eine XML-Datei.
/// </summary>
/// <param name="xmlDoc">Das zu schreibende XmlDocument.</param>
private void SchreibeConfigDatei(XmlDocument xmlDoc)
{
    XmlTextWriter writer = new XmlTextWriter(DateiName, Encoding.UTF8);
    writer.WriteStartDocument();
    foreach (XmlNode node in xmlDoc.ChildNodes)
    {
        if (node.LocalName != "xml")
        {
            this.SchreibeXmlNode(writer, node);
        }
    }
    writer.WriteEndDocument();
    writer.Close();
}

/// <summary>
/// Fügt das übergebene Element dem übergebenen XmlTextWriter hinzu.
/// Dabei werden alle Attribute und Kindelemente (mittels Rekursion) 
/// mit ihren qualifizierten Namen ausgegeben.
/// </summary>
/// <param name="writer">XmlTextWriter, der das XmlDocument schreiben soll.</param>
/// <param name="node">Element, das dem XmlTextWriter hinzugefügt werden soll.</param>
private void SchreibeXmlNode(XmlTextWriter writer, XmlNode node)
{
    writer.WriteStartElement(node.Prefix, node.LocalName, node.NamespaceURI);
    if (node.Attributes != null)
    {
        foreach (XmlAttribute attr in node.Attributes)
        {
            writer.WriteAttributeString(attr.Prefix, attr.LocalName, attr.NamespaceURI, attr.Value.ToString());
        }
    }
    if (node.HasChildNodes)
    {
        foreach (XmlNode childNode in node.ChildNodes)
        {
            if (childNode.NodeType != XmlNodeType.Text)
            {
                this.SchreibeXmlNode(writer, childNode);
            }
            else
            {
                writer.WriteString(childNode.InnerText);
            }
        }
    }
    writer.WriteEndElement();
}

20.06.2006 - 17:39 Uhr

Hallo zusammen!

Ich versuche mich gerade an der Erstellung einer XML-Datei, die mehrere Namespaces enthält. Beim Schreiben dieser Datei sollen die Elemente und Attribute nun mit ihren voll qualifizierten Namen ausgegeben werden. Das funktioniert wie folgt leider nicht. In der geschriebenen Datei sind keine Präfixe enthalten.

XmlDocument xmlDoc = new XmlDocument();
XmlDeclaration xmlDeclaration = xmlDoc.CreateXmlDeclaration("1.0", "ISO-8859-1", null);

XmlElement rootNode = xmlDoc.CreateElement("a:root");
XmlElement test = xmlDoc.CreateElement("b:test");

xmlDoc.InsertBefore(xmlDeclaration, xmlDoc.DocumentElement);
xmlDoc.AppendChild(rootNode);
rootNode.AppendChild(test);

xmlDoc.Save(@"C:\test.xml");

Ausgabe:

<?xml version="1.0" encoding="ISO-8859-1" ?> 
<root>
<test /> 
</root>

Ich möchte:

<?xml version="1.0" encoding="ISO-8859-1" ?> 
<a:root xmlns:a="url" xmlns:b="url" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="url">
<b:test /> 
</a:root>

Das Beispiel ist recht einfach. Mein eigentlicher Code ist etwas komplexer, aber vom Ansatz her gleich. Ich habe ein XmlDocument, füge Elemente hinzu und speichere es ab. Sämtliche Elemente und Attribute (inkl. dem Root-Element) sollen mit ihrem jeweiligen Präfix versehen werden.

Habe schon längere Zeit gesucht und rumprobiert (z.B. mit dem XmlNamespaceManager etc.), aber nichts passendes gefunden. Meine Alternative wäre ein simples Schreiben Element für Element per XmlWriter, aber das halte ich nicht für praktikabel. Da muss es doch eine einfachere Lösung geben!

Danke schonmal für die Hilfe und noch nen schönen Abend beim Fußballgucken 😉
Daedalus

01.06.2006 - 21:29 Uhr

Ja, deine Methode habe ich auch in den meisten Beispielen gesehen und verwende sie jetzt auch selbst. "Meine" Methode habe ich aus dem Buch von O'Reilly: "Programmieren mit C#".

Gruß
Daedalus

31.05.2006 - 17:14 Uhr

Habe die Lösung!

Siehe hier.

So sieht's jetzt bei mir aus:


BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
IDictionary props = new Hashtable();
props["port"] = 1234;
this.chan = new TcpChannel(props, null, provider);

Ich musste also "nur" beim Erstellen des TCPChannels diesen Provider dort mitgeben, der die Eigenschaft TypeFilterLevel auf Full gesetzt hat.

31.05.2006 - 16:53 Uhr

Das gleiche Problem habe ich auch... bin schon den ganzen Nachmittag auf der Suche nach einer Lösung. Mein zu übergebendes Objekt ist als [Serializable] gekennzeichnet bzw. von MarshalByRefObject abgeleitet.

Fehlermeldungen
"by value": Auf den Typ Name.meines.Typs kann aufgrund von Sicherheitseinschränkungen nicht zugegriffen werden.
"by reference": Auf den Typ System.Runtime.Remoting.ObjRef kann aufgrund von Sicherheitseinschränkungen nicht zugegriffen werden.

HILFE! 🙂

Grüße
Deadalus

31.05.2006 - 10:32 Uhr

Mhhh... die Seite mit dem Video kannte ich schon, aber beim zweiten Lesen ist mir dann doch vielleicht die Lösung aufgefallen 😉

Muss ich die Sache vielleicht wie folgt lösen?

Ausschnitt aus "Client.cs":


this.chan = new TcpChannel(0);
ChannelServices.RegisterChannel(this.chan);

object host = RemotingServices.Connect(typeof(IHost), "tcp://adresse/Host");
this.host = obj as IHost;
this.IstVerbunden = this.host.TesteVerbindung();

object berechnung1 = RemotingServices.Connect(typeof(IBerechnung1), "tcp://adresse/Berechnung1");
this.berechnung1 = obj as IBerechnung1;

Dann hätte ich einfach mehrere Remote-Objekte lokal verfügbar und könnte sie über die Interfaces ansprechen.

Hatte ich die ganze Zeit über ein (nicht gerade kleines) Brett vor'm Kopf und es ist tatsächlich so einfach? 🙂

Grüße
Daedalus

31.05.2006 - 10:26 Uhr

Moin!

Erstmal danke für die umfangreiche Antwort.

Zu meiner zweiten Frage: Das habe ich mir schon fast gedacht. Bei meinen Versuchen war es nämlich genau so: Die Formulare wurden auf dem Host angezeigt... naja, egal 🙂

Zur ersten Frage: Ich habe mich wohl ein wenig umständlich ausgedrückt. Im Prinzip kann man in den Ausführungen "Server" durch "Host" ersetzen 🙂

Ich habe mal zur besseren Anschaulichkeit ein kleines Diagramm meiner Anwendung angefügt:

Ablauf der Kommunikation:*Anwender klickt Button "Verbinden" in formClient *formClient.btnVerbindeClick wird ausgeführt *formClient.client wird als Client instantiiert und verbindeMitServer aufgerufen. Hier wird nun der Channel aufgebaut und das Host-Objekt instantiiert, das über formClient.client.host angesprochen werden kann. *Das erfolgreiche Aufbauen der Verbindung wird über TesteVerbindung
geprüft, und das funktioniert auch.

Ausschnitt aus "Client.cs": VerbindeMitServer um die Verbindung aufzubauen:


this.chan = new TcpChannel(0);
ChannelServices.RegisterChannel(this.chan);
object obj = RemotingServices.Connect(typeof(IHost), "tcp://adresse");
this.host = obj as IHost;
this.IstVerbunden = this.host.TesteVerbindung();

Wie stelle ich dem Client aber nun die Methoden BerechneWas1 zur Verfügung, wenn der Anwender auf den Button "BerechneWas1" klickt? Also wie "verknüpfe" ich die Interfaces auf dem Client mit den tatsächlichen Objekten auf dem Host?

Ich habe doch auch dem Client nur Zugriff auf das Host-Objekt!? Muss ich also...*... das Host-Interface und auch die entsprechende Klasse mit Methoden versehen wie BerechneWas1, die dann die entsprechenden Methoden in den anderen Klassen aufrufen und deren Ergebnisse zurückgeben? *... komplette Objekte übergeben, z.B. mit einer Methode GetBerechnung1 auf dem Host?

Im ersten Fall sähe meine Methode btnBerechneWas1Click in formClient vereinfacht wie folgt aus:


double ergebnis = this.client.host.BerechneWas1();

Und das finde ich seltsam wegen dem Zugriff auf client.host und auch, weil die Interfaces für die Berechnungsklassen eigentlich überflüssig wären.

Beim zweiten Ansatz bekomme ich Probleme beim "Übergeben" der Objekte, wie du auch schon gesagt hast.

Also irgendwie habe ich das Ganze wohl noch nicht so richtig verstanden... 🙂

Das mit dem Lernvideo habe ich gerade erst gesehen. Schaue ich mir mal eben an...

Grüße
Deadalus

30.05.2006 - 16:22 Uhr

Moin zusammen!

Ich habe da mal eine Frage zum grundlegenden Design einer Remoting-Anwendung.

Ich versuche mich an einer Client-/Server-Anwendung, die bislang grob den folgenden Aufbau hat:*Klasse formClient: GUI des Clients (behandelt z.B. die Eingaben des Benutzers und übergibt diese der Klasse Client) *Klasse formServer: GUI des Servers (verschiedene Server-Einstellungen wie z.B. Portnr. etc.) *Klasse Client: Logik des Clients ("schickt" u.a. die Eingaben an den Server) *Klasse Server: Logik des Servers (Erstellen des TCPChannels etc.) *Klassen mit Geschäftslogik (verschiedene Berechnungen) *Interface IServer

Der Server soll dem Client nun die Funktionen aus den Geschäftslogik-Klassen zur Verfügung stellen. Dazu habe ich nun einige Tutorials, Hilfen etc. gelesen (z.B. auch in diesem Forum) in denen empfohlen wird, auf Client-Seite nur mit Interfaces zu arbeiten, damit bei internen Änderungen auf der Server-Seite der Client nicht upgedatet werden muss.

Meine Frage ist nun, wie ich das bei mehreren Geschäftslogik-Klassen realisieren sollte. Wenn ich nun z.B. die Klasse Berechnung habe, deren Methode BerechneEtwas() ich dem Client zur Verfügung stellen will, wie würde man das am "saubersten" implementieren? *Nur den Server marshallen und hier eine Methode BerechneEtwas() anlegen, die einfach nur die Methode in Klasse Berechnung aufruft und deren Ergebnis zurückgibt. Diese müsste dann auch im Interface IServer angelegt werden. Das hätte zur Folge, dass in der Klasse Server hauptsächlich Methoden enthalten wären, die semantisch hier gar nicht hingehören. *Nur den Server marshallen und für jede gwünschte Klasse eine Methode anlegen, die ein Objekt dieser Klasse zurückgibt, mit dem der Client weiterarbeiten kann. Dazu muss jede Klasse serialisierbar und dem Client über ein Interface bekannt sein. *Jede Geschäftslogik-Klasse einzeln marshallen und entsprechend mehrere Interfaces bereitstellen. Hierzu bräuchte man (meines Wissens nach) mehrere Channel, was ich eigentlich nicht will.

Des Weiteren habe ich noch eine Frage zum Aufruf der Methoden in den Formularen. Wenn ich nun bspw. nach dem Klicken auf einen Button im Client-Formular dessen Daten an den Server zum Verarbeiten senden will, habe ich in Klasse formClient folgende zwei Möglichkeiten:*Im Formular formClient referenziere ich ein Client-Objekt, das wiederum ein Server-Objekt referenziert, und rufe dessen Methode mit den Parametern auf: this.client.server.TuWas(Parameter). Dabei greife ich auf das Attribut server zu, was nicht so sauberer Stil ist. *Im Formular formClient rufe ich die Methode TuWas(Parameter) des Cliens auf. Diese Methode ruft die Methode TuWas(Parameter) des Servers auf. Hierbei entsteht eine Verkettung von Methodenaufrufen, was ich auch irgendwie sehr komisch finde.

Bei beiden Fällen wäre dann noch zu klären, ob letztlich überhaupt der Server die Methode TuWas() anbietet oder diese an die Geschäftslogik weiterreicht (siehe erste Frage).

Ich hoffe, ich konnte mein Problem einigermaßen verständlich machen. Das Remoting an sich (per TCPChannel etc.) klappt einwandfrei, aber die logische und semantisch korrekte Abbildung der Methoden in den entsprechenden Klassen ist im Moment mein Hauptproblem. Ich hätte gerne einen schönen, sauberen Code 🙂

Oh, und wenn noch Zeit ist: Ist es möglich, Formulare auf dem Server zu implementieren, die dann auf dem Client angezeigt werden? Also quasi ein Marshaling von Formularen, die auf dem Server geändert werden können, aber auf dem Client (z.B. mit Hilfe von Interfaces) nur angezeigt werden, ohne dass dieser den Code kennen muss. Ich denke da an einen Client, der nur einige Menüeinträge umfasst, beim Klick auf diese die entsprechenden Formulare jedoch vom Server lädt und anzeigt.

Danke schonmal im Voraus an alle, die sich hierfür Zeit nehmen! 🙂

Gruß
Daedalus