views:

473

answers:

3

Background

Converting from using .Net Remoting to WCF. Most of the methods on the WCF server are working fine, but ran into one that isn't working today.

This is the service contract:

[ServiceContract]
public interface IMyService
{
  [OperationContract]
  generated.Response.ACS_Response Check(generated.Request.ACS_Request request);
}

I snipped out the rest of the methods on that interface, since they are working. Basically, I'm trying to pass in a request object and get back a response object.

The classes for ACS_Response and ACS_Reqeust are generated using XSD.exe against an XSD file. Those classes reside in an Api assembly that is referenced by both the WCF client and WCF host.

Problem

I am able to make the call to the WCF host and the Request object and the host is able to do its work. When the host attempts to return the Response object, that's where I encounter an exception.

I turned on tracing for WCF and see an SerializationException saying:

Type 'Api.generated.Response.ACS_ResponseQuestion'
with data contract name 'ACS_ResponseQuestion:http://...' is
not expected.  Add any types not known statically.........

Questions

First, I'm confused because I am able to successfully send a Request object, so it would seem the basics are working.

Second, this serialization was working under .Net Remoting. The classes are all generated by WSDL so shouldn't they be serializable as is?

Third, the host and client both reference the same Api assembly which defines these classes, so they are known to both server and client.

A: 

You are referencing the classes from both the client and the server, I would therefore recommend that you look at this way of doing WCF:

http://www.dnrtv.com/default.aspx?showNum=122

Shiraz Bhaiji
I am glad to say that I am following the "GoodService" model with separate assemblies for Contracts, Host and Implementation. I think where I'm running into problems is that I'm passing more complex objects than just "string".Most of the complex objects are being passed just fine. All of the objects that are passed are defined in my Api assembly. There are generated ones as well as others. So far, I'm only having problems with the ACS_Response class (and its child properties, such as ACS_ReponseQuestion).Thanks for the link, but I think I'm doing everything it is recommending.
quip
+1  A: 

I believe it is because Api.generated.Response.ACS_ResponseQuestion is not used in the contract (API) so it is not automatically tagged as a known type.

Read these articles, they should explain everything:

  1. Known Types
  2. Data Contract Known Types
  3. Understanding Known Types


Quick solution: Try adding this to the class which implements the interface:

[KnownType(typeof(Api.generated.Response.ACS_ResponseQuestion))]

If this doesn't work, you may have to declare it as a ServiceKnownType:

// Define a service contract and apply the ServiceKnownTypeAttribute
// to specify types to include when generating client code. 
// The types must have the DataContractAttribute and DataMemberAttribute
// applied to be serialized and deserialized. The attribute specifies the 
// name of a method (GetKnownTypes) in a class (Helper) defined below.
[ServiceKnownType("GetKnownTypes", typeof(Helper))]
[ServiceContract()]
public interface ICatalog
{
    // Any object type can be inserted into a Hashtable. The 
    // ServiceKnownTypeAttribute allows you to include those types
    // with the client code.
    [OperationContract]
    Hashtable GetItems();
}

// This class has the method named GetKnownTypes that returns a generic IEnumerable.
static class Helper
{
    public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
    {
        System.Collections.Generic.List<System.Type> knownTypes =
            new System.Collections.Generic.List<System.Type>();
        // Add any types to include here.
        knownTypes.Add(typeof(Widget));
        knownTypes.Add(typeof(Machine));
        return knownTypes;
    }
}

[DataContract()]
public class Widget
{
    [DataMember]
    public string Id;
    [DataMember]
    public string Catalog;
}

[DataContract()]
public class Machine : Widget
{
    [DataMember]
    public string Maker;
}
Philip Wallace
Adding KnowType attributes to the implementing class did not help, but adding ServiceKnownType attributes to the service contract did fix the problem. --- I'm still confused as to why it was a problem, but at least it is working! THANKS!!!!!!!
quip
Basically, WCF figures out all the objects it is going to have to serialize by looking at the ServiceContract. In your case, ACS_ResponseQuestion is not in the interface as a parameter or return type which is why you have to declare it as a known type so that the serializer knows what to do with it.
Philip Wallace
Ohhhhh haha well that kinda makes sense.
quip
+1  A: 

Take a look at this ServiceKnownType provider It should save you some grief. Its very simple for you to register a base type and it will scan the assembly for all classes that inherit from the base class.

Aaron Fischer