【问题标题】:Different behavior between 'Service Reference' and 'Web Reference'“服务参考”和“网络参考”之间的不同行为
【发布时间】:2010-11-10 20:01:03
【问题描述】:

我有如下定义的 WCF 端点,

<service name="MyApp.Server.Endpoint.Orange" behaviorConfiguration="MyTio.Server.Endpoint.OrangeBehavior">
  <endpoint address="" binding="basicHttpBinding" contract="Host.Server.Contract.IMyAppApi" bindingNamespace="http://host.com/myapp">
    <identity>
      <dns value="localhost"/>
    </identity>
  </endpoint>
  <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>

当我在 .NET 3.5 中添加“服务引用”时,我们会在代理中得到以下类,这非常好:

    [System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="GetMemberBillersRequest", Namespace="http://schemas.datacontract.org/2004/07/Contract.MemberBillers")]
[System.SerializableAttribute()]
public partial class GetMemberBillersRequest : WCFClient.MyRequest {

    [System.Runtime.Serialization.OptionalFieldAttribute()]
    private int ApplicationIdField;

    [System.Runtime.Serialization.OptionalFieldAttribute()]
    private int ProductIdField;

    [System.Runtime.Serialization.DataMemberAttribute()]
    public int ApplicationId {
        get {
            return this.ApplicationIdField;
        }
        set {
            if ((this.ApplicationIdField.Equals(value) != true)) {
                this.ApplicationIdField = value;
                this.RaisePropertyChanged("ApplicationId");
            }
        }
    }

    [System.Runtime.Serialization.DataMemberAttribute()]
    public int ProductId {
        get {
            return this.ProductIdField;
        }
        set {
            if ((this.ProductIdField.Equals(value) != true)) {
                this.ProductIdField = value;
                this.RaisePropertyChanged("ProductId");
            }
        }
    }
}

问题是当您在 .NET 2.0 中添加对同一服务的引用时

您将获得相同合同的以下代理:

    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.3082")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://schemas.datacontract.org/2004/07/Contract.MemberBillers")]
public partial class GetMemberBillersRequest : MyRequest {

    private int applicationIdField;

    private bool applicationIdFieldSpecified;

    private int productIdField;

    private bool productIdFieldSpecified;

    /// <remarks/>
    public int ApplicationId {
        get {
            return this.applicationIdField;
        }
        set {
            this.applicationIdField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlIgnoreAttribute()]
    public bool ApplicationIdSpecified {
        get {
            return this.applicationIdFieldSpecified;
        }
        set {
            this.applicationIdFieldSpecified = value;
        }
    }

    /// <remarks/>
    public int ProductId {
        get {
            return this.productIdField;
        }
        set {
            this.productIdField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlIgnoreAttribute()]
    public bool ProductIdSpecified {
        get {
            return this.productIdFieldSpecified;
        }
        set {
            this.productIdFieldSpecified = value;
        }
    }
}

除了通过 .NET 2.0 生成的代理有两个附加字段之外,两者都是相同的:

productIdFieldSpecified 和 applicationIdFieldSpecified。这两个字段的问题是,如果您不手动将它们设置为 true,则它们的相应字段(ApplicationId、ProductId)将不会被序列化并传递给服务器!

有人可以向我解释一下这里发生了什么吗?

编辑:

我发现这只发生在 int 类型上,而不是字符串! 这是此操作的数据合约

[DataContract]
public class GetMemberBillersRequest : MyRequest
{
    [DataMember]
    public int ApplicationId { get; set; }

    [DataMember]
    public int ProductId { get; set; }
}

【问题讨论】:

    标签: c# .net wcf web-services serialization


    【解决方案1】:

    这是预期的行为,自 .NET 1.0 以来一直如此。对于架构中可选的任何原始类型,您都会看到这一点——无论是带有 use="optional" 的属性,还是带有 minOccurs="0" 的元素。

    如果缺少属性或元素,则生成的属性不能设置为 null。相反,在这种情况下, *specified 字段设置为 false。在决定是否存在“真实”字段之前检查该字段。

    同样,如果要设置主属性,则必须将 *specified 属性设置为 true,否则不会发送。


    我相信你知道,但我要为未来的读者添加这个:是的,现在有可以为空的类型。然而,随着 WCF 的出现,ASMX Web 服务的开发速度大大放缓。从未为原始类型实现可空属性,这并不让我感到惊讶。

    另外,请注意:Microsoft: ASMX Web Services are a “Legacy Technology”

    【讨论】:

    • 这个服务中有很多操作合约,都有自己的数据合约,它们非常相似(只是名称不同),没有一个得到额外的字段。只有这个合同和这两个字段!
    • 我不知道。我必须查看 XML Schema 的相应部分,也许还有 WSDL。请注意,我对 *specified 字段的解释毫无疑问,唯一的问题可能是为什么在一种情况下而不是另一种情况下。
    • 实际上所有 int 类型都会发生这种情况,即使在其他合同中也是如此。
    • 所有不可为空的值类型都应该发生这种情况。尝试将一个设置为 [DataMember(Required=true)],将一个设置为 Nullable,看看会发生什么。
    • 保持原样。为什么说有“问题”?这就是它的本意。
    【解决方案2】:

    查看您的服务代码会很有帮助。我已经有一段时间没有使用 Web 服务引用了,但我的猜测是,如果这些字段不是可选的,请将 IsRequired = True 添加到您的 DataMemeber 属性并重新生成代理。

    【讨论】: