【问题标题】:Generic return types with interface type params in WCFWCF 中具有接口类型参数的通用返回类型
【发布时间】: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&lt;T&gt; 的第二种方法之前,即使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() 时的错误消息:

类型“ServiceResult`1[IBarData]”无法添加到已知类型列表,因为 具有相同数据合约名称的另一种类型 'ServiceResult`1[IFooData]' 'http://schemas.datacontract.org/2004/07/ServiceResultOfanyType' 是 已经存在。

错误消息的其余部分是关于碰撞集合类型List&lt;Test&gt;Test[] 的红鲱鱼,这里不是这种情况。

显然,IFooDataIBarData 并不相同,实现它们的类也不相同。

那么为什么ServiceResult&lt;IFooData&gt;ServiceResult&lt;IBarData&gt; 都解析为ServiceResultOfanyType

是我遗漏了什么,还是没有办法解决这个问题?

【问题讨论】:

  • 到目前为止,我发现的唯一解决方法是将通用 ServiceResult 类型扁平化为一组固定的 ServiceResultOfIFooData : ServiceResult&lt;IFooData&gt; 类,并更改 OperationContracts 以返回这些类型。不过,这让我感到恶心,因为它首先违反了使用泛型的目的。
  • 我已经尝试使用 [DataContract(Name = "ServiceResultOf{0}")] 为 ServiceResult 使用 overriding the DataContract's Name [DataContract(Name = "ServiceResultOf{0}")],但 {0} 仍被解析为“anyType”,回想起来,这令人失望但并非完全出乎意料,因为这是当 Name 未被覆盖时,可能正是默认情况下正在执行的操作。
  • 好问题。我有同样的问题,真的想避免使用具体的类。除了接受的答案中提到的解决方案之外,您是否可能找到另一种解决方案?
  • 我用公认的答案将它摔倒在地。如果我再次这样做,我会放弃我对使用接口的“纯粹性”的坚持,并坚持使用具体的类。他们只是更容易使用。该解决方案有效,但回想起来,我只是觉得不值得付出努力。

标签: json wcf generics serialization interface


【解决方案1】:

经过多次尝试和错误,我终于通过最小的更改完成了这项工作:

  • 我的服务操作现在返回ServiceResult&lt;T&gt; 而不是IServiceResult&lt;T&gt;。事实上,IServiceResult&lt;T&gt; 现在已经完全消失了。
  • GetServiceKnownTypes() 不再返回ServiceResult&lt;T&gt; 的所有变体。我只返回用作TDataContracts。

    #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
    

我现在可以调用所有这些方法并取回它们在ServiceContract 中的接口所引用的泛型类型列表。

【讨论】:

  • 我已经发布了自己的答案,因为至少有人认为这个问题值得一票。因此,希望同样的人可能会发现这个答案很有用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-10
  • 1970-01-01
相关资源
最近更新 更多