Laden...

WebService Typen Konvertierung

Erstellt von MuhammedC# vor 15 Jahren Letzter Beitrag vor 15 Jahren 3.227 Views
M
MuhammedC# Themenstarter:in
222 Beiträge seit 2005
vor 15 Jahren
WebService Typen Konvertierung

Hallo,

ich habe eine Soultion, welche aus mehreren Teilprojekten besteht.
Zum einen wäre da eine Library welche einen von mir definierte Klasse enthält, welche ich zum Datenspeichern benutze. Die Klasse nenn ich mal KlasseA
Nun gibt es in der Solution noch einen Webservice, welcher als Übergabeparameter für eine Funktion jenen bestimmten Typen erwartet.
Ein drittes Projekt ist ein den Webservice Konsumierender Client. Dieser kennt aber auch die Library.

Wenn ich von dem Client aus die Klasse aus der Library fülle und dem Webservice vor die Füße schmeißen will, sagt Visual Studio mir, das er den Typen KlasseA nicht in den Typen ...webReference.KlasseA konvertieren kann.
Da mir Visual Studio ja beim generieren des Proxys diese Typen irgendwie noch mal mit in dem Proxy neuanlegt.

Jetzt meine Frage wie konvertiere ich am geschicktesten den ursprünglichen Typen aus der shared library zu etwas was der Webservice bzw. der Compiler akzeptieren?

Danke im Voraus
LG MC#

3.971 Beiträge seit 2006
vor 15 Jahren

Wenn du 2 gleiche Klassen haben musst, schreib doch in der KlasseA (deine eigene) einen impliziten oder expliziten Konvertierungsoperator.

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

M
MuhammedC# Themenstarter:in
222 Beiträge seit 2005
vor 15 Jahren

hmmm,

aber es sind ja eigentlich komplett identische klassen, nur das die eine halt vom designer generiert wurde.... Hält mir .NET da kein Hintertürchen offen?

LG MC#

3.971 Beiträge seit 2006
vor 15 Jahren

Ansonsten nachträglich die vom Designer löschen. Eventuell Namespaces noch anpassen.

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

M
MuhammedC# Themenstarter:in
222 Beiträge seit 2005
vor 15 Jahren

aber dann sind die bei jedem aktualisieren der web reference wieder da. ich kann mir einfach nicht vorstellen das dies von ms so gewollt ist. naja werd ich mir nen converter machen und die objekte stumpf umkopieren. aber trotzdem danke...

G
497 Beiträge seit 2006
vor 15 Jahren

das Problem ist, daß für den Client nicht klar ist, daß die vom Webservice erwartete Klasse dieselbe ist wie die, die du in deiner Library hast. Denn der Client kennt die Implementierung des Webservices nicht (soll er auch nicht kennen). Dementsprechend generiert Visual Studio bei Aktualisierung der WebReference eine Klasse, die alle vom Webservice benötigten Eigenschaften enthält. Dass du zufälligerweise eine Klasse hast, die bereits alle diese Eigenschaften besitzt, ist VS natürlich egal - es weiß ja nicht, daß die Klasse dieselbe ist wie die, die auf der anderen Seite benutzt wird.

Ich hab vor einiger Zeit ein ähnliches Problem gehabt und das ganze pragmatisch gelöst, da ich nicht zuviel Zeit damit verlieren wollte: ich hab in der Client-Implementierung einen Konverter geschrieben, der mir aus dem lokal bekannten Objekt des Typs X das für den Webservice benötigte Objekt erzeugt. Man kann das ganze über Reflection auch etwas automatisieren, indem man in einer Methode Quell- und Zieltyp angibt und dann nur prüft, welche Eigenschaften und Felder gleich benannt und gleichen Datentyps sind und dann die Werte übernimmt. Dann muss man sich nicht jedes mal, wenn man an der Klasse Eigenschaften ändert, mit dem Konverter auf Clientseite befassen.

übrigens: schau dir mal die Webcasts (http://www.microsoft.com/germany/msdn/webcasts/finder/default.mspx) von MS zum Thema Webservices an. Besonders die Einführung zu dem Thema (ist aus dem Jahr 2004) ist zum Verständnis der zugrundeliegenden Techniken ganz interessant. Denn es werden im Grunde keine .NET-Klassen zwischen Server und Client hin- und hergeschoben, sondern nur XML-Dokumente (serialisierte Versionen dieser Klassen). Auf beiden Seiten wird dann daraus jeweils ein Objekt erzeugt (deserialisiert). Man kann bei dem Thema auch ganz schnell Probleme bekommen, wenn man dem Framework nicht explizit vorgibt, wie es eine Eigenschaft oder einen Typ serialisieren soll. Da kann eine Umbenennung eines Parameters in einer Webservice-Methode dazu führen, daß der Client nicht mehr mit dem Webdienst kommunizieren kann, obwohl sich für ihn eigentlich nichts geändert hat.

M
MuhammedC# Themenstarter:in
222 Beiträge seit 2005
vor 15 Jahren

Hallo,

erst mal vielen lieben dank GeraldGreen ( 🙂 ) für die ausführliche Antwort. Das Webservice nur mit XML kommunizieren war mir bekannt, nur da ich leider noch nicht wirklich aus der Welt von .NET und Webservice rausgekommen bin, ist leider durch die Codegenerierung vom VS etwas an Know-How auf der Strecke geblieben.
Die Sache mit dem Konverter habe ich schon umgesetzt bin nur leider wenig zufrieden mit der Lösung, da ich ansonsten in diesem Projekt sehr systematisch vorgegangen bin. Die Objekte die hin und her geschickt werden habe ich Contract-First technisch schön ordentlich in XSD designed und will sie jetzt halt auch portabel benutzen. Ich hatte die Frage eingehend so vereinfacht formuliert, da ich dachte das dies ein grundlegendes Problem wäre über das schon andere gestolpert sind.

Wie macht es das Framework denn eigentlich? Immerhin kann ich z.b. ein DataSet ja auch über einen Webservice schicken ohne das der Proxy mir einen neuen Typen anlegt.
LG MC#

G
497 Beiträge seit 2006
vor 15 Jahren

für ein untypisiertes Dataset muss das Framework keinen neuen Typen anlegen, da wird einfach die Standardklasse genommen (bzw. die übergebene Auflistung wird in ein Dataset gewandelt). Das passiert (wenn ich mich recht erinnere) auch, wenn du elementare Datentypen als Parameter benutzt (String, Integer usw.). Auch da erzeugt VS keine neuen Klassen, sondern castet das einfach in den entsprechenden .NET-Typen. Das ganze funktioniert nur dann nicht mehr, wenn VS nicht weiß, welchen Typen es nehmen soll. Teilweise kann man in VS 2008 auch Konvertierungsoptionen einstellen (z. B. für Auflistungen), teilweise ist das wahrscheinlich einfach fest verdrahtet. Sobald VS mit dem Typ nix anfangen kann bzw. der Typ nicht Teil des .NET-Frameworks ist, legt VS eine Proxyklasse dafür an.

Man kann auch selbst mit dem Webdienst kommunizieren, ohne die von VS generierten Klassen zu benutzen (man kann sich ja auch den generierten Code anschauen und als Grundlage nutzen). Nachteil ist dann allerdings, daß man dann nicht mal eben die Update-Funktion von Visual Studio zur Aktualisierung der Webreferenz (weil es eben diese nicht mehr gibt) nutzen kann. Wäre in deinem Fall aber auch nicht nötig, da beide Seiten dieselben Klassen nutzen. Du müsstest dann allerdings dafür sorgen, daß auf beiden Seiten dieselbe Version der Klassenbibliothek zum Einsatz kommt.

A
10 Beiträge seit 2008
vor 15 Jahren
Probleme mit Serialisierung von Webserviceparameter

Hallo,

Ich hab ein ähnliches Problem, bin noch ziemlich frisch mit C# und Webservices und bitte schon mal vorab um nachsicht 🙂

Ich habe ein Webservice das als Parameter eine von mir erstellte Klasse entgegen nimmt (Die Klasse hat string, datetime und guid properies ).

Ich rufe das Webservice aus einem C# Client auf und bekam die Fehlermeldung das die Objekte nicht zusammenpassen ( das eine im Webservice Proxy namespace und das andere im original namespace). Darauf hin hab ich mal versucht es einfach als Object Parameter zu handlen. Daraufhin wurde mir vom compiler mitgeteilt das ich das nur machen kann wenn ich Soapinclude bzw. xmlinclude verwende. Das hab ich dann auch probiert hat aber niemals funktioniert.

Ja jetzt bin ich ein wenig verzweifelt und auf diesen Thread gestoßen und würde nun gerne den Lösungansatzz für den von euch erwähnten Konverter ausprobieren. Da ich aber nicht so der pro bin was serialisierung und dergleicheb betrifft fehlt mir da der zugang? Wie kann ich mir so einen Konverter vorstellen?

Hier mein Webservice Code:



[WebService(Namespace = "http://www.xxx.at/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

public class MyService : System.Web.Services.WebService
{

    public MyService()
    {
    }

    [WebMethod]
    [SoapInclude(typeof(xxx.Common.MyObject) )]
    public void TestIt(Object  o)
    {
        //Start Logging
        Console.WriteLine("Test");

    }


Der Aufruf des Service:


xxx.MyService InternalWebservice = new xxx.MyService();
                try
                {
                    InternalWebservice.TestIt(new xxx.Common.MyObject());

                }
                catch (Exception ex)
                {

                    Console.WriteLine(ex.InnerException.Message );
                }


Die Exceptionmessage wenn ich es mit einem Object Parameter und SoapInclude mache:
"The type xxx.Common.MyObject was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically."

vielen dank im voraus für eure hilfe
lg
r.

3.971 Beiträge seit 2006
vor 15 Jahren

Object als Parameter oder Rückgabeelement macht keinen Sinn, da für Object kein DataContract-Attribut definiert wurde.

Du hast mehrere Möglichkeiten:*Du beschränkst dich auf eine der Klasse (deine eigene oder die vom Designer). Dabei würd ich meine eigene bevorzugen hast aber den Nachteil dass der Designer diese beim nächsten mal wieder generiert und verwendet.

  • Du baust dir in deiner Klasse/Struktur einen impliziten Konvertierungsoperator (in beide Richtungen *Oder du verwendest für das eine Element direkt Xml zu Kommunikation, bist aber selbst für das (De-)Serialisieren zuständig, was in dem Fall auch nicht das wahre sein wird.

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

A
10 Beiträge seit 2008
vor 15 Jahren

Erst mal danke für die schnelle Antwort,

Ganz ist mir das nicht klar mit dem Konvertierungsoperator, der compiler zwingt mich dazu das Objekt aus dem Webservice Projekt zu verwenden, obwohl ich ihm extra das Objekt aus dem "richtigen" Namespace angebe.

Nun mach ich also einen Konverter und rufe das Webservice mit MyObject.ToWebserviceObject() auf. was bleibt ist, das daß Webservice den "falschen" Objekttypen verlangt oder?

lg
r.

3.971 Beiträge seit 2006
vor 15 Jahren

Schau dir mal C# Galileo OpenBook - Kapitel 25 Benutzerdefinierte Konvertierung an.

Du definierst in deinem Struct (Foo1) folgende Methoden:


struct Foo1{
 public Foo2 ToFoo2();
 
 public Foo1(Foo2 foo);

 public static implicit operator Foo1(Foo2 foo);

 public static implicit operator Foo2(Foo1 foo);
}

Dabei ist Foo1 deine Klasse/Struct und Foo2 die generierte Klasse/Struct. Eine Änderung an Foo2 ist notwendig und der C# Compiler erkennt automatisch die Typumwandlung.

PS: Ist nicht der Beste Weg aber er sollte funktionieren und den Aufwand in Grenzen halten.

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

A
10 Beiträge seit 2008
vor 15 Jahren

Danke für die Infos Eichhörnchen,

Eine letzte Frage noch, was hat bringt es für einen Vorteil wenn ich das mittels eines Operators mache und dort die einzelnen Felder meiner Klasser rumkopiere und nicht in einer "normalen" Methode wo ich die Felder rüberschupfe?

bedankt
r.

3.971 Beiträge seit 2006
vor 15 Jahren

Egal ob du das in deiner eigene Methode machst oder dazu die Konvertierungsoperatoren verwendest, es muss erstmal Speicher für den Zieldatentyp angelegt werden (oder ist bereits), egal ob Heap oder Stack, anschließend kopierst du die einzelnen Member von A nach B.

Der Vorteil von Konvertierungsoperatoren ist, dass der C# kompiler diese Methoden automatisch erkennt und die entsprechenden Funktionsaufrufe macht, ohne dass du die Funktion als Programmierer angibst.

Beispiel:


struct Complex{
  private double re;
  private double im;

  public double ToDouble(){
    return re;
  }

  public Complex(double re, double im){
    this.re = re;
    this.im = im;
  }

  public Complex(double re){
    this.re = re;
  }

  public static implicit operator Complex(double re){
    return new Complex(re);
  }

  public static explicit operator double(Complex cm){
    return cm.ToDouble();
  }
}

Du hast eine Komplexe Zahl, die sich aus reeller Zahl und imginärer Zahl zusammensetzt. Möchtest du eine Komplexe Zahl in ein Double konvertieren hast du die Möglichkeit das über eine explizite Konvertierung oder über die Funktion ToDouble zu machen.

Möchtest du eine Double in eine Komplexe Zahl wandeln, kannst du nicht in Double eine Funktion ToComplex einbauen, sondern musst über die Operatorüberladung gehen, in dem Fall implizit

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

X
1.177 Beiträge seit 2006
vor 15 Jahren

Huhu,

da ich dieses Problem die letzte Zeit öfter hier lese wollte ich noch was dazu beitragen / zusammenfassen.

Ihr habt genau 2 Elegante Möglichkeiten (beide im Thread von kleines_eichhoernchen schon genannt)

  1. Definiert in eurem Datentypen einen
public static implizit operator MeinDatentyp ( Webservice.MeinDatentyp )

ob nun als Copy-Constructor oder direkt spielt keine Rolle. Dies sorgt dafür, dass der Compiler automatisch eine Konvertierung durchführen kann. Der Nachteil: es muss natürlich zu jeder Konvertierung diese Routine durchlaufen und die Daten in die andere Struktur kopiert werden. Der Vorteil: immer nutzbar und nur bei Feld-Änderungen anzupassen.

  1. Die generierten Klassen editieren.

Entfernt aus den generierten cs-Files die Typdeklarationen und passt den Namespace auf den eures Original-Typen an. Dann könnt ihr direkt eure eigenen Typen benutzen. Der Nachteil: bei jedem aktualisieren der Webreference immer wieder editieren. Der Vorteil: Die Typen sind identisch.

Ich benutze immer Version 1 - aus Bequemlichkeit. Version 2 wäre Ressoursenschonender.

🙂

Xynratron

Herr, schmeiss Hirn vom Himmel - Autsch!

Die Erfahrung zeigt immer wieder, dass viele Probleme sich in Luft auslösen, wenn man sich den nötigen Abstand bzw. Schlaf gönnt.

A
10 Beiträge seit 2008
vor 15 Jahren

Vielen Dank für eure Hilfe