Let’s say class Person with the following structure
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;
namespace PersonService
{
[DataContract]
public class Person
{
private int id;
[DataMember(IsRequired = false, Order = 1)]
public int Id
{
get { return id; }
set { id = value; }
}
private string firstName;
[DataMember(IsRequired = true, Order = 2)]
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
private string lastName;
[DataMember(IsRequired = true, Order = 3)]
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
private int age;
[DataMember(IsRequired = true, Order = 4)]
public int Age
{
get { return age; }
set { age = value; }
}
private string country;
[DataMember(IsRequired = true, Order = 5)]
public string Country
{
get { return country; }
set { country = value; }
}
}
}
We wish to add 2 more classes derived from Person say EmployedPerson and Student with the following structure
Note: Please see the DataContract and DataMember attribute for the members of the class
namespace PersonService
{
[DataContract]
class Student : Person
{
private string college;
[DataMember]
public string College
{
get { return college; }
set { college = value; }
}
private string favSubject;
[DataMember]
public string FavSubject
{
get { return favSubject; }
set { favSubject = value; }
}
}
}
namespace PersonService
{
[DataContract]
class EmployedPerson : Person
{
private string title;
[DataMember]
public string Title
{
get { return title; }
set { title = value; }
}
private string emplyoedAt;
[DataMember]
public string EmplyoedAt
{
get { return emplyoedAt; }
set { emplyoedAt = value; }
}
}
}
Now that we have Student or EmployedPerson derived from Person, each of the derived classes follow “is a” relationship.
And if you assume WCF to support inheritence by default then you are mistaken. What do I mean by default?
Leat’s track back for a second and look at the definition of our service class
[ServiceContract(Namespace="http://techkrishnan.blogspot.com")]
public interface IPersonService
{
[OperationContract(IsOneWay=true)]
void AddPerson(Person pers);
[OperationContract()]
int RemovePerson(Person pers);
[OperationContract]
Person GetPersonById(int nId);
//get collection of person
[OperationContract]
PersonCollection GetPeople();
}
We have a function GetPersonById that returns Person class. If you think that you can return Student or EmployedPerson in place of Person, then you are in for surprise.
Only way to bring in inheritence is through “knowntype”. There are atleast 3 different ways to achieve this.
Choice 1
namespace PersonService
{
[ServiceKnownType(typeof(EmployedPerson))]
[ServiceKnownType(typeof(Student))]
[ServiceContract(Namespace="http://techkrishnan.blogspot.com")]
public interface IPersonService
{
[OperationContract(IsOneWay=true)]
void AddPerson(Person pers);
[OperationContract()]
int RemovePerson(Person pers);
[OperationContract]
Person GetPersonById(int nId);
//get collection of person
[OperationContract]
PersonCollection GetPeople();
}
}
Define at the service level the types that will be used in the service. This has inherent advantage of using EmployedPerson or Student or Person in every occurrence of Person class in any OperationContract which accepts or return Person class
Alternatively if you are sure that there are specific operations that will need inheritence, you can define them just above those operations like the one below
namespace PersonService
{
[ServiceContract(Namespace="http://techkrishnan.blogspot.com")]
public interface IPersonService
{
[OperationContract(IsOneWay=true)]
void AddPerson(Person pers);
[OperationContract()]
int RemovePerson(Person pers);
[OperationContract]
[ServiceKnownType(typeof(EmployedPerson))]
[ServiceKnownType(typeof(Student))]
Person GetPersonById(int nId);
//get collection of person
[OperationContract]
PersonCollection GetPeople();
}
}
In this case AddPerson and RemovePerson methods will accept only Person data type (and not EmployedPerson or Student) while GetPersonById can return either EmployedPerson or Student
Choice 2
Dynamically get the list of inherited classes from Person
namespace PersonService
{
[DataContract]
[KnownType("GetKnownTypes")]
public class Person
{
private int id;
[DataMember(IsRequired = false, Order = 1)]
public int Id
{
get { return id; }
set { id = value; }
}
private string firstName;
[DataMember(IsRequired = true, Order = 2)]
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
private string lastName;
[DataMember(IsRequired = true, Order = 3)]
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
private int age;
[DataMember(IsRequired = true, Order = 4)]
public int Age
{
get { return age; }
set { age = value; }
}
private string country;
[DataMember(IsRequired = true, Order = 5)]
public string Country
{
get { return country; }
set { country = value; }
}
static Type[] GetKnownTypes()
{
return new Type[] { typeof(EmployedPerson), typeof(Student) };
}
}
}
GetKnownTypes can fetch values from database or some other custom code too
Choice 3
Pick from configuration files. Check MSDN for section <datacontractserializer> and <declaredTypes> in them
No comments:
Post a Comment