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

发布于 2024-09-03 12:20:20 字数 3585 浏览 4 评论 0原文

我正在努力从 Web 服务/Web 客户端架构迁移到 WCF 架构。该对象非常复杂,有很多嵌套的 xsd 和不同的命名空间。代理类是通过将 Web 引用添加到具有 30 多个 Web 方法的原始 wsdl 并使用 xsd.exe 生成缺少的 SOAPFault 对象来生成的。我的试点 WCF 服务仅包含 1 个 Web 方法,它与原始方法之一的语法完全匹配:1 个对象作为参数,返回 1 个其他对象作为结果值。我使用这些代理类创建了一个 WCF 接口,在接口上使用属性: XMLSerializerFormatServiceContract,在原始 wsdl 的一种方法上使用 OperationContract 指定 < code>Action、ReplyAction,都具有正确的命名空间。我使用 SoapUI 创建传入的客户端消息;我从原始 WSDL 文件生成了一个项目(导致 SoapUI 项目有 30 多个方法),并在实现的 WebMethod 处创建了一个新请求,将 url 更改为我的 wcf webservice 并发送消息。由于 OperationContractAttribute 中指定了 (Reply-)Action,消息实际上被接收并正确反序列化为对象。

为了走到这一步(40 个小时的谷歌搜索),很多挫折让我使用了一个自定义端点,其中删除了 WCF“包装标签”,更正了嵌套类型的命名空间,并且对生成的 wsdl get 进行了扁平化(为了更好)与 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 发送消息。但生成的 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>

错误的 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 客户端发送消息。为了清楚起见:该方法确实使用我的 Web 服务上的 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);

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

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

有人可以帮我吗?我无法用谷歌搜索任何关于这个的东西......

I'm struggling with migrating from webservice/webclient architecture to WCF architecture. The object are very complex, with lots of nested xsd's and different namespaces. Proxy classes are generated by adding a Web Reference to an original wsdl with 30+ webmethods and using xsd.exe for generating the missing SOAPFault objects. My pilot WCF Service consists of only 1 webmethod which matches the exact syntax of one of the original methods: 1 object as parameter, returning 1 other object as result value. I created a WCF Interface using those proxy classes, using attributes: XMLSerializerFormat and ServiceContract on the interface, OperationContract on one method from original wsdl specifying Action, ReplyAction, all with the proper namespaces. I create incoming client messages using SoapUI; I generated a project from the original WSDL files (causing the SoapUI project to have 30+ methods) and created one new Request at the one implemented WebMethod, changed the url to my wcf webservice and send the message. Because of the specified (Reply-)Action in the OperationContractAttribute, the message is actually received and properly deserialized into an object.

To get this far (40 hours of googling), a lot of frustration led me to using a custom endpoint in which the WCF 'wrapped tags' are removed, the namespaces for nested types are corrected, and the generated wsdl get's flattened (for better compatibility with other tools then MS VisualStudio).

Interface code is this:

[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);

When I use a Webclient in code to send a message, everything works. My problem is, when I use a WCF client. I use ChannelFactory<IOntvangAsynchroon> to send a message. But the generated xml looks different: it includes the parametername of the method! It took me a lot of time to figure this one out, but here's what happens:

Correct xml (stripped soap envelope):

<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>

Notice the zakLk011 element? It is the name of the parameter of the method in my interface! So NOW it is zakLk011, but it when my parameter name was zakLk01, the xml seemed to contain some magical duplicate of the tag above, but without namespace. Of course, you can imagine me going crazy over what was happening before finding out it was the parametername!

I now have actually created a WCF Service, at which I cannot send messages using a WCF Client anymore. For clarity: The method does get invoked using the WCF Client on my webservice, but the parameter object is empty. Because I'm using a custom endpoint to log the incoming xml, I can see the message is received fine, but just with the wrong syntax!

WCF client code:

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);

I am not using a generated client, i just reference the webservice (share libraries) like I am used to.

Edit: When generating a Service Reference, it generates duplicate classes(dont know why..). When these duplicates are removed however, a client sends messages with correct xml. But my architecture requires shared libraries, so this doesn't help me out.

Can anyone please help me? I can't google anything on this...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

深白境迁sunset 2024-09-10 12:20:20

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

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

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

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

然后你就可以开始改变事情了。尝试[XmlSerializerFormat]。尝试添加各种 [Xml*] 属性。慢慢地开始改变事情,直到你看到哪里出了问题。

Suggestion: if you're just getting started with WCF, then start off by doing things "the WCF way". Once you know how to do that correctly, you can start changing things. Right now, you don't know how much of your problem is due to WCF, and how much is just due to your lack of experience with WCF.

I suggest you start from scratch, not using [XmlSerializerFormat]. Just create a ServiceContract with a single OperationContract. Include at least one FaultContract so you can see how that works.

Create a WCF client using "Add Service Reference", and make sure it works.

Then create a SOAPUI project using the WSDL from the WCF service (not the original WSDL). Make sure that works.

Then you can start changing things. Try [XmlSerializerFormat]. Try adding various [Xml*] attributes. Slowly start changing things until you see what breaks.

暗喜 2024-09-10 12:20:20

好吧,我自己想出来了。我已经创建了一个可以工作的 WCF 客户端(服务参考),通过仔细查看生成的代码,我弄清楚了发生了什么。它与 WCF 包装 Webservice 方法的 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;
    }
}

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

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

没有 XMLElement 标记更加干净,该函数(生成正确的 xml)现在已被包装器类替换。

我可以接收非包装的 xml 的原因是我的自定义消息检查器包含一些代码,旨在接受非包装的 xml 消息,而无需向所有现有类添加 MessageContract 标签(或创建大量包装类)(在某处搜索) ,它做得很好。代码片段:

MessageDescription.Body.WrapperName = null;

但是接收由我的(第一个)WCF 客户端发送的包装消息仍然包装类不起作用,当然......

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

Okay, I figured it out myself. I had already created a WCF Client (Service Reference) that worked, and by looking closely at the generated code, I figured out what is happening. It has to do with way WCF wrappes all classes used in the DECLARATION of Webservice methods (so the classes used in properties of the method-mentioned-classes are not wrapped). In my case, the body of the ZAKLk01 class get wrapped with XMLElement tags, using the parametername as XMLElement-name. To get rid of this behaviour I am now using wrapper classes for my generated proxy-classes ZAKLk01 and Bv03Bericht, just like the generated classes do of my Service References proxy classes in my new WCF Client. These wrapper classes are decorated with MessageContractAttributes.

To give an example of one of those wrapper classes:

[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;
    }
}

My Interface method now looks like this:

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

Much cleaner without the XMLElement tags, which function (generate correct xml) have now been replaced by the wrapper classes.

The reason I could receive non-wrapped xml, was that my custom messageinspector contained some code, meant to accept non-wrapped xml messages without having to add MessageContract tags to all existing classes (or create lots of wrapper classes) (googled it somewhere), which it did just fine. Code snippet:

MessageDescription.Body.WrapperName = null;

But receiving wrapped messages which were send by my (first) WCF Client which still wrapped the classes didn't work, off course...

What I still don't understand is how those Action attributes work: if I don't supply them, my generated wsdl does not contain any method. Well, not important for now, since I can finally move on, and will tinker with the Action attributes some other time.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文