WCF - multiple service contracts using pretty same data contracts
Asked Answered
N

4

7

I have a new question for WCF gurus.

So, I have a class User which is close to the 'User' representation from the DB which I use for database operations. Now, I would like to have 2 different service contracts that use this class as data contract, but each in their own way... I mean,

public class DBLayer
{
    void InsertUsers(List<User> userList)
    {
        // both 'PropertyVisibleForService1' and 'PropertyVisibleForService2'
        // are used HERE to be inserted into their columns 
    }
}

[DataContract]
public class User
{
  [DataMember] public string PropertyVisibleOnlyForService1{...}
  [DataMember] public string PropertyVisibleOnlyForService2{...}
}

[ServiceContract]
public interface IService1  
{   
   List<User> GetUsers();  // user with 'PropertyVisibleOnlyForService1' inside
}

[ServiceContract]
public interface IService2  
{   
    List<User> GetUsers(); // user with 'PropertyVisibleOnlyForService2' inside 
}

So, the idea is that each service will get a different kind of user, subset of 'User'. Keeping in mind that I want to use the 'User' as is for DB operations, what would be my options to achieve this? Do I really need to create different data contracts or is there another smarter way?

Best would be to not only give me the solution, but also to explain me some best practices and alternatives.

Thank you in advance.

EDIT1: I added a dummy DBLayer class here for a better overview and why I think the inheritance may not be good in this case.

A solution would be of having another 'UserForService1' and 'UserForService2' as data contracts which would map at the end from/into an 'User' but I wanted some other points of view.

EDIT2: Very good article which helped me in this case: http://bloggingabout.net/blogs/vagif/archive/2009/03/29/iextensibledataobject-is-not-only-for-backward-compatibility.aspx

Numinous answered 6/5, 2011 at 14:55 Comment(6)
I'm not a WCF expert so suggesting via a comment: Sounds like you should have two classes deriving from User; one has the PropertyVisibleOblyForService1 property and the other has the other property.Sulfide
Thank you for your reply... So, what you recommended is to take out these properties and put into new derived classes. The thing is that I would like the 'User' class unchanged.Numinous
Yes, I think you understand my recommendation. But as I am no WCF expert and I don't know your project, requirements, etc., I don't know whether it is possible to do what you want leaving the User class unchanged. If you post more detail I could perhaps help, or maybe someone with more WCF experience will weigh in. Good luck either way!Sulfide
Thank you for jumping in, any comments are welcome. I know one solution would be to create 2 different data contracts and then to map the 'User' to appropriate data contract, but maybe is another better practice for this case. I don't really think the inheritance would be good in my case. Thank you.Numinous
Ok, so everybody suggested the same thing. Maybe I was not clear enough with my request. I don't want to take out the properties from 'User' and put into someother derived classes, since this 'User' (and its properties are used for DB operations AS IS). So, 'PropertyVisibleOnlyForService1' and 'PropertyVisibleOnlyForService2' are both columns in the user table.Numinous
... or only if I maybe hide these properties in the derived classes?Numinous
M
4

You could create separate DTO's for each service but your case would actually be ideal for a Decorator pattern:

[DataContract]
public class UserForService1 : User
{
     private User mUser;
     public UserForService1(User u)
     {
         mUser = u;
     }

     //expose only properties you'd like the user of this data contract to see
     [DataMember]
     public string SomeProperty
     {
         get
         {
            //always call into the 'wrapped' object
            return mUser.SomeProperty;
         }
         set
         {
            mUser.SomeProperty = value;
         }
     }
     // etc...
}

and for Service2 similar code, that exposes only what you care for there...

Microdot answered 9/5, 2011 at 11:8 Comment(1)
This was kind of answer that I was looking for. Very good one! Cheers!Numinous
N
1

If they are designed to represent different types of users, they should be different classes. I agree with phoog in the comments, you should derive the type you want from the shared User class and add the specific service properties to the derived classes.

Why don't you think inheritance would be good in this case? If you give us some more details, we could try to revise the suggestions to suit your actual problem.

Ninetta answered 6/5, 2011 at 15:59 Comment(0)
K
1

As suggested in the comment, you can have two classes deriving from a base User then using Data Contract Known Types, you can accomplish your desired goal. See the following links for more examples.

http://www.freddes.se/2010/05/19/wcf-knowntype-attribute-example/

http://footheory.com/blogs/bennie/archive/2007/07/28/handling-data-contract-object-hierarchies-in-wcf.aspx

Kurus answered 6/5, 2011 at 16:25 Comment(1)
Given the fact that 'PropertyVisibleOnlyForService1' and 'PropertyVisibleOnlyForService2' are used by 'User' for db operations since are columns in the db table... which implies that I cannot take out these properties from 'User' to put them into derived classes unless I hide them somehow? Maybe I should give it a try.Numinous
S
1

If you don't want to use inheritance, something like:

[DataContract]
public class User
{
}

[DataContract]
public class Service1User : User
{
  [DataMember] public string PropertyVisibleOnlyForService1{...}
}

[DataContract]
public class Service2User : User
{
  [DataMember] public string PropertyVisibleOnlyForService2{...}
}

[ServiceContract]
public interface IService1  
{   
   List<Service1User> GetUsers();  // user with 'PropertyVisibleOnlyForService1' inside
}

[ServiceContract]
public interface IService2  
{   
    List<Service2User> GetUsers(); // user with 'PropertyVisibleOnlyForService2' inside 
}

Then I'm not sure what you would do. Your sortof breaking the principals of an type declaration at that point. Think of it in a normal .NET way; if you define "User" in your application, then it is the same type everywhere. Some properties cant be hidden from certain other classes or methods.

WCF is also going to pack this type information into the generated WSDL, and it is only going to define the User type once, so it needs to know what properties are there.

Now, if all you care about is the actual SOAP message that is constructed, and you don't care about the WSDL or what any clients generated off the WSDL will see, then technically you can have it not emit that property into the SOAP message when it is null, by doing:

    [DataMember(EmitDefaultValue=false)]

Then when that property is null, it wont be included in the serialization. That would make no real difference if the client was generated from the WSDL though, as its User type would still have to contain both properties. It would just change the serialization so that instead of sending the client something like:

<User>
  <PropertyVisibleOnlyForService1 nil="true" />
  <PropertyVisibleOnlyForService2>something</PropertyVisibleOnlyForService2>
</User>

it would instead send:

<User>
  <PropertyVisibleOnlyForService2>something</PropertyVisibleOnlyForService2>
</User>
Sylvestersylvia answered 6/5, 2011 at 16:39 Comment(1)
Thank you for your point of view and for how to use the 'Emit' stuff. I think is good to have it in mind maybe i could use it in the future.Numinous

© 2022 - 2024 — McMap. All rights reserved.