Welcome to Windows Communication Foundation (WCF)
Top Tasks :

WCF Team Bloggers

Browse by Tags

All Tags » Proxies » Indigo » Contracts   (RSS)

  • System Types in Metadata

    It's bad practice to use system types when defining an operation contract. A system type is often a complex composition of primitive types that has no direct analog in other implementations. By using a system type, you bind your service to the particular implementation used by that type, which effectively ends any chance of having an easily interoperable service. For example, a contract containing an IPAddress seems innocuous. [OperationContract] string LookupHostName(IPAddress address); However, that reference translates into a significantly sized chunk of metadata for defining the IPAddress type. < xs:schema xmlns:tns ="http://schemas.datacontract.org/2004/07/System.Net" elementFormDefault ="qualified" targetNamespace ="http://schemas.datacontract.org/2004/07/System.Net" xmlns:xs ="http://www.w3.org/2001/XMLSchema" > < xs:import namespace ="http://schemas.datacontract.org/2004/07/System.Net.Sockets" /> < xs:import namespace ="http://schemas.microsoft.com/2003/10/Serialization/Arrays" /> < xs:complexType name ="IPAddress" > < xs:sequence > < xs:element name ="m_Address" type ="xs:long" /> < xs:element xmlns:q1 ="http://schemas.datacontract.org/2004/07/System.Net.Sockets" name ="m_Family" type ="q1:AddressFamily" /> < xs:element name ="m_HashCode" type ="xs:int" /> < xs:element xmlns:q2 ="http://schemas.microsoft.com/2003/10/Serialization/Arrays" name ="m_Numbers" nillable ="true" type ="q2:ArrayOfunsignedShort" /> < xs:element name ="m_ScopeId" type ="xs:long" /> </ xs:sequence > </ xs:complexType > < xs:element name ="IPAddress" nillable ="true" type ="tns:IPAddress" /> </ xs:schema > Notice the inclusion of System.Net.Sockets.AddressFamily. This schema is only the tip of the iceberg. It continues on and becomes much, much worse. Nevertheless, there are times when you can accept putting interoperability aside and have a reason to bind your service to a system type. The metadata still leaves you with the problem of generating a working proxy. Although some system types are recognized and filtered out, other types slip through and will lead to compilation errors due to conflicting type definitions. The standard referencing mechanism of svcutil works with system types as well as your types. You just need to find and point svcutil at the appropriate dll so that it can compute the types to exclude. Since IPAddress is defined in system.dll, if you wanted to resolve a conflict, you Read More...
  • Private Data Members

    Why does a data contract with private or internal members generate a proxy with public fields? The obvious answer is that the representation for data contracts doesn't contain information about member visibility but that just leads to the question of why the information isn't preserved by the representation. If we take a data contract that contains both public and private members, [DataContract] class Data { [DataMember] public int i; [DataMember] private string s; } Then, the type representation used to generate a proxy is based on an XML schema. < xs:schema xmlns:tns ="http://schemas.datacontract.org/2004/07/" elementFormDefault ="qualified" targetNamespace ="http://schemas.datacontract.org/2004/07/" xmlns:xs ="http://www.w3.org/2001/XMLSchema" > < xs:complexType name ="Data" > < xs:sequence > < xs:element minOccurs ="0" name ="i" type ="xs:int" /> < xs:element minOccurs ="0" name ="s" nillable ="true" type ="xs:string" /> </ xs:sequence > </ xs:complexType > < xs:element name ="Data" nillable ="true" type ="tns:Data" /> </ xs:schema > A schema is a type system that is independent of the type system used by the proxy and has no concept of member visibility. However, by saying that a member is part of the data contract, you've effectively said that that member is as intrinsically part of the data as any other public facing member. Member visibility is a facet of information hiding to suppress details that are not needed by other parts of the system, but a data member by definition is needed by other parts of the system. Therefore, regardless of how one of the parties has chosen to represent that data, it's more likely than not that the other side will need to manipulate that data to uphold the contract. Next time: Generating Types with Lists Read More...
  • Differences in Guid Serialization

    Why do the guids in my contract turn into strings when generating a client? You're probably mixing different types of serializers between the client and service. There's nothing wrong with this and the generated client will work correctly but you don't get the user-friendly types. To see why, let's look at the metadata. A guid in a contract with the DataContractSerializer generates a type in the http://schemas.microsoft.com/2003/10/Serialization/ namespace that looks like this: < xs:element name ="guid" nillable ="true" type ="tns:guid" /> < xs:simpleType name ="guid" > < xs:restriction base ="xs:string" > < xs:pattern value ="[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}" /> </ xs:restriction > </ xs:simpleType > On the other hand, a guid in a contract with the XmlSerializer generates a type in the http://microsoft.com/wsdl/types/ namespace that looks like this: < xs:simpleType name ="guid" > < xs:restriction base ="xs:string" > < xs:pattern value ="[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}" /> </ xs:restriction > </ xs:simpleType > These generated types are needed because a guid is not a primitive type. DataContractSerializer came after XmlSerializer so it recognizes both definitions but XmlSerializer has to rely on the schema when it sees a DataContractSerializer guid. Since the schema is based on a string type, the generated client field is a string. The same thing happens with other serializers that don't know how to map a particular schema pattern to a user-friendly type. Next time: TCP Throttling Read More...
  • Calling Services Without a Contract

    Contracts are largely an illusion about the kinds of messages that can be exchanged between a client and a server. From a practical perspective, a service can describe itself using any contract it likes but the true measure of compatibility comes from being able to exchange data. The easiest way to experience this observation is to build and send some messages without using any contracts. I've talked a lot in the past about sending messages directly using channels, which are a very thin wrapper around the semantics of sending and receiving. You don't have to go all the way to using channels to experience contract-less programming though. The standard ChannelFactory class for building proxy objects has a limited built-in knowledge about the IRequestChannel shape. In essence, this is a way to send messages in a contract-oriented programming model without actually using contracts. Quite a few systems require sending and receiving messages but lack any support for dealing with contracts. It can be difficult to acclimate yourself to this style of programming, but it can be done to a large degree without having to give up the conveniences of a nice programming model. Next time: Serializing UniqueId Read More...
  • ChannelFactory Contract and Generated Types

    Last Monday we had an introduction to the contract associated with a ChannelFactory . Today's article is a tangential continuation of that discussion. Let's take a look at the typed version of the contract for the same echo service that we had before. [ServiceContract(Namespace= "" )] public interface IService { [OperationContract(Action= "Echo" )] string Echo( string text); } public class Service : IService { public string Echo( string text) { return text; } } I've set the namespace and action to specific values here just to make the service call simpler. Now, here's a service that uses a metadata exchange connection to get the service metadata for the IService contract. Using the metadata, we can create the same IRequestChannel interface to the service that we had last time. What is different here of course is that the format of the message has to change because the service really is using a typed contract. string uri = "http://localhost:8000/service" ; string mex = "http://localhost:8000/mex" ; Binding binding = new CustomBinding( new HttpTransportBindingElement()); ServiceHost service = new ServiceHost( typeof (Service)); service.Description.Behaviors.Add( new ServiceMetadataBehavior()); service.AddServiceEndpoint( typeof (IService), binding, uri); service.AddServiceEndpoint( typeof (IMetadataExchange), binding, mex); service.Open(); MetadataExchangeClient client = new MetadataExchangeClient( new EndpointAddress(mex)); MetadataSet metadata = client.GetMetadata(); WsdlImporter importer = new WsdlImporter(metadata); ServiceEndpointCollection endpoints = importer.ImportAllEndpoints(); ChannelFactory<IRequestChannel> factory = new ChannelFactory<IRequestChannel>(endpoints[0]); IRequestChannel channel = factory.CreateChannel(); XmlReader reader = XmlReader.Create( new StringReader( "<Echo><text>a message</text></Echo>" )); Message message = Message.CreateMessage(binding.MessageVersion, "Echo" , reader); Console.WriteLine(channel.Request(message)); message.Close(); factory.Close(); service.Close(); The attempt to invoke the service fails with a different InvalidOperationException this time: "Instance of MessagePartDescription Name='text' Namespace='' cannot be used in this context: required 'Type' property was not set." Why is there a missing Type property for the contract definition? The lack of types is because the dynamic metadata generation call does not create the runtime contract types needed to describe the typed message Read More...

Copyright © 2006 Microsoft Corporation. All Rights Reserved. | Terms of Use | Privacy Statement | Contact Us