WCF中带有接口类型参数的通用返回类型 [英] Generic return types with interface type params in WCF

查看:135
本文介绍了WCF中带有接口类型参数的通用返回类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何从我的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();

//如果我取消注释该操作,则在调用任一方法时失败。
// [WebGet(UriTemplate =bar)]
// [OperationContract]
// IServiceResult< IBarData> GetBarData();
}

我离开了 GetCount() GetDescription()指出这两个通用结果工作正常,但显然它们是具体类型。甚至 GetFooData()都可以正常工作,直到我添加第二个方法 IServiceResult< T> ,其中 T 是一个接口。 $ b

返回类型 GetFooData() GetBarData()不一样,实现它们的具体类也是不一样的。



你可以想象我已经将实现简化为骨架,因为我不认为执行是问题的核心:

  #region我的服务实现
public class MyService:IMyService
{
public IServiceResult< int> GetCount()
{
返回新的ServiceResult< int>(42);
}
public IServiceResult< string> GetDescription()
{
返回新的ServiceResult< string>(Muffins);
}
public IServiceResult< IFooData> GetFooData()
{
返回新的ServiceResult< IFooData>(new FooData(){Foo = 99});
}
public IServiceResult< IBarData> GetBarData()
{
返回新的ServiceResult< IBarData>(new BarData(){Bar =Elvis was here));


#endregion

#region ServiceKnownTypesHelper.GetServiceKnownTypes():
public static class ServiceKnownTypesHelper
{
private静态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>及其具体实现:
public interface IServiceResult< T>
{
IList< string>错误{get; }
T值{get;组; }
}

[DataContract]
public class ServiceResult< T> :IServiceResult< T>
{
public ServiceResult(T value)
{
this.Value = value;
}

私有IList< string> errors = new List< string>();

[DataMember]
public IList< string>错误
{
得到
{
return this.errors;
}
}

[DataMember]
public T Value {get;组; }
}
#endregion

#region IFooData及其具体实现:
public interface IFooData
{
int Foo {get;组; }

$ b $ [DataContract]
public class FooData:IFooData
{
[DataMember]
public int Foo {get;组; }
}
#endregion

#region IBarData及其具体实现:
public interface IBarData
{
string Bar {get;组; }

$ b $ [DataContract]
public class BarData:IBarData
{
[DataMember]
public string Bar {get;组; }
}
#endregion

当我调用<$

 
类型'ServiceResult`1 [IBarData]'can not被添加到已知类型的列表中,因为
具有相同数据协定名称
'http://schemas.datacontract.org/2004/07/ServiceResultOfanyType'的另一个类型'ServiceResult`1 [IFooData]'是
已经存在。

其余的错误信息是关于碰撞集合类型的红鲱鱼 List< Test> Test [] ,这里不是这种情况。

显然, IFooData IBarData 不是同样的,也不是实现它们的类。

那为什么要做 ServiceResult< IFooData> ServiceResult< IBarData> 都解析为 ServiceResultOfanyType

我错过了什么,或者没有办法解决这个问题?

解决方案

在经过多次试验和错误之后,我终于通过最小的改变来完成这项工作:




  • 我的服务操作现在返回 ServiceResult 而不是 IServiceResult< T> code>。实际上, IServiceResult< T> 现在完全消失了。 code>不再返回 ServiceResult< T> 的所有变体。我只返回用作 T DataContract



    <$ (ServiceKnownTypes,typeof(ServiceKnownTypesHelper))]
    public interface IMyService
    [ServiceContract]
    [ServiceKnownTypes] {
    [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我的服务实现(减体)
    public class MyService:IMyService
    {
    public ServiceResult< int> GetCount(){}
    public ServiceResult< string> GetDescription(){}
    public ServiceResult< IFooData> GetFooData(){}
    public ServiceResult< IBarData> GetBarData(){}
    }
    #endregion
    #region ServiceKnownTypes
    //我的ServiceKnownTypes列表现在更短更简单。我给这个服务提供了太多的信息
    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>),
    };

    //剩余的实现与之前相同
    }
    #endregion
    #region ServiceResult< T> (没有使用或不需要)
    [DataContract]
    public class ServiceResult< T> //:IServiceResult< T>
    {
    //实现与之前相同
    }
    #endregion




  • $ b现在我可以调用所有这些方法并获取 ServiceContract 中由它们的接口引用的泛型类型列表。 code>。


    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:

    • My service operations now return 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 DataContracts 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.

    这篇关于WCF中带有接口类型参数的通用返回类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆