2013-03-28 184 views
9

如何在我的WCF REST服務中從OperationContracts返回泛型類型參數中的接口類型?更具體地說,它適用於一個操作,但不適用於當我使用通用接口T添加第二個操作時。WCF中帶接口類型參數的泛型返回類型

我使用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()的例子指出,這兩個通用的結果做工精細,但很明顯,他們是具體類型。即使GetFooData()工作正常,直到我添加第二種方法IServiceResult<T>其中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()從瀏覽器:

 
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. 

錯誤消息的其餘部分是一個紅色的鯡魚約碰撞集合類型List<Test>Test[],這是不是這裏的情況。

很明顯,IFooDataIBarData是不一樣的,也沒有實現它們的類。

那麼爲什麼ServiceResult<IFooData>ServiceResult<IBarData>都解決爲ServiceResultOfanyType

我錯過了什麼,或者有沒有辦法解決這個問題?

+0

到目前爲止,我發現的唯一解決方法是將通用ServiceResult 類型平鋪到一組固定的「ServiceResultOfIFooData:ServiceResult 」類中,並將OperationContracts更改爲返回這些類型。這讓我感到不舒服,因爲它首先違反了仿製藥的目的。 – JMD 2013-03-28 18:32:01

+0

我試過[覆蓋DataContract的名稱](http://msdn.microsoft.com/en-us/library/ms731045(v = vs.100).aspx)ServiceResult 使用'[DataContract(Name =「) ServiceResultOf {0}「)]'但{0}仍然解析爲」anyType「,回想起來,這是令人失望的,但並不是完全沒有預料到的,因爲這可能正是默認情況下當Name不被覆蓋時正在做的事情。 – JMD 2013-03-28 22:07:41

+0

偉大的問題。我有同樣的問題,真的想避免使用具體的類。您是否可能找到另一種解決方案,而不是接受答案中提到的解決方案? – smoksnes 2016-02-12 08:56:14

回答

5

大量的試驗和錯誤後,我終於得到這個工作以最小的變化:

  • 我的服務業務現在回到ServiceResult<T>,而不是IServiceResult<T>。實際上,IServiceResult<T>現在完全消失了。
  • GetServiceKnownTypes()不再返回ServiceResult<T>的所有變體。我只返回用作TDataContract

    #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提到泛型類型的列表。

+3

我發佈了自己的答案,因爲至少有一個人認爲這個問題值得讚揚。因此,希望同一個人可能會找到有用的答案。 – JMD 2013-04-02 22:15:47