Question

I'm having a problem getting the helper method specified in my ServiceKnownType attribute to run. For simplicity's sake, I have two assemblies: one with my service interface and interfaces for my data contracts, and another with my service implementation and concrete data contracts.

Here's a simplified/stripped down version of my service and its implementation.

MyService.Interface.dll

// IMyService.cs
[ServiceContract]
IMyService
{
    [OperationContract]
    IList<IMyData> GetData();
}

// IMyData.cs
public interface IMyData
{
    int Foo { get; set; }
}

MyService.dll (with a reference to MyService.Interface.dll)

// MyService.svc.cs
public class MyService: IMyService
{
    public IList<IMyData> GetData()
    {
        // Instantiate List<IMyData>, add a new MyData to the list
        // return the List<IMyData>.
    }
}

// MyData.cs
[DataContract]
public class MyData : IMyData
{
    [DataMember]
    public int Foo { get; set; }
}

The root of the problem is that to serialize the result of GetData(), the service must be informed about the concrete MyData class and the concrete List<IMyData> generic class because the service definition uses interface types not the concrete types.

The simple answer is to decorate IMyService with:

[ServiceKnownType(typeof(MyData))]
[ServiceKnownType(typeof(List<IMyData>))]

However, MyData is defined in an assembly that is not referenced within MyService.Interface.dll (and cannot be due to a circular reference.)

My next thought was to use the "helper method" form of ServiceKnownType on MyService itself:

[ServiceKnownType("GetDataContracts", MyService)]
public class MyService: IMyService
{
    public static IEnumerable<Type> GetDataContracts(ICustomeAttributeProvider provider)
    {
        // create a List<Type>, add MyData to it, and return it.
    }
    //...
}

As far as I can see that ought to work except that GetDataContracts is never called. I tried moving it into a separate static class (parallel to MyService and nested within it) but in no case can I get a breakpoint to stop there.

EDIT: I meant to also say that adding the known types through web.config doesn't work either because I have generic types can't be added that way. You can only add simple, concrete types via web.config:

<knownType type="TypeName, Assembly" />

My concrete List<IMyData> doesn't have a fully-qualified type name in an assembly.

No correct solution

OTHER TIPS

Fixed. The answer was to add the ServiceKnownType, using the helper method form, to the service interface, not the service implementation, and to add a helper class that reflects the types I need instead of adding them by referring to the concrete types in code. (Recall, I couldn't do that because I can't add a reference to that assembly.)

[ServiceContract]
[ServiceKnownType("GetDataContractTypes", typeof(MyServiceHelper))]
public interface IMyService
{ ... }

And I added a new MyServiceHelper class to Nightingale.Interface, but it's not public so I'm not concerned about unnecessarily exposing a class from an assembly that I want to be "pure" interface only.

// Not public outside of this assembly.
static class MyServiceHelper
{
    public static IEnumerable<Type> GetDataContractTypes(ICustomAttributeProvider paramIgnored)
    {
        // Get the assembly with the concrete DataContracts.
        Assembly ass = Assembly.Load("MyService");  // MyService.dll
        // Get all of the types in the MyService assembly.
        var assTypes = ass.GetTypes();
        // Create a blank list of Types.
        IList<Type> types = new List<Type>();
        // Get the base class for all MyService data contracts
        Type myDataContractBase = ass.GetType("MyDataContractBase", true, true);
        // Add MyService's data contract Types.
        types.Add(assTypes.Where(t => t.IsSubclassOf(myDataContractBase)));
        // Add all the MyService data contracts to the service's known types so they can be serialized.
        return types;
    }
}

This particular solution works for me because all of my DataContract classes extend a common base class. It could be reworked to load all Types from the assembly that have the DataContract attribute which would result in the same set, in my case.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top