Laden...

WCF - Komplexe Klasse übertragen

Erstellt von mipa_acc vor 8 Jahren Letzter Beitrag vor 8 Jahren 4.832 Views
M
mipa_acc Themenstarter:in
318 Beiträge seit 2006
vor 8 Jahren
WCF - Komplexe Klasse übertragen

Hallo Community,

anscheinend habe ich einen grundlegenden Denkfehler in meinem Projekt. Denn nirgends finde ich jemanden, der eine ähnliche Problematik hat. In Fachbüchern wie z.B. "Verteilte Systeme und Services mit .NET 4.5" habe ich diesbezüglich auch nichts gefunden.

Foldender Aufbau:
Der Aufbau meines Projekts befindet sich im Anhang.

In meinem WcfService habe ich unter anderem folgenden OperationContract:


    ...
    namespace HomeCom.Server {
    ...

    [ServiceContract(Namespace = "http://HomeCom.Server.WCFService")]
    public interface IWCFService {

        [OperationContract]
        List<AvailablePluginLight> GetAllPlugins();

        [OperationContract]
       ...
    }

Das AvailablePluginLight hat under anderem folgenden Datamember:


        [DataMember]
        public PluginLevel Level {
            get {
                return _Level;
            }
            set {
                _Level = value;
            }
        }

Die PluginLevel - Klasse sieht so aus. Sie befindet sich in einem anderen Namespace, als die AvailablePluginLight - Klasse, oder der WcfService ist:


...
namespace HomeCom.Server.PluginInterface {
    [Serializable]
    [DataContract(Namespace="http://HomeCom.Server.WCFService")]
    public class PluginLevel {
        [DataMember]
        private string _Layer = null;

        [DataMember]
        private string _Underlayer = null;

        [DataMember]
        private bool _IsUsable = false;

        [DataMember]
        public bool IsUsable { ....   }

        [DataMember]
        public string Layer { ....   }
        }

        [DataMember]
        public string UnderLayer { ....   }

        public PluginLevel() {        }

    }
}


Im Klartext, mein AvailablePluginLight besitzt als Propertie eine eigene erstellte Klasse. Diese liegt allerdings in einem anderen Projekt, also auch in einem anderen Namespace.
Wenn ich meinen WcfService Debugge und anschließend die GetAllPlugins() - Methode aufrufe, erhalte ich einen Fehler, da der Dienst nicht mehr zur Verfügung steht (er ist abgeschmiert).

Folgende Tatsachen habe ich heraus gefunden:
Wenn ich in meiner AvailablePluginLight - Klasse das PluginLevel als Propertie entferne, funktioniert alles wie es soll. Meines erachtens liegt es daran, dass sich die Klasse eben in einem anderen Namespace befindet. Natürlich ist dieser in meinem WcfService als Verweis eingefügt (sonst könnte ich ja nicht mal kompilieren), aber zur Laufzeit, sprich genau dann, wenn ihn meine AvailablePluginLight - Klasse "returnt", stürzt mir mein WcfService ab.

Meine Frage:
Wie schaffe ich es, meinem WcfService während der Laufzeit, die PluginLevel - Klasse "verwendbar" zu machen? Habe es - wie ihr seht - bereits mit den Namespace-Attribut probiert, leider vergebens. Habe ich irgendwo einen Denkfehler?

Vielen Dank erstmal, dass Ihr euch das alles durchgelesen habt.

Gruß
mipa_acc

2.298 Beiträge seit 2010
vor 8 Jahren

Hallo,

versuche zu aller erst einmal die Ursache des Absturzes heraus zu finden. - Die ausgelöste Exception könnte schon einiges erklären.

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

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

502 Beiträge seit 2004
vor 8 Jahren

Wenn alle beteiligten Seiten (sprich: Server & Client) die Klassen kennen (sprich: eine Referenz auf die Assembly besitzen), dann sollte das problemlos gehen - immer vorausgesetzt, dass die einzelnen Properties/Klassen allesamt serialisierbar sind. Und ich vermute mal, dass hier das Problem liegt. Da Du aber nichts über den Fehler sagst, ist das nur geraten...

-> Welchen Fehler genau bekommst Du denn (und hier ist v.a. auch die "Server-Seite" interessant)

P.S. Sehr hilfreich bei der Fehlersuche kann hierbei das Aktivieren des Tracing und anschließende Auswerten der Logfiles mit dem Service Trace Viewer (aus dem SDK, heißt im deutschen Startmenü "Viewer für Dienstabläufe" glaub ich)sein.
Außerdem kann es helfen, die Exceptions via FaultContract auch mit zu übertragen. Das macht es am Client relativ leicht, Fehler auf Serverseite zumindest eingrenzen zu können.

Bart Simpson

Praxis ist wenn alles funktioniert und keiner weiss warum.
Theorie ist wenn man alles weiss, aber nichts funktioniert.

Bei uns wird Theorie und Praxis vereint: Nichts funktioniert und keiner weiss warum...

16.807 Beiträge seit 2008
vor 8 Jahren

DataMember soll man auf Properties ansetzen; nicht auf Fields - vor allem nicht auf private Fields.

Bzgl. des Fehlers: zeig uns die genaue Fehlermeldung.
[Hinweis] Wie poste ich richtig? Punkt 5 😃

3.170 Beiträge seit 2006
vor 8 Jahren

Hallo,

DataMember soll man auf Properties ansetzen

Ist ja zusätzlich auch noch der Fall - sieht jedenfalls so aus als würden hier sowohl die Properties als auch ihre Backingfields attributiert. Das ist definitiv nicht simmvoll und ich könnte mir vorstellen, dass das Probleme macht.

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

M
mipa_acc Themenstarter:in
318 Beiträge seit 2006
vor 8 Jahren

Hallo,

erstmal sorry, dass ich die Fehlermeldung nicht genauer spezifiziert habe. Das liegt allerdings daran, dass ich euch auch nicht genau sagen kann, was nicht past. Ich sehe nur, dass der Server beendet wurde. Folgende Meldung erhalte ich:

Fehlermeldung:
Fehler beim Empfangen der HTTP-Antwort für
>
. Die Ursache kann sein, dass die Dienstendpunktbindung kein HTTP-Protokoll verwendet. Eine andere mögliche Ursache ist, dass der HTTP-Anforderungskontext vom Server abgebrochen wird (vermutlich auf das Herunterfahren des Diensts zurückzuführen). Weitere Informationen finden Sie in den Serverprotokollen.

Server stack trace:
bei System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)
bei System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
bei System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
bei System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
bei System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
bei System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
bei System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Exception rethrown at [0]:
bei System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
bei System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
bei IWCFService.GetAllPlugins()
bei WCFServiceClient.GetAllPlugins()

Inner Exception:
Die zugrunde liegende Verbindung wurde geschlossen: Unbekannter Fehler beim Empfangen..
bei System.Net.HttpWebRequest.GetResponse()
bei System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)

Inner Exception:
Von der Übertragungsverbindung können keine Daten gelesen werden: Eine vorhandene Verbindung wurde vom Remotehost geschlossen.
bei System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
bei System.Net.PooledStream.Read(Byte[] buffer, Int32 offset, Int32 size)
bei System.Net.Connection.SyncRead(HttpWebRequest request, Boolean userRetrievedStream, Boolean probeRead)

Inner Exception:
Eine vorhandene Verbindung wurde vom Remotehost geschlossen
bei System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
bei System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)

Leider weiß ich nicht, wie ich an bessere Fehlermeldungen kommt. Ich erhalte obige Meldung an dem Punkt im Code, an dem ich im AvailablePluginLight - Objekt das PluginLevel-Objekt im Getter zurück gebe, also bei:

Dass irgendwas mit der Verbindung nicht passt schließe ich aus, da ich ja - wenn ich keine Plugins in der AvalablePluginLight - Klasse verwende, alles klappt.


   [DataMember]
        public PluginLevel Level {
            get {
                 return _Level;  //<-- Erzeugt die obige Meldung
            }
            ...

Frage:
Gibt es eine Möglichkeit, wie ich an genauere Fehlermeldungen komme?

Vielen Dank für euer bemühen!

F
10.010 Beiträge seit 2004
vor 8 Jahren

Mach erstmal die DataMember weg wie Abt beschreiben hat, und dann schau nochmal weiter.

Denn das hat weder etwas mit Namespaces noch damit zu tun das das in anderen Assemblies ist.

M
mipa_acc Themenstarter:in
318 Beiträge seit 2006
vor 8 Jahren

Ist erledigt. Exakt gleiches Verhalten + Fehler.meldung Das hatte ich vor meinem Post bereits getestet, allerdings nicht rein geschrieben. Sorry.

Ich habe auch schonmal getestet, die Namespaces in den Attributen weg zu lassen - auch gleiches Verhalten + Fehlermeldung.

AvailablePluginLight + PluginLevel haben nun nur noch bei den Properties das DataMember Attribut. Keine der verwendeten klassen hat mehr eine Angabe des Namespace im ServiceContract. Also verwenden alle tempuri.org (habe ich in dem Buch gelesen).

2.298 Beiträge seit 2010
vor 8 Jahren

Hallo,

soweit ich das sehe hast du bisher nur geprüft, was für eine Exception der Client erhält. - Bau doch mal richtiges Exception-Handling im Server in der aufgerufenen Methode ein und Logge dies. Manchmal erfährt man auf diese Weise detaillierter, was passiert.

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

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

1.029 Beiträge seit 2010
vor 8 Jahren

Hi,

wie du an den Fehler rankommst hängt vom Hosting ab.

Die Client-Fehlermeldung ist hier leider schlicht nicht ausreichend wie du ja festgestellt hast.

Hier mal ein Beispiel, wie du an die wichtige Exception rankommst:
http://serverfault.com/questions/472120/detailed-errors-not-working-for-wcf-service-in-iis

LG

M
mipa_acc Themenstarter:in
318 Beiträge seit 2006
vor 8 Jahren

Alles klar, dann werde ich das heute Abend umsetzen. Rein Logisch kann es aber fast an nichts anderem liegen, dass die PluginLevel - Klasse während der Laufzeit nicht vom WcfService verarbeitet kann - oder?

M
mipa_acc Themenstarter:in
318 Beiträge seit 2006
vor 8 Jahren

Hallo,

so, Logging ist implementiert, hätte ich aber eigentlich nicht gebraucht, jetzt wird´s richtig interessant!

Ausschnitt aus meiner WcfService.svc.cs


        public AvailablePluginLight GetLightPlugin(Guid guid) {

            try {

                //Go through all Available Plugins
                foreach (AvailablePlugin currentPlugin in PluginContainer.AvailablePlugins) {

                    if (currentPlugin.Guid == guid) {

                        return new AvailablePluginLight(currentPlugin.Instance.Name,
                                                                currentPlugin.Instance.Description,
                                                                currentPlugin.Instance.Author,
                                                                currentPlugin.Instance.Version,
                                                                currentPlugin.Instance.PluginType,
                                                                currentPlugin.Instance.Guid,
                                                                currentPlugin.Instance.ClientGuiOutput,
                                                                currentPlugin.Instance.Level);

                    }

                }

                return new AvailablePluginLight();

            } catch (Exception ex) {

                Logging.SetErrorMessage("Test", ex);

            }

            return new AvailablePluginLight();

        }   <--- Ich gehe an dieser Stelle mit F11 rein

Ich erhalte mein Objekt incl. alle PluginLevel Daten! Am Ende der Methode gehe ich mit F11 weiter. Ich gelange beim Debuggen dann in meine AvailablePluginLight - Klasse, wo ich alle Getter von den Properties durchgehe. Beim return von meinem Level Scheppert´s dann:


//Ausschnitt aus der AvailablePluginLight Klasse
        [DataMember]
        public PluginLevel Level {
            get {
                return _Level;  <-- Hier ist der Ofen aus
            }
            set {
                _Level = value;
            }
        }

Ich erhalte die selbe Meldung, welche ich in Obigen Post bereit erwäht habe ( "bei System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)").

Weiter habe ich einfach mal eine Methode in meiner WcfKlasse, die Funktioniert wie folgt erweitert:


      public List<string> GetPluginWorkMethodes(Guid guid) {

            try {
                 AvailablePluginLight Blub = GetLightPlugin(new Guid("43cd6dc4-22a9-4c27-b866-a1bcbcc384ff"));
            } catch (Exception ex) {

                Logging.SetErrorMessage("Test aus funktioniertender Methode", ex);

            }

            List<string> MyList = new List<string>();
            ....
       }

Ich gelange nicht in den Catch-Block, sondern erhalte mein Objekt - so wie es sein soll (incl. PluginLevel)! Die Methode läuft nach wie vor ohne den Fehler durch.

Leider bin ich im WCF-Service Thema wirklich nicht sonderlich gut... Für mich erhärtet sich der Verdacht, dass es irgendwas mit der Übertragung zu tun hat.

Was denkt ihr?

Viele Grüße

W
955 Beiträge seit 2010
vor 8 Jahren

Hallo,

Du könntest den Verkehr mit Wireshark mal mitschneiden und schauen ob der Member mit übertragen wird.

16.807 Beiträge seit 2008
vor 8 Jahren

Schau Dir die InnerException an. Da wirste dann feststellen, was mit der Serialisierung der komplexen Klasse PluginLevel nicht stimmt.
Entweder fehlt dessen Definition oder ein Teil davon ist nicht serialisierbar.

Was daran nun "spannend" sei.... mir schleierhaft 😃

M
mipa_acc Themenstarter:in
318 Beiträge seit 2006
vor 8 Jahren

Ich kann die PluginLevels Klasse Serialisieren! Das ist das spannende, bzw. das, was ich nicht verstehe...

Leider kann ich auch nicht in die inner Exception sehen, da ich keine habe. Beim Debuggen bekomme ich keinerlei Fehler. Ich bekomme die Fehlermeldung im WCF-Testclient.

Ich versuche jetzt mal - wie von Taipi88 vorgeschlagen, die config so anzupassen,
dass ich bessere Fehlermeldungen bekomme.

Wenn es jemanden Interessiert, ich stelle auch gerne den Code zur Verfügung.