Friday 22 January 2010

Testing for default T - Generics

I came accross this SO post after discovering you cannot just write:


if ( T == default(T) ) {}


You actually need either
if ( object.Equals(value, default(T)) ) {}


or (according to the SO post more efficient - though I've not checked this)
if (EqualityComparer.Default.Equals(value,default(T)))  {}



UPDATE:

I was asked the question: “I assume that this is to avoid evaluating the reference (pointer) rather than the value?”

I think that would depend on the implementation of the compare method for the type being used and as the SO post suggests this isn’t the whole story… to take a step back…

The reason for this roundabout way of checking that the value (T) is empty is logically because T could be either value or reference type, so a method is needed which can be made to deal with both value and reference types.

As you suggest Object.Equals() is a compares pointers (references), so if T was a value type it would first need boxing (first needs to be turned into a reference type), you can see this in the IL (example1 below – a contrived and nonsense function!) …

Also note in the IL that “default(T)” statement means that T is boxed too – this will be because T can be either reference or value type, so the IL boxes to force the issue (actually this surprises me a bit, I was thinking the compiler would be able to work this out based on the use, but maybe that’s asking a lot?!).

So to go back to the original post (and a check in MSDN confirms this) the use of “EqualityComparer.Default.Equals(value, default(T))” – because the underlying implementation supports value and reference types [class/struct] - means that the IL generated no longer needs to first box (example2) and I believe that’s why it’s considered more efficient and that’s the jist of the comment in SO.

Note: I haven’t dug down any deeper, it could well be that if the code falls back to Object.Equals for a value type then boxing will still occur.


Examples:

        public static T Foo()
        {
            T r = default(T);
            int x=0;
            string y = "";
            bool result = object.Equals(x, default(T));
            return r;
        } 

(abridged)
IL_0012: box [mscorlib]System.Int32
IL_0017: ldloca.s CS$0$0001
IL_0019: initobj !!T
IL_001f: ldloc.s CS$0$0001
IL_0021: box !!T
IL_0026: call bool [mscorlib]System.Object::Equals(object, object)

        public static T Foo()
        {
            T r = default(T);
            int x=0;
            string y = "";
            bool b = EqualityComparer.Default.Equals(r, default(T));
            return r;
        }


(abridged)
IL_0001: ldloca.s r
IL_0003: initobj !!T
.
.
IL_0019: initobj !!T
IL_001f: ldloc.s CS$0$0001
IL_0021: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, !0)

Thursday 21 January 2010

WCF Basic HTTP, XML Serializer, XmlRoot and namespaces

Example service takes a simple complex type and appends additional data to the string value:

public class TestService : ITestService
    {
        public MyType MyTestMethod(MyType obj)
        {
            obj.StringValue += "Suffix";
            return obj;
        }
    }

…And the interface definition…
[ServiceContract(Namespace = "http://servicecontract"), XmlSerializerFormat()]
    public interface ITestService
    {
        [OperationContract]
        MyType MyTestMethod(MyType obj);
    }
    [Serializable, XmlType, XmlRoot(Namespace = "http://datacontract")] 
    public class MyType
    {
        [XmlAttribute]
        public string StringValue { get; set; }
    }


Notice that the type (defined in the data contract) which is passed through MyTestMethod() has a different namespace from the service.

Also note that the service is decorated with xml serialization attributes and marked explicitly to use XML Serializer rather than the default Data Contract Serializer (DCS).

When this service is consumed within a .net client application the proxy is auto-generated (svcutil.exe), but note that the proxy definition and original definition do not match exactly, there is a subtle difference:

(Abridged definition)
[System.SerializableAttribute()]
    [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://datacontract")]
    public partial class MyType : object, System.ComponentModel.INotifyPropertyChanged {
        
        private string stringValueField;
        
        /// 
        [System.Xml.Serialization.XmlAttributeAttribute()]
        public string StringValue {
            get {
                return this.stringValueField;
            }
            set {
                this.stringValueField = value;
            }
        }
        
    }


The missing attribute is:

[System.Xml.Serialization.XmlRootAttribute(Namespace="http://datacontract")]

This does not stop the proxy from working; you can still send and receive “MyType” through the service, it will be de-serialized at the service and the client correctly:

static void Main(string[] args)
        {
            using (TestServiceClient service = new TestServiceClient())
            {
                MyType t = new MyType();
                t.StringValue = "Prefix:";
                MyType r = service.MyTestMethod(t);
            }
        }


But, if you serialize the type you will notice that the namespace is missing from the request to the service

<mytype StringValue="Prefix:" />

And from the response, from the service

<mytype StringValue="Prefix:Suffix" />


A close look at the wsdl helps to explain why
<xs:schema elementFormDefault="qualified" targetNamespace="http://servicecontract" xmlns:tns="http://servicecontract">
    <xs:import schemaLocation="http://.../?xsd=xsd1" namespace="http://datacontract"/>
    <xs:element name="MyTestMethod">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="obj" type="q1:MyType" xmlns:q1="http://datacontract"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="MyTestMethodResponse">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="MyTestMethodResult" type="q2:MyType" xmlns:q2="http://datacontract"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>


Both the request “obj” and response “MyTestMethodResult” elements belong to the service namespace though the type is defined correctly in the namespace I defined for the data contract.

We understand this behaviour is part of WCF by design; it’s not a bug and should not cause too many problems provided it’s understood.

***

Out of curiosity I tried adding this simple service to BTS 2006 using “add web reference” from the project context menu, but received this error:

ERROR: Failed to add a Web Reference.

I then read this post from Saravana Kumar (with comment from our Jeremy).

I removed the service namespace, rebuilt the service and re-added the service as a “web reference” successfully, I then re-added the service namespace, re-built and tried updating the web reference in my BTS 2006 project (just to see) and got a different error, again noted in Saravana’s post:-

Could Not generate BizTalk files. Index was out of range. Must be non-negative and less than the size of the colleciton. Parameter name: index

I then altered my service definition as per Jeremy’s comments, adding a service behaviour attribute, also with the service namespace:
[ServiceBehavior(Namespace = "http://servicecontract")]
    public class TestService : ITestService
    {
        public MyType MyTestMethod(MyType obj)
        {
            obj.StringValue += "Suffix";
            return obj;
        }
    }


The service Interface stays the same, the service contract attribute stays with the namespace declared.
Important note: The namespaces are the same:

[ServiceContract(Namespace = "http://servicecontract"), XmlSerializerFormat()]
    public interface ITestService
    {
        [OperationContract]
        MyType MyTestMethod(MyType obj);
    }
    [Serializable, XmlType, XmlRoot(Namespace = "http://datacontract")] 
    public class MyType
    {
        [XmlAttribute]
        public string StringValue { get; set; }
    }



This worked OK and I was able to add my web reference to my BTS 2006 project, with the service displaying the correct service namespace.

However, the WSDL differs significantly with this change…

Here’s the WSDL before the change (BizTalk 2006 threw an error when we tried to import from this), note the target namespace of the wsdl is “tempuri”, though I had thought a service namespace had been provided in the service contract (in the service interface definition):

<?xml version="1.0" encoding="utf-8"?>
<!-- without behaviour, BizTalk unhappy!-->
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:i0="http://servicecontract" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:tns="http://tempuri.org/" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsa10="http://www.w3.org/2005/08/addressing" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" name="TestService" targetNamespace="http://tempuri.org/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
    <wsdl:import namespace="http://servicecontract" location="http://localhost:8732/Design_Time_Addresses/WcfServiceLibrary1/Service1/?wsdl=wsdl0" />
    <wsdl:types />
    <wsdl:binding name="BasicHttpBinding_ITestService" type="i0:ITestService">
        <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
        <wsdl:operation name="MyTestMethod">
            <soap:operation soapAction="http://servicecontract/ITestService/MyTestMethod" style="document" />
            <wsdl:input>
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="TestService">
        <wsdl:port name="BasicHttpBinding_ITestService" binding="tns:BasicHttpBinding_ITestService">
            <soap:address location="http://localhost:8732/Design_Time_Addresses/WcfServiceLibrary1/Service1/" />
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>


…And here’s the WSDL with the addition of “service behaviour” attribute and namespace, note that the types are now defined and no import is required to a separately defined wsdl.

<?xml version="1.0" encoding="utf-8"?>
<!-- with behaviour, BizTalk is happy!-->
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:i0="http://tempuri.org/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:tns="http://servicecontract" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsa10="http://www.w3.org/2005/08/addressing" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" name="TestService" targetNamespace="http://servicecontract" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
    <wsdl:import namespace="http://tempuri.org/" location="http://localhost:8732/Design_Time_Addresses/WcfServiceLibrary1/Service1/?wsdl=wsdl0" />
    <wsdl:types>
        <xsd:schema targetNamespace="http://servicecontract/Imports">
            <xsd:import schemaLocation="http://localhost:8732/Design_Time_Addresses/WcfServiceLibrary1/Service1/?xsd=xsd0" namespace="http://servicecontract" />
            <xsd:import schemaLocation="http://localhost:8732/Design_Time_Addresses/WcfServiceLibrary1/Service1/?xsd=xsd1" namespace="http://datacontract" />
        </xsd:schema>
    </wsdl:types>
    <wsdl:message name="ITestService_MyTestMethod_InputMessage">
        <wsdl:part name="parameters" element="tns:MyTestMethod" />
    </wsdl:message>
    <wsdl:message name="ITestService_MyTestMethod_OutputMessage">
        <wsdl:part name="parameters" element="tns:MyTestMethodResponse" />
    </wsdl:message>
    <wsdl:portType name="ITestService">
        <wsdl:operation name="MyTestMethod">
            <wsdl:input wsaw:Action="http://servicecontract/ITestService/MyTestMethod" message="tns:ITestService_MyTestMethod_InputMessage" />
            <wsdl:output wsaw:Action="http://servicecontract/ITestService/MyTestMethodResponse" message="tns:ITestService_MyTestMethod_OutputMessage" />
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:service name="TestService">
        <wsdl:port name="BasicHttpBinding_ITestService" binding="i0:BasicHttpBinding_ITestService">
            <soap:address location="http://localhost:8732/Design_Time_Addresses/WcfServiceLibrary1/Service1/" />
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

The difference is this: In the first wsdl types tag is empty, the types are defined in imported WSDL0 and this appears to be because the service itself has two namespace, the service namespace (as defined in the service contract) and tempuri.

When the service behaviour attribute is added to the service implementation (and it shares the same namespace as defined on the contract) then the whole service now exists under a single namespace and therefore the WSDL is complete without any imports.

Clearly in this example the type generator in BTS 2006 is seeing the empty types definition and doesn’t know to resolve the imported wsdl (wsdl=0) which would yield the type definitions.

The SOAP adapter would appear to deal with the XML Root issue OK, I believe this is because from Biztalk you pass an instance of the type, and the SOAP adapter builds the SOAP envelope so you do not have to construct the message elements directly (request “obj” and response “MyTestMethodResult, see wsdl extract above).

Here is the xml for the message I sent to the service from Biztalk 2006

<mytype StringValue="somevalue" xmlns="http://datacontract" />

And here is the xml from the message once it’s passed through the service from BTS:-

< mytype StringValue="somevalueSuffix" xmlns="http://datacontract" />


Here is the schema that describes the type (MyType) this is the “Reference.xsd” file which the web message types reference in BTS, note that somehow(?) BTS knows to create the type “properly”

<?xml version="1.0"?>
<xs:schema xmlns:tns="http://datacontract" elementFormDefault="qualified" targetNamespace="http://datacontract" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="MyType" nillable="true" type="tns:MyType" />
  <xs:complexType name="MyType">
    <xs:attribute name="StringValue" type="xs:string" />
  </xs:complexType>
</xs:schema>


The "somehow" is the bit that maybe we should take more time to understand, but there is always a balance in how far to analyze!

Related: Yossi post on SO