views:

796

answers:

2

Hi,

I'm looking at building a WCF service that can store/retrieve a range of different types. Is the following example workable and also considered acceptable design:

[ServiceContract]
public interface IConnection
{        
   [OperationContract]
    IObject RetrieveObject(Guid ObjectID); 

   [OperationContract]
    Guid StoreObject(IObject NewObject); 


}

[ServiceContract]
[ServiceKnownType(IOne)]
[ServiceKnownType(ITwo)]
public interface IObject
{
    [DataMember]
    Guid ObjectID;

}

[ServiceContract]
public interface IOne:IObject
{
    [DataMember]
    String StringOne;

}

[ServiceContract]
public interface ITwo:IObject
{
    [DataMember]
    String StringTwo;

}

When using the service, I would need to be able to pass the child types into the StoreObject method and get them back as their Child type from the RetrieveObject method.

Are there better options?

Thanks, Rob

A: 

I believe that your solution is correct. I used the same approach, and it worked quite well.

Ariel

ArielBH
+5  A: 

Your example will not compile because interfaces cannot contain fields, which is what ObjectID, StringOne, and StringTwo are. What you're trying to define with IObject, IOne, and ITwo is a data contract, not a service contract. As such, you should be using the DataContract attribute, not the ServiceContract attribute and classes, not interfaces.

[DataContract]
[KnownType(MyOne)]
[KnownType(MyTwo)]
public class MyObject
{
    [DataMember]
    Guid ObjectID;
}
[DataContract]
public class MyOne : MyObject
{
    [DataMember]
    String StringOne;
}
[DataContract]
public class MyTwo : MyObject
{
    [DataMember]
    String StringTwo;
}

Notice that these are classes, not interfaces. The DataContract attribute has replaced the ServiceContract attribute. The KnownType attribute has replaced the ServiceKnownType attribute. This is more canonical from what I've seen.

Your service contract would then be defined like this:

[ServiceContract]
public interface IConnection
{
    [OperationContract]
    [ServiceKnownType(typeof(MyOne))]
    [ServiceKnownType(typeof(MyTwo))]
    MyObject RetrieveObject(Guid ObjectID);

    [OperationContract]
    [ServiceKnownType(typeof(MyOne))]
    [ServiceKnownType(typeof(MyTwo))]
    Guid StoreObject(MyObject NewObject);
}

You can put the ServiceKnownType attributes at the contract level (i.e., beneath the ServiceContract attribute) to have it apply to all operations of the contract.

[ServiceContract]
[ServiceKnownType(typeof(MyOne))]
[ServiceKnownType(typeof(MyTwo))]
public interface IConnection
{
    [OperationContract]
    MyObject RetrieveObject(Guid ObjectID);

    [OperationContract]
    Guid StoreObject(MyObject NewObject);
}

You can use interfaces in your data contracts like this:

interface IEmployee
{
    string FirstName
    { get; set; }
    string LastName
    { get; set; }
}
[DataContact]
class Employee : IEmployee
{...}

However, the IEmployee interface is not included in the exported metadata. So if you use svcutil to generate your proxy classes, your clients will not know about IEmployee. This is not a big deal if your service and client reside in the same application (which is good way to communicate between app domains). However if your client is separate from your service (in the overwhelming majority of cases, it will be), this becomes problematic because you'll have to duplicate the IEmployee interface on the client-side manually.

Matt Davis