tags:

views:

123

answers:

3

I'm quite stuck on one part of using WCF for a client/server messaging system, and would very much appreciate some help.

On the server, I've got a Message DataContract object where one of the properties points to a typed collection of MessageBody DataContracts. A stripped down version of the class looks like this:

[DataContract]
class Message {

  [DataMember]
  string From;

  [DataMember]
  string To;

  [DataMember]
  string Subject{get;set;}

  [DataMember]
  MessageBodies {get;}
}

[DataContract]
class MessageBodies : CollectionBase<MessageBody>{}

[DataContract]
class MessageBody {
  [DataMember]
  BodyType Type get {get;set;}

  [DataMember]
  string Body {get;set;}
}

From the App.Client.dll, I create a WCF Proxy of the a ServiceContract and DataContracts (btw: no referencing to a common 'App.Contracts.dll' where I could have put the above defined DataContracts), For transporting data from client to server, I'm now all set...

But from user functionality side on the client side, there's still a ways to go.

I want to ensure that users can work with the above properties, but with some type checking happening as they instantiate the client objects. For example, I wish the actual class that users work with to look more like:

class MessageBody {
  //ReadOnly only:    
  public BodyType Type get {get {return _Type;}}
  private BodyType _Type;

  //Validated property:
  public string Body {
    get{ return _Body;}
    set{
      if (value == null){throw new ArgumentNullException();}
      _Body = value;
      }
  }
  private string _Body;

  //Set via Constructor only:
  public MessageBody (BodyType type, string body){
    //validate type and body first...
    _Type = type;
    _Body = body;
  }
}

One direction I tried to use to solve this was as follows:

If I renamed the DataContract from Message to CommMessage, I could then wrap the POCO/WCF object with a smarter object... But although this would work for most of the properties, except for the collection properties:

public Message {
  CommMessage _InnerMessage;

  public string Subject {get {_InnerMessage.Subject;}}

  //Option 1: give direct access to inner object...but they are just poco's...
  public CommMessageBodies MessageBodies { get {_InnerMessage.Addresses;}} 

  //Option 2...don't have one yet...what I would like is something like
  //this (returning MessageBody rather than CommMessageBody):
  //public MessageBodies MessageBodies {get {_InnerMessage.Bodies;}}
}

Thanks very much for any and all suggestions!

+1  A: 

You're looking for something that's not meant to be there. The types on the client will not, in general, be the same as the types on the server, and, in general, they should not be. In the general case, the client won't even be running .NET.

Remember that these Data Contracts are meant to become the XML Schema definitions of some XML messags that will flow from the client to the service. XML Schema does not describe programming-language concepts such as Read-only.

If you feel you need the clients to have an API like that, then you do need them to use an assembly that you will have to provide. This assembly could contain the exact same data contract types that the server is using, but could potentially contain a set of types intended solely for the use of the clients. Of course, you'll have to keep the two sets of types compatible (same name, order, type and namsspace for each data member).

John Saunders
+2  A: 

I think it is very important to note that messages/datacontracts have a very specific purpose in a service-oriented environment. A message is a packet of information that needs to be transferred between a client and a server (or vice versa.) If your client needs specific behavior, then you should have client-specific types that provide for the specific needs of the client. Those types should be populated by some kind of adapter or facade that wraps your service references, abstracting your client application from the service as much as possible, and providing the necessary behavior, rules, and restrictions as appropriate.

using WCF.ServiceClientReference; // Contains WCF service reference and related data contracts

class ServiceFacade
{
    private ServiceClient m_client;

    void SendMessage(ClientMessage message)
    {
        Message serviceMessage = new Message
        {
            Subject = message.Subject,
            MessageBodies = new CommMessageBodies(message.MessageBodies.Select(b => new CommMessageBody(b))
        }

        m_client.SendMessage(serviceMessage);
    }
}

class ClientMessage
{
    public ClientMessage()
    {
        MessageBodies = new List<ClientMessageBody>();
    }

    public string Subject {get; }
    public IList<ClientMessageBody> MessageBodies { get; private set; } 
}

// etc.
jrista
A: 

Lost my editing points/anon profile...but just wanted to say thanks to both of you for the clear answers. Got me moving on to the following solution:

  • ClientMessage on Server, creating a proxy of that on Client, with no direct dependency.
  • create Message on client that has properties that mirror names of poco/wcf ClientMessage, but with added arg checking, etc.
  • created Extension method to VisualStudio generated ClientMessage, with static Extension method MapFrom(this ClientMessage thisClientMessage, Message message){...} to map from Client facing message, to transport message object.
  • off it goes.

ClientMessage on server could have logic so that webpages would use that as the backing object. Costs a bit more to map those back and forth, even though on same server, but I gain from being able to cut/paste/use the same client logic for both scenarios (web and distance client). (Unless anybody sees an error with this logic as well :-) ...)

Again, thank you.