Answered by:
How to hide DataMember property in child DataContract class ??

Question
-
[DataContract] public class Contact { [DataMember] public string Name { get; set; } [DataMember] public string Email { get; set; } [DataMember] public string Address { get; set; } } [DataContract] public class Contact2 : Contact { [IgnoreDataMember] // This doesnt work public string Address { get; set; } }
Hi, in derived class Contact2 I want to hide Address, so that the client connecting to my service will not see Adress property of Contact2. IgnoreDataMember attribute is not working (probably intended for something else).Any ideas? Thanks a lot.
Wednesday, July 13, 2011 1:10 PM
Answers
-
You really can't :). Even outside of WCF, if you have the Contact2 class "hiding" the address property of its base class, all you need is a reference of type Contact, and you'll be accessing the property of the base class. A Contact class has an Address. Contact2 is-a Contact. Therefore, Contact2 has an Address as well.
Now, if you control the client which will use your service, you can provide them with an equivalent class (customize the data contract / service contract name / namespace) so that the client's class doesn't have that property (see below). But if you don't control the client, then you're really out of luck.
public class Post_97575d79_2b6c_4cc6_8c8e_450aff5008ea { [DataContract(Name = "Contact", Namespace = "http://my.namespace")] public class Contact { [DataMember] public string Name { get; set; } [DataMember] public string Email { get; set; } [DataMember] public string Address { get; set; } } [DataContract(Name = "Contact2", Namespace = "http://my.namespace")] public class Contact2 : Contact { public new string Address { get; set; } public override string ToString() { return string.Format("Name={0},Email={1},base.Address={2},Address={3}", Name, Email, base.Address ?? "<<null>>", this.Address ?? "<<null>>"); } } [DataContract(Name = "Contact", Namespace = "http://my.namespace")] public class ClientContact { [DataMember] public string Name { get; set; } [DataMember] public string Email { get; set; } } [DataContract(Name = "Contact2", Namespace = "http://my.namespace")] public class ClientContact2 : Contact { public new string Address { get; set; } } [ServiceContract(Name = "ITest")] public interface ITest { [OperationContract] Contact2 Echo(Contact2 input); } [ServiceContract(Name = "ITest")] public interface ITestClient { [OperationContract] ClientContact2 Echo(ClientContact2 input); } public class Service : ITest { public Contact2 Echo(Contact2 input) { Console.WriteLine(input); return input; } } public static void Test() { string baseAddress = "http://" + Environment.MachineName + ":8000/Service"; ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress)); host.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), ""); host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true }); host.Open(); Console.WriteLine("Host opened"); var factory = new ChannelFactory<ITestClient>(new BasicHttpBinding(), new EndpointAddress(baseAddress)); var proxy = factory.CreateChannel(); ClientContact2 contact = new ClientContact2 { Address = "This won't be sent to the server", Email = "a@b.c", Name = "My name", }; proxy.Echo(contact); Console.Write("Press ENTER to close the host"); Console.ReadLine(); host.Close(); } }
Carlos Figueira- Proposed as answer by CarlosFigueiraMicrosoft employee Monday, July 18, 2011 8:37 PM
- Marked as answer by Yi-Lun Luo Wednesday, July 20, 2011 9:47 AM
Thursday, July 14, 2011 10:41 PM
All replies
-
You can't. The base class states that it has three members (Name, Email, Address), so any derived class "is-a" base class as well, and it will have the three members. Just like you can't declare a public property in a base class and then "reduce" its visibility to private/internal on a subclass, once you declare a member in a base type, it will be there for you.
Carlos FigueiraWednesday, July 13, 2011 3:00 PM -
Well, actually, I found how I can do this.
[DataContract] public class Contact2 : Contact { //[DataMember] public new string Address { get; set; } // Hiding Email property of parent class }
This way Address will not be accessible thru the WCF service to the client, since there is no [DataMember] attribute for Address in Contact2.Wednesday, July 13, 2011 3:46 PM -
Actually it is accessible, just not by your code (and WCF will run any code on the setter of that property when you're deserializing a value for the type Contact2) - if you run the code below, and generate a client (using svcutil or add service reference), you'll still see the Address property in the generated client. Just because you "hide" a member in a derived class doesn't mean that it's not there. A "new" member actually creates a, well, new member in the derived class, but it doesn't remove the original one. If you share the contract between the service and the client then yes, the users of the Contact2 class won't be able to set the Address property, but you should not count on an arbitrary client not sending it to your service.
public class Post_97575d79_2b6c_4cc6_8c8e_450aff5008ea { [DataContract] public class Contact { [DataMember] public string Name { get; set; } [DataMember] public string Email { get; set; } [DataMember] public string Address { get; set; } } [DataContract] public class Contact2 : Contact { public new string Address { get; set; } } [ServiceContract] public interface ITest { [OperationContract] Contact2 Echo(Contact2 input); } public class Service : ITest { public Contact2 Echo(Contact2 input) { return input; } } static Binding GetBinding() { BasicHttpBinding result = new BasicHttpBinding(); //Change binding settings here return result; } public static void Test() { string baseAddress = "http://" + Environment.MachineName + ":8000/Service"; ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress)); host.AddServiceEndpoint(typeof(ITest), GetBinding(), ""); host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true }); host.Open(); Console.WriteLine("Host opened"); Console.Write("Press ENTER to close the host"); Console.ReadLine(); host.Close(); } }
Carlos FigueiraWednesday, July 13, 2011 6:28 PM -
You right.
Strange why it was inaccessible when I tried.
Now it is accessible.
Can you suggest some technique to hide parent class's property in child class for wcf clients?
Thursday, July 14, 2011 3:23 PM -
You really can't :). Even outside of WCF, if you have the Contact2 class "hiding" the address property of its base class, all you need is a reference of type Contact, and you'll be accessing the property of the base class. A Contact class has an Address. Contact2 is-a Contact. Therefore, Contact2 has an Address as well.
Now, if you control the client which will use your service, you can provide them with an equivalent class (customize the data contract / service contract name / namespace) so that the client's class doesn't have that property (see below). But if you don't control the client, then you're really out of luck.
public class Post_97575d79_2b6c_4cc6_8c8e_450aff5008ea { [DataContract(Name = "Contact", Namespace = "http://my.namespace")] public class Contact { [DataMember] public string Name { get; set; } [DataMember] public string Email { get; set; } [DataMember] public string Address { get; set; } } [DataContract(Name = "Contact2", Namespace = "http://my.namespace")] public class Contact2 : Contact { public new string Address { get; set; } public override string ToString() { return string.Format("Name={0},Email={1},base.Address={2},Address={3}", Name, Email, base.Address ?? "<<null>>", this.Address ?? "<<null>>"); } } [DataContract(Name = "Contact", Namespace = "http://my.namespace")] public class ClientContact { [DataMember] public string Name { get; set; } [DataMember] public string Email { get; set; } } [DataContract(Name = "Contact2", Namespace = "http://my.namespace")] public class ClientContact2 : Contact { public new string Address { get; set; } } [ServiceContract(Name = "ITest")] public interface ITest { [OperationContract] Contact2 Echo(Contact2 input); } [ServiceContract(Name = "ITest")] public interface ITestClient { [OperationContract] ClientContact2 Echo(ClientContact2 input); } public class Service : ITest { public Contact2 Echo(Contact2 input) { Console.WriteLine(input); return input; } } public static void Test() { string baseAddress = "http://" + Environment.MachineName + ":8000/Service"; ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress)); host.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), ""); host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true }); host.Open(); Console.WriteLine("Host opened"); var factory = new ChannelFactory<ITestClient>(new BasicHttpBinding(), new EndpointAddress(baseAddress)); var proxy = factory.CreateChannel(); ClientContact2 contact = new ClientContact2 { Address = "This won't be sent to the server", Email = "a@b.c", Name = "My name", }; proxy.Echo(contact); Console.Write("Press ENTER to close the host"); Console.ReadLine(); host.Close(); } }
Carlos Figueira- Proposed as answer by CarlosFigueiraMicrosoft employee Monday, July 18, 2011 8:37 PM
- Marked as answer by Yi-Lun Luo Wednesday, July 20, 2011 9:47 AM
Thursday, July 14, 2011 10:41 PM