将webclient迁移到WCF; WCF客户端序列化方法的参数

时间:2010-06-01 09:40:15

标签: .net wcf wcf-proxy

我正在努力从webservice / webclient架构迁移到WCF架构。该对象非常复杂,有许多嵌套的xsd和不同的命名空间。通过向具有30+ webmethods的原始wsdl添加Web引用并使用xsd.exe生成缺少的SOAPFault对象来生成代理类。我的飞行员WCF服务只包含1个webmethod,它匹配原始方法之一的确切语法:1个对象作为参数,返回1个其他对象作为结果值。我使用这些代理类创建了一个WCF接口,在接口上使用XMLSerializerFormatServiceContractOperationContract在原始wsdl指定ActionReplyAction的一个方法上创建,都具有适当的命名空间。我使用SoapUI创建传入的客户端消息;我从原始的WSDL文件生成了一个项目(导致SoapUI项目有30多个方法)并在一个实现的WebMethod上创建了一个新的Request,将url更改为我的wcf webservice并发送消息。由于OperationContractAttribute中指定的(回复)操作,实际上会接收消息并将其正确反序列化为对象。

为了实现这一目标(40小时的谷歌搜索),很多挫折让我使用了一个自定义端点,其中删除了WCF的“包装标签”,更正了嵌套类型的命名空间,并且生成的wsdl得到了扁平化(为了更好地与其他工具兼容,然后使用MS VisualStudio)。

接口代码是这样的:

[XmlSerializerFormat(Use = OperationFormatUse.Literal, Style = OperationFormatStyle.Document, SupportFaults = true)]
[ServiceContract(Namespace = Constants.NamespaceStufZKN)]
public interface IOntvangAsynchroon
{

    [OperationContract(Action = Constants.NamespaceStufZKN + "/zakLk01", ReplyAction = Constants.NamespaceStufZKN + "/zakLk01", Name = "zakLk01")]
    [FaultContract(typeof(Fo03Bericht), Namespace = Constants.NamespaceStuf)]
    Bv03Bericht zakLk01([XmlElement("zakLk01", Namespace = Constants.NamespaceStufZKN)] ZAKLk01 zakLk011);

当我在代码中使用Webclient发送消息时,一切正常。我的问题是,当我使用WCF客户端时。我使用ChannelFactory<IOntvangAsynchroon>发送消息。但生成的xml看起来不同:它包含方法的参数名称!我花了很多时间来解决这个问题,但这是发生的事情:

纠正xml(剥离的肥皂信封):

<soap:Body>
  <zakLk01 xmlns="http://www.egem.nl/StUF/sector/zkn/0310">
    <stuurgegevens>
      <berichtcode xmlns="http://www.egem.nl/StUF/StUF0301">Bv01</berichtcode>
      <zender xmlns="http://www.egem.nl/StUF/StUF0301">
        <applicatie>ONBEKEND</applicatie>
      </zender>
    </stuurgegevens>
    <parameters>
    </parameters>
  </zakLk01>
</soap:Body>

Bad xml:

<soap:Body>
  <zakLk01 xmlns="http://www.egem.nl/StUF/sector/zkn/0310">
    <zakLk011>
    <stuurgegevens>
      <berichtcode xmlns="http://www.egem.nl/StUF/StUF0301">Bv01</berichtcode>
        <zender xmlns="http://www.egem.nl/StUF/StUF0301">
          <applicatie>ONBEKEND</applicatie>
        </zender>
      </stuurgegevens>
      <parameters>
      </parameters>
    </zakLk011>
  </zakLk01>
</soap:Body>

注意zakLk011元素?它是我界面中方法参数的名称!现在它是zakLk011,但是当我的参数名称为zakLk01时,xml似乎包含上面标记的一些神奇副本,但没有名称空间。当然,你可以想象我在发现它之前发生了什么事情,然后才发现它是参数号!

我现在实际上已经创建了一个WCF服务,我无法再使用WCF客户端发送消息。为清楚起见:该方法确实在我的webservice上使用WCF客户端调用,但参数对象为空。因为我使用自定义端点来记录传入的xml,所以我可以看到消息收到的很好,但是语法错误了!

WCF客户端代码:

ZAKLk01 stufbericht = MessageFactory.CreateZAKLk01();
ChannelFactory<IOntvangAsynchroon> factory = new ChannelFactory<IOntvangAsynchroon>(new BasicHttpBinding(), new EndpointAddress("http://localhost:8193/Roxit/Link/zkn0310"));
factory.Endpoint.Behaviors.Add(new LinkEndpointBehavior());
IOntvangAsynchroon client = factory.CreateChannel();
client.zakLk01(stufbericht);

我没有使用生成的客户端,我只是像我以前那样引用webservice(共享库)。

编辑:生成服务引用时,它会生成重复的类(不知道为什么......)。但是,当删除这些重复项时,客户端会发送带有正确xml的消息。但我的架构需要共享库,所以这对我没有帮助。

任何人都可以帮助我吗?我不能谷歌任何东西...

2 个答案:

答案 0 :(得分:0)

建议:如果您刚刚开始使用WCF,那么首先要做“WCF方式”。一旦你知道如何正确地做到这一点,你就可以开始改变一切。现在,你不知道你的问题有多少是由于WCF造成的,而且由于你缺乏WCF经验而导致了多少问题。

我建议你从头开始,而不是使用[XmlSerializerFormat]。只需使用一个ServiceContract创建OperationContract即可。至少包含一个FaultContract,以便了解其工作原理。

使用“添加服务引用”创建WCF客户端,并确保它正常工作。

然后使用WCF服务(而不是原始WSDL)中的WSDL创建SOAPUI项目。确保 有效。

然后你可以开始改变一切。试试[XmlSerializerFormat]。尝试添加各种[Xml*]属性。慢慢开始换东西,直到看到什么断裂。

答案 1 :(得分:0)

好的,我自己弄清楚了。我已经创建了一个有效的WCF客户端(服务参考),通过仔细查看生成的代码,我发现了正在发生的事情。它与WCF的方式有关,它包含了Web服务方法的DECLARATION中使用的所有类(因此在方法提到的类的属性中使用的类不会被包装)。在我的例子中,ZAKLk01类的主体用XMLElement标签包装,使用参数名作为XMLElement-name。为了摆脱这种行为,我现在使用生成的代理类ZAKLk01和Bv03Bericht的包装类,就像我的新WCF客户端中生成的类服务引用代理类一样。这些包装类使用MessageContractAttributes进行修饰。

举一个包装类之一的例子:

[MessageContract(IsWrapped = false)]
public partial class zakLk01Request
{

    [MessageBodyMember(Namespace = Constants.NamespaceStufZKN, Order = 0)]
    public ZAKLk01 zakLk01;

    public zakLk01Request()
    {
    }

    public zakLk01Request(ZAKLk01 zakLk01)
    {
        this.zakLk01 = zakLk01;
    }
}

我的界面方法现在看起来像这样:

[OperationContract(Action = Constants.NamespaceStufZKN + "/zakLk01", ReplyAction = Constants.NamespaceStufZKN + "/Bv03Bericht")]
[FaultContract(typeof(Fo03Bericht), Namespace = Constants.NamespaceStuf)]
zakLk01Response zakLk01(zakLk01Request zakLk01);

没有XMLElement标签的更清洁,哪个函数(生成正确的xml)现在已被包装类替换。

我可以收到非包装xml的原因是我的自定义messageinspector包含一些代码,意味着接受非包装的xml消息,而不必将MessageContract标记添加到所有现有的类(或创建大量的包装类)(googled)它在某处),它做得很好。代码段:

MessageDescription.Body.WrapperName = null;

但接收由我的(第一个)WCF客户端发送的包裹的消息,这些消息仍然包含了这些类,但是当然没有...

我仍然不明白这些Action属性是如何工作的:如果我不提供它们,我生成的wsdl不包含任何方法。嗯,现在并不重要,因为我可以继续前进,并且会在其他时间修改Action属性。