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:
-
[ServiceContract]
-
public interface ICustomerService
-
{
-
[OperationContract]
-
void AddCustomerInterface(ICustomer customer);
-
-
[OperationContract]
-
void AddCustomerBase(CustomerBase customer);
-
}
-
-
public class CustomerService : ICustomerService
-
{
-
public void AddCustomerInterface(ICustomer customer)
-
{
-
string msg = string.Format("Adding customer of type {0}.",
-
customer.GetType());
-
Console.WriteLine(msg);
-
}
-
-
public void AddCustomerBase(CustomerBase customer)
-
{
-
AddCustomerInterface(customer);
-
}
-
}
und die folgenden DataContracts:
-
public interface ICustomer
-
{
-
string Name
-
{
-
get;
-
set;
-
}
-
}
-
-
[DataContract]
-
public abstract class CustomerBase : ICustomer
-
{
-
[DataMember]
-
private string name;
-
public string Name
-
{
-
get
-
{
-
return name;
-
}
-
set
-
{
-
name = value;
-
}
-
}
-
}
-
-
[DataContract]
-
public class SimpleCustomer : CustomerBase
-
{
-
}
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:
-
[ServiceContract]
-
public interface ICustomerService
-
{
-
[OperationContract]
-
void AddCustomerInterface(ICustomer customer);
-
-
[OperationContract]
-
void AddCustomerBase(CustomerBase customer);
-
}
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.
-
[DataContract]
-
public class ExtendedCustomer : CustomerBase
-
{
-
[DataMember]
-
private string customerID;
-
public string CustomerID
-
{
-
get
-
{
-
return customerID;
-
}
-
set
-
{
-
customerID = value;
-
}
-
}
-
}
-
-
[ServiceContract]
-
public interface ICustomerService
-
{
-
[OperationContract]
-
void AddCustomerInterface(ICustomer customer);
-
-
[OperationContract]
-
void AddCustomerBase(CustomerBase customer);
-
}
Das Spiel lässt sich nahezu unendlich so weiter spielen.
Eine Lösung dafür gibt es in Teil 2.
[...] habe ich im ersten Teil ein Problem geschildert, das auftritt, wenn man versucht Polymorphismus im Zusammenhang mit [...]
[...] 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 [...]