c# - 如何向 WCF ServiceContract 命名空间添加前缀

标签 c# .net wcf upnp

我正在尝试在 WCF 中实现 UPnP MediaServer。我正在慢慢地到达那里,但现在我遇到了一堵砖墙。我需要为 ServiceContract 命名空间添加一个前缀。现在我有以下内容:

[ServiceContract(Namespace = "urn:schemas-upnp-org:service:ContentDirectory:1")]
public interface  IContentDirectory
{
    [OperationContract(Action = "urn:schemas-upnp-org:service:ContentDirectory:1#Browse")]
    void Browse(string ObjectID, string BrowseFlag, string Filter, ulong StartingIndex, ulong RequestedCount, string SortCriteria, out string Result, out ulong NumberReturned, out ulong TotalMatches, out ulong UpdateID);
}

这会监听正确的肥皂消息。但是,我首先需要肥皂体

<u:Browse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1">

WCF 现在正在监听:

<Browse xmlns="urn:schemas-upnp-org:service:ContentDirectory:1">

我如何获得那里的前缀?有关系吗?或者是否有其他原因导致参数未传递到 Browse 方法中?

更新 这里有一些额外的信息: 下面的消息是由一个真正的 UPnP 控制点发送的。参数传递给浏览方法。

<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <To s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://XXXXX:8731/ContentDirectory</To>
    <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">urn:schemas-upnp-org:service:ContentDirectory:1#Browse</Action>
  </s:Header>
  <s:Body>
      <u:Browse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1">
         <ObjectID>0</ObjectID>
         <BrowseFlag>BrowseDirectChildren</BrowseFlag>
         <Filter>*</Filter>
         <StartingIndex>0</StartingIndex>
         <RequestedCount>15</RequestedCount>
         <SortCriteria />
      </u:Browse>
   </s:Body>
</s:Envelope>

这是 WCF 测试客户端生成的请求。现在将参数传递给 Browse 方法:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <To s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://XXXXXX:8731/ContentDirectory</To>
    <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">urn:schemas-upnp-org:service:ContentDirectory:1#Browse</Action>
  </s:Header>
  <s:Body>
    <Browse xmlns="urn:schemas-upnp-org:service:ContentDirectory:1">
      <ObjectID>0</ObjectID>
      <BrowseFlag>BrowseMetadata</BrowseFlag>
      <Filter>*</Filter>
      <StartingIndex>0</StartingIndex>
      <RequestedCount>0</RequestedCount>
      <SortCriteria i:nil="true" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" />
    </Browse>
  </s:Body>
</s:Envelope>

最佳答案

您显示的两个请求(一个来自 UPnP 控制点,一个来自 WCF 测试客户端)不等同。第一个 (UPnP) 中 Browse 元素的子项的命名空间是空 ("") 命名空间,而在第二个请求 (WCF TC) 中它是 "urn:schemas-upnp-org:service:ContentDirectory:1"-空前缀绑定(bind)到空命名空间,除非它绑定(bind)到另一个命名空间,并且它会在 WCF 请求中反弹,但不会在 UPnP 请求中反弹。

现在,如果您希望操作参数的命名空间不同于操作本身的命名空间,则必须使用消息协定 - 这将使 WCF 发送的请求等效(模前缀)到 UPnP 控制点发送的请求。下面的代码显示了如何定义消息协定以生成与您为控制点显示的请求等效的请求。

public class StackOverflow_2495195
{
    [MessageContract(WrapperName="Browse", WrapperNamespace="urn:schemas-upnp-org:service:ContentDirectory:1", IsWrapped=true)]
    public class BrowseRequest
    {
        [MessageBodyMember(Namespace = "", Order = 0)]
        public string ObjectID;

        [MessageBodyMember(Namespace = "", Order = 1)]
        public string BrowseFlag;

        [MessageBodyMember(Namespace = "", Order = 2)]
        public string Filter;

        [MessageBodyMember(Namespace = "", Order = 3)]
        public ulong StartingIndex;

        [MessageBodyMember(Namespace = "", Order = 4)]
        public ulong RequestedCount;

        [MessageBodyMember(Namespace = "", Order = 5)]
        public string SortCriteria;
    }

    [MessageContract(WrapperName = "BrowseResponse", WrapperNamespace = "urn:schemas-upnp-org:service:ContentDirectory:1", IsWrapped = true)]
    public class BrowseResponse
    {
        [MessageBodyMember(Namespace = "", Order = 0)]
        public string Result;

        [MessageBodyMember(Namespace = "", Order = 1)]
        public ulong NumberReturned;

        [MessageBodyMember(Namespace = "", Order = 2)]
        public ulong TotalMatches;

        [MessageBodyMember(Namespace = "", Order = 3)]
        public ulong UpdateID;
    }

    [ServiceContract(Namespace = "urn:schemas-upnp-org:service:ContentDirectory:1")]
    public interface IContentDirectory
    {
        [OperationContract(Action = "urn:schemas-upnp-org:service:ContentDirectory:1#Browse")]
        //void Browse(string ObjectID, string BrowseFlag, string Filter, ulong StartingIndex, ulong RequestedCount, string SortCriteria, out string Result, out ulong NumberReturned, out ulong TotalMatches, out ulong UpdateID);
        BrowseResponse Browse(BrowseRequest request);
    }
    public class Service : IContentDirectory
    {
        //public void Browse(string ObjectID, string BrowseFlag, string Filter, ulong StartingIndex, ulong RequestedCount, string SortCriteria, out string Result, out ulong NumberReturned, out ulong TotalMatches, out ulong UpdateID)
        //{
        //    Result = null;
        //    NumberReturned = 0;
        //    TotalMatches = 0;
        //    UpdateID = 0;
        //}
        public BrowseResponse Browse(BrowseRequest request)
        {
            return new BrowseResponse { NumberReturned = 0, Result = null, TotalMatches = 0, UpdateID = 0 };
        }
    }
    static Binding GetBinding()
    {
        return new CustomBinding(
            new TextMessageEncodingBindingElement(MessageVersion.Soap11WSAddressing10, Encoding.UTF8),
            new HttpTransportBindingElement());
    }
    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        host.AddServiceEndpoint(typeof(IContentDirectory), GetBinding(), "");
        host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });
        host.Open();
        Console.WriteLine("Host opened");

        ChannelFactory<IContentDirectory> factory = new ChannelFactory<IContentDirectory>(GetBinding(), new EndpointAddress(baseAddress));
        IContentDirectory proxy = factory.CreateChannel();
        //string result;
        //ulong ul1, ul2, ul3;
        //proxy.Browse(null, null, null, 0, 0, null, out result, out ul1, out ul2, out ul3);
        proxy.Browse(new BrowseRequest { BrowseFlag = null, Filter = null, ObjectID = null, RequestedCount = 0, SortCriteria = null, StartingIndex = 0 });

        ((IClientChannel)proxy).Close();
        factory.Close();

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}

现在,WCF 发送的请求是 XML 等效的,但前缀仍然不同。正如 John Saunders 所提到的,这应该无关紧要,因此 WCF 中没有简单的旋钮可以让您为 WCF 消息中写入的元素设置前缀。但是,您可以使用自定义编码器来做到这一点。我不久前在 http://blogs.msdn.com/b/carlosfigueira/archive/2010/06/13/changing-prefixes-in-xml-responses.aspx 上发布了它- 您可以使用该示例创建一个自定义编码器,它将前缀设置为您期望的样子。

关于c# - 如何向 WCF ServiceContract 命名空间添加前缀,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2495195/

相关文章:

从网站客户端脚本进行 WCF RESTful 服务跨域访问

c# - WCF 请求返回错误响应

c# - 具有超过 2000 个参数的 ADO.NET 批量插入

c# - 不能从内部类公开可见类?

c# - 如何在特定环境下运行dotnet测试?

c# - 可以通过 $http 但不能通过 ngResource 调用 WCF 服务

wcf - 在 IIS7 和 Windows 7 中配置 WCF

c# - 从 native 调用 C# 来检索字符串

c# - 我在哪里可以获得 'IgnoreDatabaseOutOfSync' 属性

.net - 如何将较高版本的 .net 库 (.dll) 与较低版本的二进制文件集成