How to properly update the client service reference?

Feb 5, 2013 at 3:10 PM
Ok, this is driving me nuts. No matter what every time I add a new command or query, then try to perform it on the client, I get this error:

There was an error while trying to serialize parameter http://www.cuttingedge.it/solid/queryser.. The InnerException message was 'Type 'Contract.Queries.Orders.GetExpensiveOrdersQuery' with data contract name 'GetExpensiveOrdersQuery:http://schemas.datacontract.org/2004/07/Contract.Queries.Orders' is not expected. Consider using a DataContractResolver or 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.

It happens with the codeplex project, and with my adaptation of it. How do you explicitly make the client aware of the new command/query contract? I right click the service reference on the client and update service but that doesn't work. I rebuild the entire project and update the service and that doesn't work either. I remove the service reference and add it back and that works sometimes. I also have to be aware of stopping the local IISExpress.

My question is what is the proper way to update the client/service once a new command/query is added? Thanks.

PS: you can repeat this problem by downloading this sample, adding a new query or command, then trying to use it from the program.
Coordinator
Dec 8, 2014 at 10:12 AM
Hi,

I'm very sorry for this late reply. I just noticed that I didn't configure e-mail notification for this project, and I never noticed that questions where posted here.

Working like this with WCF is a PITA, I found this out myself. In my current projects, I serialize my commands and queries to JSON and have WCF just have one method that accepts the command or query type name and the JSON contents. Internally this information is than deserialized again. This prevents the WCF contract from having to be updated, which removes lots of the pain of working with WCF. I now only use WCF for its transport capability; not for its contract capability.
Coordinator
Dec 18, 2015 at 2:10 PM
I found a way to prevent the client from having to constantly update the service references by making use of a custom DataContractResolver. This can be seen in this commit. The custom DataContractResolver tries to resolve types from the assembly that contains that commands and query objects.
Mar 3, 2016 at 1:29 PM
Hi Steven,

I really like the custom DataContractResolver in your last post - updating the service reference was always a bit of a pain, so removing that process has streamlined things even more. However, I'm a bit confused by something which you seem to have come across too in this commit. Generic types get seemingly random strings appended to their typeName and I can't figure out why! I asked a StackOverflow question but haven't had any responses. My workaround is a bit hacky and I'd like to make it less so. Any ideas?

Cheers,
Alex
Coordinator
Mar 12, 2016 at 2:04 PM
Hi Alex,

The weird string postfixing thingie of WCF is something that annoyed and worried me as well. The string generation seems random and unpredictable, and I couldn't find any resource about this behavior on the internet. I even dived in the source code of WCF using Reflector, but wasn't able to find the related code for this.

So I'm dazzled as well and I am glad you raised this question on stackoverflow.

Steven
Coordinator
Mar 12, 2016 at 2:11 PM
Btw, I am not pleases with the given answer, because this forces WCF attributes on all our DTO classes. This is labor intensive, ugly and error prone. And also note that these attrbutes are not available on every platform, while we might want to place these types in a PCL library.
Coordinator
Apr 25, 2016 at 9:15 AM
Edited Apr 25, 2016 at 9:17 AM
Hi @alexfoxgrill,

Did you ever get this working using the DataContractAttribute? I can't get this to work, so I'm very interested what you had to do to make this working. In my case adding the DataContractAttribute causes a type to be serialized incorrectly. For instance, I added the attribute to the Contract.Queries.Paged<T> class as follows:
[DataContract(Name = "PagedOf{0}")]
public class Paged<T>
{
    public PageInfo Paging { get; set; }
    public T[] Items { get; set; }
}
The effect of this however on the WSDL is that the WSDL changed from:
<xs:complexType name="PagedOfOrderInfomX0E65e4">
    <xs:annotation>
        <xs:appinfo>
            <GenericType xmlns="http://schemas.microsoft.com/2003/10/Serialization/" 
                Namespace="http://schemas.datacontract.org/2004/07/Contract.Queries" 
                Name="PagedOf{0}{#}">
                <GenericParameter 
                    Namespace="http://schemas.datacontract.org/2004/07/Contract.DTOs" 
                    Name="OrderInfo"/>
            </GenericType>
        </xs:appinfo>
    </xs:annotation>
    <xs:sequence>
        <xs:element type="q1:ArrayOfOrderInfo" name="Items" nillable="true" minOccurs="0" 
            xmlns:q1="http://schemas.datacontract.org/2004/07/Contract.DTOs"/>
        <xs:element type="tns:PageInfo" name="Paging" nillable="true" minOccurs="0"/>
    </xs:sequence>
</xs:complexType>
to:
<xs:complexType name="PagedOfOrderInfo">
    <xs:annotation>
        <xs:appinfo>
            <GenericType xmlns="http://schemas.microsoft.com/2003/10/Serialization/" 
                Namespace="http://schemas.datacontract.org/2004/07/Contract.Queries"
                Name="PagedOf{0}">
                <GenericParameter 
                    Namespace="http://schemas.datacontract.org/2004/07/Contract.DTOs" 
                    Name="OrderInfo"/>
            </GenericType>
        </xs:appinfo>
    </xs:annotation>
    <xs:sequence/>
</xs:complexType>
Notice here how the <xs:sequence> tag has become completely empty. This happens just by adding the [DataContract(Name = "PagedOf{0}")] to the Paged<T> class. No other changes were made.

The omision of the two elements of the sequence element obviously cause the data not to be deserialized by the client (or not serialized by the server, I haven't checked). This causes both the Items and PageInfo properties to become null, which of course isn't very useful.

I'm interested to hear if you experienced this problem and how you solved this.
Apr 25, 2016 at 11:56 AM
Sadly I did not have any success with the DataContract attribute, I had the same problem as you in that data was not correctly serialized. I had no time to investigate further so I stuck with trimming the final X characters from the end of the string and checking those. Sorry I can't be of more help
Coordinator
Apr 25, 2016 at 12:35 PM
Edited Apr 25, 2016 at 8:38 PM
I got it. In case you mark a class with the [DataContract] attribute, you will have to explicitly mark the properties with [DataMember] for them to be serialized. So I change the Page<T> to the following:
[DataContract(Name = nameof(Paged<T>) + "Of{0}")]
public class Paged<T>
{
    [DataMember] public PageInfo Paging { get; set; }
    [DataMember] public T[] Items { get; set; }
}
Ands now it works.

Unfortunately it forces a types to be marked with System.Runtime.Serialization attributes. Fortunately it seems that most most target platforms support these attributes (such as ASP.NET Core, Windows Phone 8 and Windows Phone Silverlight 8). That makes this a workable solution in most cases.

After some digging around, I found out that that weird postfix value is a base64 encoded hash of all the namespaces of the generic type arguments in the generic type. This postfix value is highly unstable. A different closed version of the same generic type will easily cause a completely different hash and because of this, trying to check for the existence of the postfix based on a fixed string is very unlikely to work; it is a really fragile solution. This basically means that using the [DataContract] attribute is actually the only feasible solution.