Skip to content

Interfaces und abstrakte Klassen als Parameter – Teil 1

Anfang der Woche hat mich ein Arbeitskollege danach gefragt, ob es möglich ist Interfaces oder abstrakte Klassen als Parameter an (WCF-)Service-Methoden zu übergeben. Zunächst war mir nicht ganz klar, was er eigentlich vor hatte, dabei liegt es klar auf der Hand. Die Frage war, ob man das Prinzip des Polymorphismus der Objektorientierung auch auf WCF-Services anwenden kann.

Meine erste Antwort: Ja, klar!

Nicht ganz so klar, wie sich später herausstellte ...

Angenommen wir haben einen (sehr einfachen) Service wie den folgenden:

C#:
  1. [ServiceContract]
  2. public interface ICustomerService
  3. {
  4.     [OperationContract]
  5.     void AddCustomerInterface(ICustomer customer);
  6.  
  7.     [OperationContract]
  8.     void AddCustomerBase(CustomerBase customer);
  9. }
  10.  
  11. public class CustomerService : ICustomerService
  12. {
  13.     public void AddCustomerInterface(ICustomer customer)
  14.     {
  15.         string msg = string.Format("Adding customer of type {0}.",
  16.             customer.GetType());
  17.         Console.WriteLine(msg);
  18.     }
  19.  
  20.     public void AddCustomerBase(CustomerBase customer)
  21.     {
  22.         AddCustomerInterface(customer);
  23.     }
  24. }

und die folgenden DataContracts:

C#:
  1. public interface ICustomer
  2. {
  3.     string Name
  4.     {
  5.         get;
  6.         set;
  7.     }
  8. }
  9.  
  10. [DataContract]
  11. public abstract class CustomerBase : ICustomer
  12. {
  13.     [DataMember]
  14.     private string name;
  15.     public string Name
  16.     {
  17.         get
  18.         {
  19.             return name;
  20.         }
  21.         set
  22.         {
  23.             name = value;
  24.         }
  25.     }
  26. }
  27.  
  28. [DataContract]
  29. public class SimpleCustomer : CustomerBase
  30. {
  31. }

OK, soweit so gut! Wo ist das Problem?
Wenn wir einen Client haben, der beispielsweise eine SimpleCustomer-Instanz an diesen Service sendet, ist nicht klar, wie diese deserialisiert werden muss. Der Service hat nämlich keine Informationen darüber, wie ein "Name" aus dem Schema innerhalb des eingehenden Streams auf die spezielle Klasse, in diesem Fall SimpleCustomer, gemappt werden soll.

Hier kommt das Attribut ServiceKnownType ins Spiel. Durch dieses Attribut können dem Service Typen bekannt gemacht werden, die bei der Serialisierung und Deserialisierung genutzt werden sollen. Das sieht dann so aus:

C#:
  1. [ServiceContract]
  2. [ServiceKnownType(typeof(SimpleCustomer))]
  3. public interface ICustomerService
  4. {
  5.     [OperationContract]
  6.     void AddCustomerInterface(ICustomer customer);
  7.  
  8.     [OperationContract]
  9.     void AddCustomerBase(CustomerBase customer);
  10. }

Auf den ersten Blick war das eine sehr schnelle und schöne Lösung für das Problem. Auf den zweiten Blick aber waren wir uns schnell darüber einig, dass das mit "echtem" Polymorphismus nicht viel zu tun hat. Wenn wir eine weitere Klasse einführen, dann müssten wir diese auch über das ServiceKnownType-Attribut bekannt machen.

C#:
  1. [DataContract]
  2. public class ExtendedCustomer : CustomerBase
  3. {
  4.     [DataMember]
  5.     private string customerID;
  6.     public string CustomerID
  7.     {
  8.         get
  9.         {
  10.             return customerID;
  11.         }
  12.         set
  13.         {
  14.             customerID = value;
  15.         }
  16.     }
  17. }
  18.  
  19. [ServiceContract]
  20. [ServiceKnownType(typeof(SimpleCustomer))]
  21. [ServiceKnownType(typeof(ExtendedCustomer))]
  22. public interface ICustomerService
  23. {
  24.     [OperationContract]
  25.     void AddCustomerInterface(ICustomer customer);
  26.  
  27.     [OperationContract]
  28.     void AddCustomerBase(CustomerBase customer);
  29. }

Das Spiel lässt sich nahezu unendlich so weiter spielen.
Eine Lösung dafür gibt es in Teil 2.

Categories: Programming, Web.

Tags: , , , ,

Comment Feed

2 Responses



Some HTML is OK

or, reply to this post via trackback.

Continuing the Discussion

  1. [...] habe ich im ersten Teil ein Problem geschildert, das auftritt, wenn man versucht Polymorphismus im Zusammenhang mit [...]

  2. [...] will use the Customer-Service already presented in Interfaces und abstrakte Klassen als Parameter - Teil 1 as an example (it's in german, but the code stays the same [...]

WP SlimStat