WCF:Interfaces,Generics和ServiceKnownType

时间:2009-09-03 23:28:41

标签: .net wcf generics interface serviceknowntype

我有以下内容:

[ServiceContract]
[ServiceKnownType(typeof(ActionParameters))]
[ServiceKnownType(typeof(SportProgram))]
[ServiceKnownType(typeof(ActionResult<SportProgram>))]
public interface ISportProgramBl  
{
    [OperationContract]
    IActionResult<ISportProgram> Get(IActionParameters parameters);
}

当我运行Get方法时,我收到以下错误:

  

尝试序列化参数http://tempuri.org/:GetResult时出错。 InnerException消息是'Type'PPS.Core.DomainModel.Support.Action.ActionResult`1 [[PPS.Core.DomainModel.SportProgram.ISportProgram,PPS.Core.DomainModel,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null]]'与数据契约名称'ActionResultOfanyType:http://schemas.datacontract.org/2004/07/PPS.Core.DomainModel.Support.Action'不是预期的。将任何静态未知的类型添加到已知类型列表中 - 例如,通过使用KnownTypeAttribute属性或将它们添加到传递给DataContractSerializer的已知类型列表中。有关详细信息,请参阅InnerException。

从这个错误中我可以看到它可以解析ActionResult但是它无法解析ISportProgram,即使我的服务接口上有 ServiceKnownType(typeof(ActionResult&lt; SportProgram&gt;))

请注意,这是生成的引用存根,如下所示,因此我可以看到已正确引入已知类型:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="SportProgramStb.ISportProgramBl")]
public interface ISportProgramBl {

    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/ISportProgramBl/Get", ReplyAction="http://tempuri.org/ISportProgramBl/GetResponse")]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(PPS.Core.DomainModel.SportProgram.SportProgram))]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(PPS.Core.DomainModel.Support.Action.ActionParameters))]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(PPS.Core.DomainModel.Support.Action.ActionResult<PPS.Core.DomainModel.SportProgram.SportProgram>))]
    object Get(object parameters);
}

为什么会出错????请注意它正确地通过WCF服务...但它会在返回结果时抛出异常。

最后,ActionResult看起来像这样:

public interface IActionResult<T> 
{
    T Result { get; set; } 
}

干杯 安东尼

5 个答案:

答案 0 :(得分:15)

嗯,我认为这是SOA与OOP“阻抗不匹配”的另一个案例。这两个世界是完全分开的。

在WCF中,从客户端传递到服务器的所有内容都作为序列化消息传递 - 没有使用任何引用。

这意味着:您要在客户端上序列化的所有内容,将其发送到服务器,然后反序列化并在那里使用它,必须具体 - 您无法传递接口,您无法使用“未解决的”泛型 - 你需要拼写出来。基本上,所有通过网络从客户端传递到服务器的都必须在XML模式中表达。

这有很多含义:

  • 没有接口 - 你不能传递接口 - 你需要使用具体类型
  • 没有“自动”继承 - 你不能只定义一个基类并根据它来传递派生类 - 那些也需要特殊化(这就是ServiceKnownType属性的用途)
  • 没有自动泛型 - 再次,您需要使用具体类型

这可能听起来有很多限制 - 但这是因为WCF正在使用所有基于消息的通信 - 它无法处理引用,继承,泛型等 - 你需要拼写出来。

所以我本身并没有给你答案 - 我只是认为你需要重新思考你的策略并改变客户端和服务器通过WCF交换信息的方式。

马克

PS:我做了更多的研究,与我的理解相反,似乎有一种方法可以序列化基于接口和/或抽象基类的任何东西,只要你能确定它是在线路的任何一端始终只有.NET(即可与Java进行互操作)。

请参阅Aaron Skonnard blog post on the NetDataContractSerializer和另一个blog postyet another,了解如何使用NetDataContractSerializer将IPerson之类的内容作为参数传递给您的方法。

答案 1 :(得分:2)

这是我用ServiceStack.NET - 我的开源.NET和MONO Web服务框架解决的问题之一。

服务堆栈受Martin Fowlers Data Transfer Object Pattern的影响很大,因为它允许您简单地使用DTO来定义您的Web服务 - 即SOA方式:)。

我通过生成我自己的WSDL来避免这种在WCF中固有的限制,其行为与您期望的一样。作为替换WCF的复杂配置/ ServiceContract模型的好处 - SOAP Web服务也适用于MONO - see the live demo

答案 2 :(得分:1)

这是一个古老的问题,尽管接受的答案是完全正确的,但我在寻找类似问题的过程中不知所措,并认为我可以分享我的经验。它经常令人头痛,但可以将泛型与WCF接口结合使用。这是我已经完成的另一个(类似的)实现的工作示例:

[ServiceContract]
[ServiceKnownType(typeof(CollectionWrapper<IAssociation>))]
public interface IService : 
{
    [OperationContract]
    ICollectionWrapper<IAssociation> FindAssociation(string name, int pageSize, int page);
}

public interface ICollectionWrapper<TModel>
{
    int TotalCount { get; set; }
    IEnumerable<TModel> Items { get; set; }
}

[KnownType(typeof(OrganizationDto))]
[KnownType(typeof(CompanyDto))]
public class CollectionWrapper<TModel> : ICollectionWrapper<TModel>
{
    [DataMember]
    public int TotalCount { get; set; }
    [DataMember]
    public IEnumerable<TModel> Items { get; set; }
}

public class CompanyDto :  IAssociation
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class OrganizationDto :  IAssociation
{
    public int Id { get; set; }
    public string Name { get; set; }
}

此处的关键是使用KnownTypeServiceKnownType的组合。

所以在你的情况下你可以这样做:

[ServiceContract]
[ServiceKnownType(typeof(ActionParameters))]
[ServiceKnownType(typeof(ActionResult<ISportProgram>))] // Actual implementation of container, but interface of generic.
public interface ISportProgramBl  
{
    [OperationContract]
    IActionResult<ISportProgram> Get(IActionParameters parameters);
}

[KnownType(typeof(SportProgram))] // Actual implementation here.
public class ActionResult<T>
{
    // Other stuff here
    T FooModel { get; set; }
}

如果您有共享合同(访问实际服务接口)并使用ChannelFactory<ISportProgramBl>合同,这将有效。我不知道它是否适用于服务引用。

然而,这里提到的实施似乎存在一些问题:

WCF With a interface and a generic model

另一个类似的问题在这里提出并回答:

Generic return types with interface type params in WCF

答案 3 :(得分:0)

您是在对象中指定接口而不是具体类型吗?

PPS.Core.DomainModel.Support.Action.ActionListResult<IList<PPS.Core.DomainModel.SportProgram.ISportProgram>>

编辑:

我所说的是,您在通用类型列表中传递的泛型(包括通过接口的子对象)中传递的所有具体类型。我们遇到了所有类型都不知道的问题。

答案 4 :(得分:0)

您正在返回T的IList。可能系统在查找T是什么时遇到问题。

不确定是否可以返回接口而不是类型。