views:

84

answers:

3

I have a set of DataContracts that are serialzed through WCF.

Please note this is a very simplified example.

[DataContract]
public class MyData
{
    [DataMember]
    public List<int> MyList
    {
        get;
        set;
    }
}

I would like to use object oriented design so that the server and client aren't creating any unnecessary dependencies. For example, I would like to encapsulate a list so that the user can't directly modify it.

Ideally, I would like the class to look like this if it wasn't a DTO.

public class MyData
{
    private List<int> _list = new List<int>();

    public IEnumerable<int> MyList
    {
        get
        {
            return _list;
        }
    }

    public void AddItem( int value )
    {
        _list.Add( value );
    }
}

I am using the same C# assembly from both the service and the client. So I can add non-DataMember methods, but I'm not sure if that is a good approach. It doesn't smell quite right to me.

Does anybody have a clever way of treating DTO classes more like objects instead of simple serializable structures?

+1  A: 

How about having DTO versions of your logic class which are used solely for the purpose of message passing? That way, you can put all the methods and properties on your logic class as necessary without having to worry about what the user has access to when it's passed over the wire. There are many ways you can go about this, for instance:

you can implement some method on your logic class to return the DTO

public class Player
{
   // methods that do interesting things here
   ...

   public string Name { get; set; }

   public PlayerDTO ToTransport()
   {
      return new PlayerDTO { Name = Name, ... };
   }
}

[DataContract]
public class PlayerDTO
{
   [DataMember]
   public string Name { get; set; }

   ...
}

Or you can implement an explicit/implicit conversion

public class Player
{
   // methods that do interesting things here
   ...

   public string Name { get; set; }
}

[DataContract]
public class PlayerDTO
{
   [DataMember]
   public string Name { get; set; }

   ...

   public static explicit operator PlayerDTO(Player player)
   {
      return new PlayerDTO { Name = player.Name, ... };
   }
}

this lets you cast a Player object to PlayerDTO:

var player = new Player { Name = .... };
var dto = (PlayerDTO) player;
theburningmonk
I do like this approach. I thought about doing something similar to this, but didn't like having to convert back and forth between the object and DTO when setting/modifing the data on both the server and client side.
Jerod Houghtelling
Does the entire object have to be back and forth between the client and the server? So is your application (server side) expecting to receive some data from the client, do some processing on the data, then pass it back?
theburningmonk
I hadn't thought of that yet, I will have to explore that more tommorrow. EF is creating a majority of the DTO's for me. I might only have to change some repository methods to get this suggestion to work.
Jerod Houghtelling
A: 

I do not think having methods unadorned by attributes in your DataContract's class necessarily smells. You have your service-oriented concerns on one hand (the operation and data contracts) and your object-oriented concerns on the other. What the client does with the provided data is of no concern to the service. The object-oriented issue you describe really only exists for the client.

If a client obtained Ball data from your service and it wants to Draw() it to the screen, whether or not the Ball class has a Draw() method has nothing to do with the contract between service and client. It is a contract between the api your assembly provides and those that use it. So I say, why not have a method in the assembly that is not an operation/data contract?

insipid
+1  A: 

Personally, I do think having DataContract on objects which are for more than service operations is a bit of a smell, just as it would be for ORM column mappings. One somewhat limited way to make these DTOs more like true OO is to have your methods be extension methods of the DTO. You might need to do something creative if the OO version has state that needs to be captured between calls that is not inherent in the DTO object itself, though.

Rich