Hiding base class property using new keyword in WCF - c#

I have a Scneario like this in my Server DLL class library.
[DataContract]
public class Base
{
[DataMember]
public string Info { get; set; }
}
[DataContract]
public class Child : Base
{
[DataMember]
public new int Info { get; set; }
public int Save()
{
}
}
My WCF Proxy at client side creates a Reference class. It Renames "Info" to "Info1". And shows proper properties in Base class.My code compiles great. So far so good. When I try to run ChildProxy.Save() from my client it gives me error Saying
"There was an error while trying to serialize parameter http://tempuri.org/:info. The InnerException message was 'Type 'ClientServiceLayer.InfoService.Info' with data contract name 'ArrayOfInfo:http://schemas.datacontract.org/2004/07/Info_DLL' is not expected. Add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.'. Please see InnerException for more details.
"
How to hide the property of base class in WCF?
UPDATE:
Here is the call on the client-side
InvoiceServiceClient infoProxy = new InfoServiceClient();
invId = invfoProxy.Save();

As stated here:
You can't. Although the Child class is "hiding" the Info property of its base class, the attribute is being read by the serializer.
You could try adding [DataMember(Name = "Info")] to the child class and see what happens.

You could use KnownType Attribute in your DataContract
MSDN Data Contract Known Types

Related

NetDataContractSerializer Deserialization With New Property

Lacking any real foresight, I've serialized a large set of data decorated only with Serializable using NetDataContractSerializer, and now I'd like to add a new field. What are my options?
The original class looks something like this (with a few levels of inheritance and quite a few fields):
[Serializable]
public class InheritedClass : BaseClass
{
public string StringId { get; set; }
}
And now I'd like to add another property, say something like:
[Serializable]
public class InheritedClass : BaseClass
{
public string StringId { get; set; }
public int IntId { get; set; }
}
Now when I update the class and go to deserialize, I receive an exception since the new field is not present, something like:
Exception thrown: 'System.Runtime.Serialization.SerializationException' in System.Runtime.Serialization.dll
Additional information: Error in line 1 position 601. 'Element' '_x003C_StringId_x003E_k__BackingField' from namespace 'http://schemas.datacontract.org/2004/07/QT' is not expected. Expecting element '_x003C_IntId_x003E_k__BackingField'.
Ok, so this makes sense since NetDataContractSerializer requires the same class. I can get around that using a DataMember attribute like:
[DataMember(IsRequired = false)]
The problem then is that switching to DataMember (as I should have done upfront, or used a different serializer) changes the implicit alphabetical ordering, and then most of my fields will silently not deserialize as is well known.
I've attempted to add an ordering that's inline with the ordering on disk manually (via Order properties on the attribute), but that doesn't appear to be respected either. (I don't see an order value I could match in the raw xml either.)
Are there any other options beyond writing something to load the xml and insert the missing node? (Or equivalently setup a parallel type and deserialize from one an re-serialize to another?) If not, I'll probably just load up with the current type and deserialize to JsonNet or protobuf, but am I missing anything more straightforward with DataMember/etc?
Marking a type with [Serializable] means that the type can be serialized by serializing its public and private fields -- not its properties. NetDataContractSerializer respects this attribute when present, serializing the fields as indicated. For an auto-implemented property the secret backing field is what is actually serialized.
When adding a new field, what one generally does to handle legacy data is to mark it with [OptionalField] to indicate that it won't always be present in serialization streams. In c# 7.3 and later, it's possible to do this to the secret backing field of an auto-implemented property by using a field-targeted attribute:
[Serializable]
public class InheritedClass : BaseClass
{
public string StringId { get; set; }
[field: OptionalField]
public int IntId { get; set; }
}
Prior to c# 7.3 there is no way to apply an attribute to the backing field of an auto-implemented property. Thus you need to make the backing field be explicit and add the attribute to it:
[Serializable]
public class InheritedClass : BaseClass
{
public string StringId { get; set; }
[OptionalField]
int intId;
public int IntId { get { return intId; } set { intId = value; } }
}
Notes:
As noted in the question, if a type is marked with data contract attributes then NetDataContractSerializer will use those in preference to the default [Serializable] contract and allow you to explicitly indicate properties to serialize (and provide names clearer than the secret backing field names).
Unfortunately it is not always practical to add data contract attributes to legacy types.
NetDataContractSerializer has not been ported to .NET Core / .NET 5 and likely never will be.

null value return from WCF service operation

I have a WCF service with an operation contract that returns null values. That is: it's supposed to return some custom class 'ProgressReport', but in the client application it shows up as null. In the WCF service itself the custom class is instantiated and returned without errors and without becoming null at any time.
The 'ProgressReport' class is not defined as DataContract because it is defined in another assembly.
For the operation that returns 'ProgressReport', the WcfTestClient informs me that it cannot perform operation because it uses type 'ProgressReport'.
I've tried adding an operationcontract with System.Object as return type, but it gives me the same error (cannot perform operation because it uses type 'System.Object')
Does anyone know how to solve this?
You could try to extend the ProgressReport-class (if it's not sealed) and define that as a DataContract (but I think that does not help with the properties of the class as they needed to be marked as DataMembers) or create a own class as a kind of a proxy and return this object:
[DataContract]
public class ProgressReportWcf : ProgressReport
{
}
or
[DataContract]
public class ProgressReportWcf
{
[DataMember]
public int Id { get; set; }
//aso...
public ProgressReportWcf(ProgressReport report)
{
Id = report.Id;
//aso...
}
}

Serialization system.object

I'm trying to create a webservice to return a generic. The return class looks like this:
[Serializable]
public class RenderReturn
{
public RenderReturnStatus StatusReturn { get; set; }
public string MessageReturn { get; set; }
public string MessageTitle { get; set; }
public object **ObjectReturn** { get; set; }
}
Where ObjectReturn can be an object or a list of application objects, like cars, customers, etc..
But the webservice returns the following error:
System.InvalidOperationException: There was an error generating the XML document. --->
System.InvalidOperationException: The type Environment was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
at System.Xml.Serialization.XmlSerializationWriter.WriteTypedPrimitive(String name, String ns, Object o, Boolean xsiType)
This is possible or method should always return specific types?
Explicitly name your types. Otherwise one can put something in that isn't serializable.
Try something along these lines:
public class RenderReturn<T>
{
public T ObjectReturn {get;set;}
}
That way, at run-time, you'll have a concrete type rather than just System.Object.
Type of object is too generic for .NET to know what kind of object it is dealing with and how to deserialise. So it asks you to give it some hint by using XmlInclude attributes telling .NET the types to expect. In WCF you do the same: you use KnownType attribute to decorate properties.
Type object is not a good candidate for DTO objects that need to cross process boundaries.
In WCF or Web Services, try not to think in object-oriented fashion but think in WSDL. As far as WSDL is concerned you have a contract which explicitly defines the type of messages passed between client and server.

WCF issues with KnownType for Dictionary

I have a service that implements the following DataMember:
[DataMember]
public Dictionary<string, List<IOptionQueryResult>> QueryResultItems { get; set; }
I have the class "OptionQuerySingleResult" which inherits from IOptionQueryResult. Now, I understand that I need to make the OptionQueryResult type "known" to the Service and thus tried to add the KnownType in various ways:
[KnownType(typeof(Dictionary<string, OptionQuerySingleResult[]>))]
[KnownType(typeof(Dictionary<string, List<OptionQuerySingleResult>>))]
[KnownType(typeof(OptionQuerySingleResult)]
However, none of those approaches worked and on the client side I'm either getting that deserialization failed or the server simply aborted the request, causing a connection aborted error.
Does anyone have an idea on what's the proper way to get this to work?
I'd like to add that if I change the QueryResultItems definition to use the concrete type, instead of the interface, everything works just fine.
Thanks,
Tom
Edit:
The exception that I am getting is:
Error in line 1 position 524. Element 'http://schemas.microsoft.com/2003/10/Serialization/Arrays:anyType' contains data from a type that maps to the name ':OptionQuerySingleResult'. The deserializer has no knowledge of any type that maps to this name. Consider using a DataContractResolver or add the type corresponding to 'OptionQuerySingleResult' to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer.
However, when I look at the client proxy that svcutil generates, "OptionQuerySingleResult" is definitely defined in it:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="OptionQuerySingleResult", Namespace="")]
[System.SerializableAttribute()]
public partial class OptionQuerySingleResult : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged
I think you would use:
[KnownType(typeof(OptionQuerySingleResult)]
But you also need a [DataContract(Name = "OptionQuerySingleResult")] on your OptionQuerySingleResult class.
I think this also relies on your client proxy classes being generated by the SVCUTIL.EXE util.
You simply need to add the following property to your datacontract class.
[DataMember]
public object UsedForKnownTypeSerializationObject;
So now the generated proxy contains the Knowtypes you set on the datacontract.
I had the same problem and this is the only solution I came up with.
If you don't at the a property of type Object to you DataContract class,
the generated proxy doesn't contain the declared knowtypes
For example:
[DataContract]
[KnownType(typeof(List<String>))]
public class Foo
{
[DataMember]
public String FooName { get; set; }
[DataMember]
public IDictionary<String, Object> Inputs { get; set; }
[DataMember]
private Object UsedForKnownTypeSerializationObject{ get; set; }
}
It's not as pretty because you end up with a dummy property which doesn't have any
functional implementation. But then again I don't have another solution.

Force WCF to serialize unused class

I appear to be having an issue with the following snippet of code in that, when I come to specifying what the Item is (eg CashInHand), the actual type CashInHandPayment is not available because it hasn't been carried across when I generate the proxy class (most likely because it doesn't read in XmlElementAttributes).
Is there any way to force classes such as AccountPayment, CashInHandPayment and CCPayment to be serialized in the proxy class?
[DataContract]
public class Payment
{
[XmlElementAttribute("Account", typeof(AccountPayment))]
[XmlElementAttribute("CashInHand", typeof(CashInHandPayment))]
[XmlElementAttribute("CreditCard", typeof(CCPayment))]
[XmlChoiceIdentifierAttribute("ItemElementName")]
[DataMember]
public object Item { get; set; }
}
[DataContract]
public enum ItemElementName
{
[EnumMember]
Account,
[EnumMember]
CashInHand,
[EnumMember]
CreditCard
}
//This class will not be in the generated proxy class
[DataContract]
public class AccountPayment
{
[DataMember]
public double Amount { get; set; }
}
//classes for CashInHandPayment and CCPayment also created, but not shown.
Forgive me if 'serialize' isn't the correct term to use, if you read the question and find that it isn't, please change it accordingly!
Update - answer mentioned by Simon Svensson:
[KnownType(typeof(AccountPayment))]
[KnownType(typeof(CashInHandPayment))]
[KnownType(typeof(CCPayment))]
[DataContract]
public class Payment
{
[XmlElementAttribute("Account", typeof(AccountPayment))]
[XmlElementAttribute("CashInHand", typeof(CashInHandPayment))]
[XmlElementAttribute("CreditCard", typeof(CCPayment))]
[XmlChoiceIdentifierAttribute("ItemElementName")]
[DataMember]
public object Item { get; set; }
}
Many thanks, Simon!
Uhm. Isnt XmlElementAttribute and XmlChoiceIdentifierAttribute xml serialization, which is a older serialization compared to the DataContractSerializer which reads DataContractAttribute and DataMemberAttribute?
I believe that you should use the KnownTypeAttribute for this, but I have never tried it, nor have I had this scenario in my own code.
Is there any way to force classes such as AccountPayment,
CashInHandPayment and CCPayment to be serialized in the proxy class?
They need to be marked with a [DataContract] attribute, that should be sufficient, I would think.
When svcutil.exe (either launched directly from the command line, or from Visual Studio using Add Service Reference) encounters classes with the [DataContract] attribute on the class and [DataMember] on the properties (or fields), it will create a copy in the proxy for those classes.
Marc
I think specifying DataContract should be sufficient. But, if that isn't working, why not try creating a dummy OperationContract method that uses the class?

Categories

Resources