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

时间:2010-03-22 19:46:10

标签: 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);
}

这会侦听正确的soap-messages。但是,我需要以肥皂体开头

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

并且WCF正在收听:

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

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

更新 这是一些额外的信息:以下消息由真正的UPnP控制点发送。参数传递给Browse方法。

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

2 个答案:

答案 0 :(得分:4)

您显示的两个请求(一个是UPnP控制点,另一个是WCF测试客户端)不等同。第一个(UPnP)中Browse元素的子元素的名称空间是空(“”)名称空间,而在第二个请求(WCF TC)中,它是“urn:schemas-upnp-org:service:ContentDirectory:1” - 空前缀绑定到空名称空间,除非它绑定到另一个名称空间,并且它在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发布了一段时间 - 您可以使用该示例创建一个自定义编码器,将前缀设置为您期望的前缀。

答案 1 :(得分:2)

为什么需要特定的前缀?您使用的是依赖前缀的软件吗?

如果是这样,那么软件就会被破坏。就XML而言,您给出的两个“xmlns”示例是完全相同的。任何关心差异的软件都会被严重破坏,需要学习XML。