How can I return interface types in generic type parameters from the OperationContracts
in my WCF REST service? More specifically, it works for one operation but not when I add a second operation with a generic T
that is an interface.
I'm using JSON as the request and response format, feeding a non-WCF client that parses the JSON responses for the data it needs. I am not using SOAP, or the WSDL generated by the service.
My service interface:
[ServiceContract]
[ServiceKnownType("GetServiceKnownTypes", typeof(ServiceKnownTypesHelper))]
public interface IMyService
{
[WebGet(UriTemplate="count")]
[OperationContract]
IServiceResult<int> GetCount();
[WebGet(UriTemplate="desc")]
[OperationContract]
IServiceResult<string> GetDescription();
[WebGet(UriTemplate="foo")]
[OperationContract]
IServiceResult<IFooData> GetFooData();
// Fails when I invoke either method if I uncomment this operation.
//[WebGet(UriTemplate="bar")]
//[OperationContract]
//IServiceResult<IBarData> GetBarData();
}
I left GetCount()
and GetDescription()
in the example to point out that those two generic results work fine, but obviously they are concrete types. And even GetFooData()
works fine until I add a second method of IServiceResult<T>
where T
is an interface.
The return types of GetFooData()
and GetBarData()
are not the same, nor are the concrete classes that implement them.
You may imagine that I've reduced the implementation to a skeleton as I don't think the implementation is the heart of the problem:
#region My service implementation
public class MyService : IMyService
{
public IServiceResult<int> GetCount()
{
return new ServiceResult<int>(42);
}
public IServiceResult<string> GetDescription()
{
return new ServiceResult<string>("Muffins");
}
public IServiceResult<IFooData> GetFooData()
{
return new ServiceResult<IFooData>(new FooData() { Foo = 99 });
}
public IServiceResult<IBarData> GetBarData()
{
return new ServiceResult<IBarData>(new BarData() { Bar = "Elvis was here" });
}
}
#endregion
#region ServiceKnownTypesHelper.GetServiceKnownTypes():
public static class ServiceKnownTypesHelper
{
private static IList<Type> serviceKnownTypes = new List<Type>()
{
typeof(FooData),
typeof(BarData),
typeof(ServiceResult<int>),
typeof(ServiceResult<string>),
typeof(ServiceResult<IFooData>),
typeof(ServiceResult<IBarData>),
};
public static IEnumerable<Type> GetServiceKnownTypes(ICustomAttributeProvider paramIgnored)
{
return serviceKnownTypes;
}
}
#endregion
#region IServiceResult<T> and its concrete implementation:
public interface IServiceResult<T>
{
IList<string> Errors { get; }
T Value { get; set; }
}
[DataContract]
public class ServiceResult<T> : IServiceResult<T>
{
public ServiceResult(T value)
{
this.Value = value;
}
private IList<string> errors = new List<string>();
[DataMember]
public IList<string> Errors
{
get
{
return this.errors;
}
}
[DataMember]
public T Value { get; set; }
}
#endregion
#region IFooData and its concrete implementation:
public interface IFooData
{
int Foo { get; set; }
}
[DataContract]
public class FooData: IFooData
{
[DataMember]
public int Foo { get; set; }
}
#endregion
#region IBarData and its concrete implementation:
public interface IBarData
{
string Bar { get; set; }
}
[DataContract]
public class BarData: IBarData
{
[DataMember]
public string Bar { get; set; }
}
#endregion
And the error message when I invoke GetBarData()
from the browser:
Type 'ServiceResult`1[IBarData]' cannot be added to list of known types since another type 'ServiceResult`1[IFooData]' with the same data contract name 'http://schemas.datacontract.org/2004/07/ServiceResultOfanyType' is already present.
The rest of the error message is a red herring about colliding collection types List<Test>
and Test[]
, which isn't the case here.
Clearly, IFooData
and IBarData
aren't the same, nor are the classes that implement them.
So why do ServiceResult<IFooData>
and ServiceResult<IBarData>
both resolve to ServiceResultOfanyType
?
Am I missing something, or is there no way to fix this?
After much trial and error, I've finally got this working with minimal changes:
ServiceResult<T>
instead of IServiceResult<T>
. In fact, IServiceResult<T>
is now gone entirely.GetServiceKnownTypes()
no longer returns all the variants of ServiceResult<T>
. I only return the DataContract
s used as T
.
#region My service interface
[ServiceContract]
[ServiceKnownType("GetServiceKnownTypes", typeof(ServiceKnownTypesHelper))]
public interface IMyService
{
[WebGet(UriTemplate="count")]
[OperationContract]
ServiceResult<int> GetCount();
[WebGet(UriTemplate="desc")]
[OperationContract]
ServiceResult<string> GetDescription();
[WebGet(UriTemplate="foo")]
[OperationContract]
ServiceResult<IFooData> GetFooData();
[WebGet(UriTemplate="bar")]
[OperationContract]
ServiceResult<IBarData> GetBarData();
}
#endregion
#region My service implementation (minus bodies)
public class MyService : IMyService
{
public ServiceResult<int> GetCount() {}
public ServiceResult<string> GetDescription() {}
public ServiceResult<IFooData> GetFooData() {}
public ServiceResult<IBarData> GetBarData() {}
}
#endregion
#region ServiceKnownTypes
// My list of ServiceKnownTypes is now much shorter and simpler. I was feeding the service too much information
public static class ServiceKnownTypesHelper
{
private static IList<Type> serviceKnownTypes = new List<Type>()
{
typeof(FooData),
typeof(BarData),
//typeof(ServiceResult<int>),
//typeof(ServiceResult<string>),
//typeof(ServiceResult<IFooData>),
//typeof(ServiceResult<IBarData>),
};
// Remaining implementation is the same as before
}
#endregion
#region ServiceResult<T> with no interface (it's not used or needed)
[DataContract]
public class ServiceResult<T> //: IServiceResult<T>
{
// implementation is the same as before
}
#endregion
I can now invoke all of those methods and get back lists of generic types referred to by their interfaces in the ServiceContract
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With