Black Friday = Pricewatch Bekijk onze selectie van de beste Black Friday-deals en voorkom een miskoop.

[C#] polymorfisme met web services?

Pagina: 1
Acties:

  • Bananenplant
  • Registratie: Januari 2001
  • Laatst online: 12:44
Ik ben bezig met het ontwikkelen van een applicatie waarin meerdere web services met elkaar moeten praten. Ook zou ik graag met behulp van overerving ervoor willen zorgen dat bepaalde door meerdere web services gedeelde functionaliteit in een parentklasse zitten. Als simpel voorbeeld:

C#:
1
2
3
4
5
[WebMethod]
public bool isAvailable()
{
  return true;
}


Dit wordt gedeeld door elke web service in het systeem en ik laat andere web services erven van de klasse met deze methode erin. Ik heb nu een simpel programmaatje gemaakt en heb daarin een web reference naar deze basisklasse gemaakt. Als ik nu echter probeer deze methode aan te roepen op een webservice die van deze basisklasse erft krijg ik een SOAP exception met als bericht "Server did not recognize the value of HTTP Header".

Probeer ik nu iets van web services te vragen wat niet kan? Een andere optie zou zijn dat ik runtime proxy classes ga genereren en verder soortgelijk gedoe, maar dat wil ik als het enigszins kan toch even voorkomen :/ .

Voor de goede orde, ik heb al wel wat gezocht, maar die resultaten gaan meer over polymorfisme van objecten die je tussen web services heen en weer kaatst. En fouten daarbij in eerdere versies van .Net.

Update: als ik bovenstaande doe met meerdere van elkaar ervende web services binnen één assembly gaat het wel goed.

Update2: op msdn staat ook iets... dit voorbeeld ga ik even na het weekend uitproberen. Zou het abstract maken van de base class helpen ofzo :? ?

[ Voor 11% gewijzigd door Bananenplant op 29-08-2008 17:37 ]

💶 Wil je in een vrije democratie blijven wonen? Betaal dan voor nieuws. 📰
❌ ceterum censeo contra factiones ad dextrum extremum esse pugnandum. 🙅🏻‍♂️


  • EfBe
  • Registratie: Januari 2000
  • Niet online
Webservices & inheritance / polymorphism: forget it. Evenals generics in veel gevallen. Het probleem is dat de XML die gegenereerd wordt geen type info bevat, maar louter data. De ontvanger aan de andere kant maakt EERST een nieuwe instance van het verwachtte type en stopt dan daar de data in.

Je snapt al: dit gaat mis wanneer aan de hand van de XML het type moet worden bepaalt. Ergo: gaat niet werken. WCF is iets slimmer, maar ook daar word je er veelal niet vrolijk van.

Jouw probleem is dus dat je de basis service hebt gereferenced. De client maakt dus een basisservice instance aan, niet die van het subtype.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • Bananenplant
  • Registratie: Januari 2001
  • Laatst online: 12:44
Maar bij meerdere web services in dezelfde assembly werkt het wél. Als je iets doet als dit:
C#:
1
Object blaat = new String();

Dan is het toch ook logisch dat je met blaat alleen dingen mag doen die bij Object horen? Dan boeit het me niet wat voor Object het precies is. Datzelfde wil ik ook met web services kunnen doen en bij meerdere web services in dezelfde assembly (meerdere .asmx-files dus) heb ik het ook werkend gekregen :/ .

Maar goed, inderdaad eens proberen of WCF dit beter kan :) .

💶 Wil je in een vrije democratie blijven wonen? Betaal dan voor nieuws. 📰
❌ ceterum censeo contra factiones ad dextrum extremum esse pugnandum. 🙅🏻‍♂️


Verwijderd

ik heb geen ervaring met C# of met SOAP maar als ik het goed volg is het probleem dat de XML het derived type doorgeeft i.p.v de basis en dat aan de andere kant dit niet gaat werken omdat de relatie tussen de basis klasse en de derived klasse mist/misloopt. of andersom kan ook natuurlijk.

mischien dat statische polymorphisme wel werkt maar ik weet niet of C# dit biedt. in C++ kan dit wel via o.a templates. het komt er eigenlijk op neer dat at compile-time de basis-klasse in de derived-klasse wordt geplaatst en er zo dus een enkele opzichzelf staande klasse at runtime onstaat.

het is maar een gedachte aangezien ik geen enkele ervaring heb op dit gebied. ik heb wel eens wat met SOAP gedaan maar dat was zonder het hele .Net framewerk erbij. zoek het eens op op google.

  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 17-10 16:43
Werkt het ook niet als je objecten heen en weer gaat casten?

Dus je derived class via XML doorgeven en daarna weer casten naar zijn base class? (long shot maar wie weet, want zoals ik de post hierboven lees is er uberhaupt geen info over de base class).

~ Mijn prog blog!


  • EfBe
  • Registratie: Januari 2000
  • Niet online
Volgens mij snap je niet wat ik meldde hierboven. Ik zal het nog een keer uitleggen.

Stel je hebt webservice W1 en W2. W2 erft van W1. Als jij dan een reference legt naar W1, dan maakt de wsdl tool een proxy aan die de xml stopt in een W1 compatible proxy. Je kunt dus nooit die proxy naar W2 casten, want het is een W1 proxy.

Idem dito met webmethods die een basetype teruggeven. Stel je hebt Webmethod M en die geeft een object van het type Person terug. Maar omdat de method polymorph is kan er ook een Employee worden teruggegeven. Echter, de wsdl tool creeert een proxy type Person en een method call naar de method die een PERSON verwacht. De data wordt dus in een instance van Person gestopt. Ookal is het een Employee, dat weet die proxy niet, want, zoals ik al zei: hij gaat EERST een nieuwe instance maken van het type dat hij verwacht, en stop DAN daar de data in. Dus dat de data (XML) van een subtype afkomstig is, dat weet die proxy niet want hij kijkt er niet naar om het type te bepalen.

Dus: webservices en polymorphism: forget it.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • Daspeed
  • Registratie: Maart 2001
  • Laatst online: 12:44
EfBe schreef op zaterdag 30 augustus 2008 @ 10:55:
Idem dito met webmethods die een basetype teruggeven. Stel je hebt Webmethod M en die geeft een object van het type Person terug. Maar omdat de method polymorph is kan er ook een Employee worden teruggegeven. Echter, de wsdl tool creeert een proxy type Person en een method call naar de method die een PERSON verwacht. De data wordt dus in een instance van Person gestopt. Ookal is het een Employee, dat weet die proxy niet, want, zoals ik al zei: hij gaat EERST een nieuwe instance maken van het type dat hij verwacht, en stop DAN daar de data in. Dus dat de data (XML) van een subtype afkomstig is, dat weet die proxy niet want hij kijkt er niet naar om het type te bepalen.

Dus: webservices en polymorphism: forget it.
Misschien dat ik je verkeerd begrijp, maar polymorphisme is tot op zekere hoogte wel mogelijk
Webservice
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Service1 : WebService
{
   [WebMethod]
   public Person[] GetPersons()
   {
       Person p = new Person();
       p.Name = "John";

       Employee e = new Employee();
       e.Name = "Pete";
       e.Function = "Boss";

       return new Person[] { p, e };
    }
}
[XmlInclude(typeof(Employee))]
public class Person
{
    public string Name { get; set; }
}
public class Employee : Person
{
    public string Function { get; set; }
}


Webservice client (andere solution)
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Program
{
    static void Main(string[] args)
    {
        Person[] persons = new Service1().GetPersons();
        foreach (Person p in persons)
        {
            Employee e = p as Employee;
            if (e != null)
            {
                Console.WriteLine(
                    string.Format("Employee with name '{0}' and function '{1}'.",
                        e.Name,
                        e.Function));
            }
            else
            {
                Console.WriteLine(
                    string.Format("Person with name '{0}'.",
                        p.Name));
            }
        }
        Console.ReadLine();
    }
}


Output
code:
1
2
Person with name 'John'.
Employee with name 'Pete' and function 'Boss'.

  • Bananenplant
  • Registratie: Januari 2001
  • Laatst online: 12:44
EfBe schreef op zaterdag 30 augustus 2008 @ 10:55:
Stel je hebt webservice W1 en W2. W2 erft van W1. Als jij dan een reference legt naar W1, dan maakt de wsdl tool een proxy aan die de xml stopt in een W1 compatible proxy. Je kunt dus nooit die proxy naar W2 casten, want het is een W1 proxy.
Ik wil helemaal niet casten maar methoden die in W1 gedefinieerd zijn op een web service van klasse W2 uitvoeren waarbij het me dus geen bal boeit dat het eigenlijk W2 is omdat ik 'm als W1 wens aan te spreken (wat in normale code ook gewoon kan). En ik ben nu even erg gefrustreerd omdat ik volgens mij van de week al wel een werkend concept ervan had maar er nu niet bij kan en hier ook geen VS heb om het na te bouwen :P .

[ Voor 9% gewijzigd door Bananenplant op 30-08-2008 15:19 ]

💶 Wil je in een vrije democratie blijven wonen? Betaal dan voor nieuws. 📰
❌ ceterum censeo contra factiones ad dextrum extremum esse pugnandum. 🙅🏻‍♂️


  • EfBe
  • Registratie: Januari 2000
  • Niet online
Daspeed schreef op zaterdag 30 augustus 2008 @ 14:25:
[...]

Misschien dat ik je verkeerd begrijp, maar polymorphisme is tot op zekere hoogte wel mogelijk
Webservice
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Service1 : WebService
{
   [WebMethod]
   public Person[] GetPersons()
   {
       Person p = new Person();
       p.Name = "John";

       Employee e = new Employee();
       e.Name = "Pete";
       e.Function = "Boss";

       return new Person[] { p, e };
    }
}
[XmlInclude(typeof(Employee))]
public class Person
{
    public string Name { get; set; }
}
public class Employee : Person
{
    public string Function { get; set; }
}


Webservice client (andere solution)
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Program
{
    static void Main(string[] args)
    {
        Person[] persons = new Service1().GetPersons();
        foreach (Person p in persons)
        {
            Employee e = p as Employee;
            if (e != null)
            {
                Console.WriteLine(
                    string.Format("Employee with name '{0}' and function '{1}'.",
                        e.Name,
                        e.Function));
            }
            else
            {
                Console.WriteLine(
                    string.Format("Person with name '{0}'.",
                        p.Name));
            }
        }
        Console.ReadLine();
    }
}


Output
code:
1
2
Person with name 'John'.
Employee with name 'Pete' and function 'Boss'.
En nu individuele objects... :) De array container kan helpen, ik had het over individuele objects. Overigens verbaast het me wel dat je employee objects over de lijn krijgt, want dat type is niet bekend in de XML, tenzij je wcf gebruikt.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • Daspeed
  • Registratie: Maart 2001
  • Laatst online: 12:44
EfBe schreef op zaterdag 30 augustus 2008 @ 20:56:
[...]

En nu individuele objects... :) De array container kan helpen, ik had het over individuele objects.
Wat bedoel je precies met individuele objects?
Dit?
C#:
1
2
3
4
5
6
7
8
9
[WebMethod]
public Person GetSingle()
{
    Employee e = new Employee();
    e.Name = "Henk";
    e.Function = "Tester";

    return e;
}


C#:
1
2
Console.WriteLine(
    (new Service1().GetSingle() as Employee).Function);

Dat werkt namelijk ook :)
Overigens verbaast het me wel dat je employee objects over de lijn krijgt, want dat type is niet bekend in de XML, tenzij je wcf gebruikt.
Daar zorgt dit voor:
code:
1
[XmlInclude(typeof(Employee))]

SoapInclude kan overigens ook, het resultaat is dat in de WSDL het type wordt meegenomen:
XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<s:complexType name="Person">
<s:sequence>
  <s:element minOccurs="0" maxOccurs="1" name="Name" type="s:string" />
</s:sequence>
</s:complexType>
<s:complexType name="Employee">
<s:complexContent mixed="false">
  <s:extension base="tns:Person">
    <s:sequence>

      <s:element minOccurs="0" maxOccurs="1" name="Function" type="s:string" />
    </s:sequence>
  </s:extension>
</s:complexContent>
</s:complexType>

  • EfBe
  • Registratie: Januari 2000
  • Niet online
Ah, dat verandert de zaak, die Xmlinclude was ik even vergeten. Overigens denk ik nog steeds dat wat ik beschreef de oorzaak is van TS's probleem.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • Bananenplant
  • Registratie: Januari 2001
  • Laatst online: 12:44
Okay, ik heb nu even getest en ik had het inderdaad voorelkaar.

De parentklasse:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml.Linq;

namespace ParentService
{
    /// <summary>
    /// Summary description for Service1
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
    // [System.Web.Script.Services.ScriptService]
    public class ParentService : System.Web.Services.WebService
    {

        [WebMethod]
        public virtual string HelloWorld()
        {
            return "Hello World";
        }
    }
}


De childklasse:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml.Linq;

namespace ChildService
{
    /// <summary>
    /// Summary description for Service1
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
    // [System.Web.Script.Services.ScriptService]
    public class ChildService : ParentService.ParentService
    {

        [WebMethod]
        public override string HelloWorld()
        {
            return "Hallo Welt";
        }

        [WebMethod]
        public string moi()
        {
            return "moi";
        }

    }
}


Ik heb daarnaast een simpel windowsformuliertje gemaakt met een web reference naar de parentklasse dat HelloWorld() aanroept op een in te voeren web service en het resultaat in het window toont. Als ik de URL van de parent invul krijg ik "Hello World" en als ik de child invul krijg ik "Hallo Welt". Het kan dus echt.

💶 Wil je in een vrije democratie blijven wonen? Betaal dan voor nieuws. 📰
❌ ceterum censeo contra factiones ad dextrum extremum esse pugnandum. 🙅🏻‍♂️


  • Daspeed
  • Registratie: Maart 2001
  • Laatst online: 12:44
Hmm, interessant. Kan het misschien zo zijn dat je in je eerdere 'probleemsituatie' verschillende namespace URI's gebruikt had?

Aangezien die namespace in de soapheader meegegeven kan worden.
http://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383498

  • Bananenplant
  • Registratie: Januari 2001
  • Laatst online: 12:44
Woei, dat was inderdaad het probleem :) !

💶 Wil je in een vrije democratie blijven wonen? Betaal dan voor nieuws. 📰
❌ ceterum censeo contra factiones ad dextrum extremum esse pugnandum. 🙅🏻‍♂️


  • Bananenplant
  • Registratie: Januari 2001
  • Laatst online: 12:44
...het leukste is nog wel dat je blijkbaar ook met een reference naar de childklasse methoden kunt uitvoeren op een instantie van de parent 8)7 ...

💶 Wil je in een vrije democratie blijven wonen? Betaal dan voor nieuws. 📰
❌ ceterum censeo contra factiones ad dextrum extremum esse pugnandum. 🙅🏻‍♂️

Pagina: 1