【发布时间】:2013-03-28 00:32:49
【问题描述】:
如何在我的 WCF REST 服务中从 OperationContracts 返回泛型类型参数中的接口类型?更具体地说,它适用于一个操作,但当我使用作为接口的通用 T 添加第二个操作时,它不起作用。
我使用 JSON 作为请求和响应格式,提供一个非 WCF 客户端,该客户端解析 JSON 响应以获取所需的数据。我没有使用 SOAP,也没有使用服务生成的 WSDL。
我的服务接口:
[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();
}
我在示例中留下了GetCount() 和GetDescription(),以指出这两个通用结果工作正常,但显然它们是具体类型。在我添加IServiceResult<T> 的第二种方法之前,即使GetFooData() 也可以正常工作,其中T 是一个接口。
GetFooData()和GetBarData()的返回类型不一样,实现它们的具体类也不一样。
您可能会认为我已将实现简化为骨架,因为我认为实现不是问题的核心:
#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
当我从浏览器调用GetBarData() 时的错误消息:
错误消息的其余部分是关于碰撞集合类型List<Test> 和Test[] 的红鲱鱼,这里不是这种情况。
显然,IFooData 和 IBarData 并不相同,实现它们的类也不相同。
那么为什么ServiceResult<IFooData> 和ServiceResult<IBarData> 都解析为ServiceResultOfanyType?
是我遗漏了什么,还是没有办法解决这个问题?
【问题讨论】:
-
到目前为止,我发现的唯一解决方法是将通用 ServiceResult
类型扁平化为一组固定的 ServiceResultOfIFooData : ServiceResult<IFooData>类,并更改 OperationContracts 以返回这些类型。不过,这让我感到恶心,因为它首先违反了使用泛型的目的。 -
我已经尝试使用
[DataContract(Name = "ServiceResultOf{0}")]为 ServiceResult使用 overriding the DataContract's Name [DataContract(Name = "ServiceResultOf{0}")],但 {0} 仍被解析为“anyType”,回想起来,这令人失望但并非完全出乎意料,因为这是当 Name 未被覆盖时,可能正是默认情况下正在执行的操作。 -
好问题。我有同样的问题,真的想避免使用具体的类。除了接受的答案中提到的解决方案之外,您是否可能找到另一种解决方案?
-
我用公认的答案将它摔倒在地。如果我再次这样做,我会放弃我对使用接口的“纯粹性”的坚持,并坚持使用具体的类。他们只是更容易使用。该解决方案有效,但回想起来,我只是觉得不值得付出努力。
标签: json wcf generics serialization interface