Welcome to Windows Communication Foundation (WCF)
Top Tasks :

WCF Team Bloggers

Browse by Tags

All Tags » Proxies » Indigo   (RSS)

  • Getting Better Time Formats

    Orcas introduced a new DateTimeOffset class that is easier to use for representing absolute times than the original DateTime class. However, if you run svcutil on a contract that contains a DateTimeOffset, you'll get an ugly generated structure because DateTimeOffset isn’t recognized as a natively supported type by the system. A new class is generated by svcutil to match the schema for DateTimeOffset in the metadata. namespace System { using System.Runtime.Serialization; [DebuggerStepThroughAttribute()] [GeneratedCodeAttribute( "System.Runtime.Serialization" , "3.0.0.0" )] [DataContractAttribute(Name= "DateTimeOffset" , Namespace= "http://schemas.datacontract.org/2004/07/System" )] public partial struct DateTimeOffset : IExtensibleDataObject { private ExtensionDataObject extensionDataField; private DateTime DateTimeField; private short OffsetMinutesField; public ExtensionDataObject ExtensionData { get { return this .extensionDataField; } set { this .extensionDataField = value ; } } [DataMemberAttribute(IsRequired= true )] public DateTime DateTime { get { return this .DateTimeField; } set { this .DateTimeField = value ; } } [DataMemberAttribute(IsRequired= true )] public short OffsetMinutes { get { return this .OffsetMinutesField; } set { this .OffsetMinutesField = value ; } } } } There's a new option on svcutil, /targetClientVersion:Version35, that can be used to indicate that code generation should use new features in Orcas. As far as I know, there are three places where this option makes a difference. DateTimeOffset is automatically added as a known type when referenced Asynchronous methods are generated using the Orcas event-based asynchronous model Additional LINQ types are supported during schema import for XmlSerializer Next time: Read Only Data Members Read More...
  • Avoiding Infinite Schema Chains

    I was working on some services with recursive data structures when I noticed that there were a few cases where I would get crashes while trying to generate proxy classes. The problems seemed to be around types that were a sequence of instances of themselves. That looks like this: < xs:schema xmlns:tns ="http://test" targetNamespace ="http://test" xmlns:xs ="http://www.w3.org/2001/XMLSchema" > < xs:complexType name ="CircularList" > < xs:sequence > < xs:element minOccurs ="0" maxOccurs ="unbounded" name ="CircularList" nillable ="true" type ="tns:CircularList" /> </ xs:sequence > </ xs:complexType > < xs:element name ="CircularList" nillable ="true" type ="tns:CircularList" /> </ xs:schema > When using message contracts I was able to fix the problem by moving the minOccurs and maxOccurs from the element to the containing sequence. This didn't change the meaning of the type when I examined the proxy. With data contracts, trying the same fix caused proxy generation to go into an infinite loop rather than crash, but that's not much of an improvement. The data contract serializer worked best when I defined an intermediate type to represent the sequence rather than directly including the list type in itself. These all look like bugs with schema analysis. Next time: Waiting for Ready Channels Read More...
  • 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...
  • Finding a Client Channel

    Where can I get the IContextChannel that OperationContextScope requires? OperationContextScope allows you to create a temporary scope in which context for a service operation can build up before and after the operation is actually called. The constructor for OperationContextScope takes an instance of IContextChannel, which is a type that you've probably never seen before. Why are you expected to have this unknown type? It's because you have instances of it floating around all the time even though there's no particular hint of this. There are different ways to get an IContextChannel depending on whether you're using a proxy generated by svcutil or a proxy generated at runtime. In either case, pretend that I've got service contract called IService with a single method called Foo. When I generate a service client using svcutil, the client object has a member called InnerChannel that works as an IContextChannel. ServiceClient client = new ServiceClient(binding, new EndpointAddress(address)); using ( new OperationContextScope(client.InnerChannel)) { WebOperationContext.Current.OutgoingRequest.Headers[ "X" ] = "from compiled proxy" ; client.Foo(); } client.Close(); Otherwise, if I'm using a ChannelFactory to create the proxy at runtime, the channel that I get back happens to be an IContextChannel as well. ChannelFactory<IService> factory = new ChannelFactory<IService>(binding); factory.Open(); IService proxy = factory.CreateChannel( new EndpointAddress(address)); using ( new OperationContextScope((IContextChannel)proxy)) { WebOperationContext.Current.OutgoingRequest.Headers[ "X" ] = "from runtime proxy" ; proxy.Foo(); } factory.Close(); Next time: Standards Guide Read More...
  • Adding Headers to a Call (HTTP Version)

    Yesterday I talked about adding SOAP headers to an outgoing request using a variety of different methods. The most straightforward method was to create an OperationContextScope in your application code to establish an OutgoingMessageHeaders collection. Although HTTP headers are similar in spirit to SOAP headers, manipulating an HTTP header through code looks a bit different. SOAP headers are elevated to a special significance in the programming model. Everything else, including HTTP headers, is relegated to a general-purpose but distinctly second-class collection of message properties. On the OperationContextScope you'll find a parallel OutgoingMessageProperties collection that can be used for HTTP headers. In Orcas, the plain OperationContext also works as a WebOperationContext that gives the same first-class programming model to HTTP headers as you get with SOAP headers. Here's a comparison of the two approaches. using ( new OperationContextScope((IClientChannel)proxy)) { HttpRequestMessageProperty requestProperty = new HttpRequestMessageProperty(); requestProperty.Headers[ "X-header" ] = "value1" ; OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestProperty; proxy.Operation(); } using ( new OperationContextScope((IClientChannel)proxy)) { WebOperationContext.Current.OutgoingRequest.Headers[ "X-header" ] = "value2" ; proxy.Operation(); } Next time: Configuring SSL Host Headers Read More...
  • Adding Headers to a Call

    How do I add SOAP headers to an outgoing request? There are a few different ways to add headers to a message depending on how you need to control the header content and where you need to insert the header. I like to think of these methods as being split among the application, the proxy, and the protocol. In your application code you can create an OperationContextScope around the request in order to change some of the request properties. Inside an OperationContextScope, you have a valid instance of OperationContext.Current, which allows manipulation of the message headers through the OutgoingMessageHeaders collection. Use of this method deeply bakes control of the headers into the application code. You would have to be responsible for copying the appropriate code wherever it was needed. Now, assume that you want the header to be present whenever you're talking to a particular service and the header value can be determined consistently. Rather than having to put the same code at each call site, you can centralize the code in the proxy or in a protocol. The simplest way to centralize header manipulation in the proxy is to create an instance of the IClientMessageInspector and attach that to the proxy. This gives you a hook for every message going through the proxy to modify the message headers. An instance of IChannel similarly gives you a hook for every message going through the channel stack to modify the message headers. Using the Message Interceptor sample gives you an extensibility point in the binding that is similar in spirit to the extensibility point provided by a message inspector. The primary difference from your perspective of a message inspector and a channel is a matter of timing. A message inspector always runs before any of the protocols in the binding while a channel can be positioned precisely in the protocol stack. In most cases you don't need precise positioning, so you should go with the simpler approach. Next time: Adding Headers to a Call (HTTP Version) Read More...
  • Faster Known Types in Orcas

    When I talked about some of the enhancements in Orcas, I left out a performance improvement for services that use a large number of known types. There are various ways of defining the known types for a service operation, and all of the known type collections are aggregated together for use the first time that an unknown type needs to become a known type. The performance of that first call grew increasingly worse rather quickly as the number of known types increased, although the performance of successive calls was not affected. This slowdown was fixed in Orcas (you don't need Orcas SP1 to get it). If you are seeing a pattern of slow first calls that is improved by not declaring known types, then you may want to see if installing Orcas makes your first call faster. Next time: Common Setup Tasks Read More...
  • Customizing Proxy Configuration

    In the past I've written about overriding ApplyConfiguration on a service to take control of the configuration process. There is a similar technique that you can use for client proxies although getting started is not quite as obvious. I'll talk today about the typed proxies generated by constructing a ChannelFactory<T>. When you're using ChannelFactory<T>, you are midway between the tool-based automatic proxy generation and the channel-based manual proxy construction. Everything here also applies to the base ChannelFactory if you don't need the supplied type parameter. On ChannelFactory there are two interesting methods that you can override in subclasses. protected virtual void ApplyConfiguration( string configurationName); protected abstract ServiceEndpoint CreateDescription(); ApplyConfiguration is similar in spirit to the ApplyConfiguration on a service but has a little bit of a different interaction with CreateDescription. All of the heavy lifting for building the endpoint (the address, binding, and contract) is done in CreateDescription. All that is done in ApplyConfiguration is to load behaviors for the client proxy from configuration. Since ApplyConfiguration is not as interesting on the client, there are going to be more cases where you need to override both ApplyConfiguration and CreateDescription. If you only need to fix up the generated proxy, then you may be able to use the base implementation for both ApplyConfiguration and CreateDescription, and apply your changes to the description in an override of ApplyConfiguration. If you want to make more extensive changes to the configuration or client proxy generation process, then you're less likely to be able to use the base implementation of ApplyConfiguration and may need to override CreateDescription as well. Next time: Pointing to External Metadata Read More...
  • Generating Types with Lists

    I have a data contract that contains a collection type but the generated proxy appears as an array. How can I make the proxy use a collection type as well? I've talked in the past about how the representation of a type in metadata is decoupled from the CLR representation of a type in the service. For example, if I have a data contract that uses a List: [DataContract] class Data { [DataMember] public List< string > data; } Then, the metadata representation of this data contract is actually described as an array because arrays are the only primitive type for collections in 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:import namespace ="http://schemas.microsoft.com/2003/10/Serialization/Arrays" > < xs:complexType name ="Data" > < xs:sequence > < xs:element xmlns:q1 ="http://schemas.microsoft.com/2003/10/Serialization/Arrays" minoccurs ="0" name ="data" nillable ="true" type ="q1:ArrayOfstring" > </ xs:element > </ xs:sequence > < xs:element name ="Data" nillable ="true" type ="tns:Data" > </ xs:element > </ xs:complexType ></ xs:import ></ xs:schema > However, just as the metadata representation isn't coupled to the service, the metadata representation also isn't coupled to the client. You can on the client generate proxies with any type for this collection that similarly can be serialized or deserialized to an array. The mechanism for doing this with svcutil.exe is the /ct switch. The /ct switch, which stands for collectionType, allows you to give a qualified type name that is used for collection data types when generating a proxy. As an example, to get back to the original collection class used by the server, the proxy would need to be constructed using /ct:System.Collections.Generic.List`1 as the option passed to svcutil.exe. However, you could leave the proxy using arrays or provide a different collection class such as /ct:System.Collections.ObjectModel.Collection`1 and with any of these configurations the proxy would be able to exchange messages with the server. Next time: Setting the Configuration Name 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...
  • Manual Context Management

    How do I manually manage the context when sharing a client object? The default mode when using a context binding is for the context to be managed internally by the context channel underneath the client proxy. This is similar to how by default cookies are managed by an HTTP channel to send and receive cookie context. With an HTTP channel you can disable automatic cookie management and control the context yourself. There is a similar process that you can use to take control for a context binding. Here's a comparison of the two processes. You can get the code for HTTP by using the link above and with the further details on custom cookie handling so I won't print it again. With HTTP, you first need to turn off automatic cookie handling by setting the AllowCookies property on the HTTP transport binding element to false. With a context binding, you first need to turn off automatic context handling by setting the Enabled property on the context manager to false. IContextManager contextManager = channel.GetProperty<IContextManager>(); contextManager.Enabled = false ; Then, for HTTP you attach an HttpRequestMessageProperty that contains the desired cookies to a message using an OperationContextScope. With a context binding, you use the same OperationContextScope approach but attach the appropriate ContextMessageProperty instead. using ( new OperationContextScope(client.InnerChannel)) { ContextMessageProperty contextProperty = new ContextMessageProperty(contextData); OperationContext.Current.OutgoingMessageProperties[ContextMessageProperty.Name] = contextProperty; client.DoOperation(); } Next time: Messaging Additions in Orcas Read More...
  • Passing Around Endpoints

    Can I construct a proxy object on one machine and pass it to another? No, there's no concept in WCF of sending a fully constructed proxy object from one place to another. Consider that there are two different things that "passing" a proxy object could mean: passing a reference to the object or passing a value by constructing an equivalent object in another location. If you passed a proxy by reference, then what you really would be doing is sending messages from your local machine to the remote machine on each invocation, to be sent by the real proxy. However, that's just a service that forwards messages between the two proxies, and that's something that you can build yourself. If you passed a proxy by value, then what you really would be doing is sending some description of the proxy to be created locally. Again though, that is something that you can build yourself by passing the endpoint description for the proxy you want to recreate and constructing a local proxy factory. Next time: One Shot Serialization 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...
  • A Proxy Proxy Factory

    I have a system that sometimes uses a fast local object and sometimes needs to communicate over a network. I have built a proxy object that wraps the proxy factory for creating typed proxies together with a proxy factory for creating local objects. Are there any downsides to this strategy? Historically, there have been a number of attempts to hide whether objects are local or remote from programmers. These attempts have had varying degrees of success. Ultimately, the sticky issue is that it's difficult to prevent network abstractions from leaking. A leaky abstraction allows the environmental details that the abstraction is supposed to be hiding surface into the calling code. Like a leaky basement, a leaky abstraction can lay in wait for a long time before you realize that it's a problem. Problems with leaky abstractions show up when the abstraction author tries to make the surface layer completely seamless. Programmers love seamless abstractions because it makes their code very simple. However, implementers haven't yet figured out how to make any interesting abstraction be truly seamless. WCF attempts to deal with this issue by defining standard behaviors for channels. These standard behaviors are an escape valve so that when the abstraction leaks, it can leak in a controlled manner. For example, channels allow almost any operation to fail but restrict the exception types that the implementer can use and gives those types particular meanings. Or, channels require the programmer specify timeouts and quotas even when the application would rather trust the other side to behave. Or, and this is the most relevant one to the original question, channels require that the sender and receiver decouple their view of message data. Decoupling the sender and receiver means that there can be no way for the sender to modify the receiver's view of the data after a message is sent. In practical terms, achieving this decoupling requires that the data almost always be copied even when using direct object calls. While you can shortcut a lot of things with local objects, the system can't guarantee the abstraction if you bypass channels. There's not a big speed difference between a channel optimized for local communication and a local object that obeys all of the channel rules. Another way of saying that is that channels are extremely cheap if they don't have underlying network resources. There's currently no channel truly optimized for local communication although the named pipe Read More...
  • Sharing Contracts Across Services

    I've deployed several services that share some of their data contracts. When I build a client application that calls more than one of the services, each service contributes a copy of the data contract during proxy generation. This causes a compile error because the same type is defined multiple times. How do I resolve the conflict? Assuming that you don't want to change the services, there are four obvious ways to fix the conflict. Three of the ways are mostly manual and one of the ways is mostly automatic. Manual resolution: split up the client application into separate compile units so that each of the compile units references only one of the services. This method is the most invasive and tedious solution. Manual resolution: edit the output of proxy generation to place each service proxy in its own namespace. This method requires very little work but makes writing the client application inconvenient because types need to be namespace qualified and passing objects between different services becomes harder. Manual resolution: edit the output of proxy generation to remove extra copies of types. This method requires the most work every time you want to regenerate the proxies but gives you the most freedom for building the client application the way you want. Automatic resolution: treat proxy generation as an incremental process and use svcutil /r to reference type assemblies generated by earlier processed services. This method is possible to fully automate but introduces complications into the proxy generation process. This method is the best long-term solution but requires infrastructure work that may be too much overhead for smaller projects. Next time: Cleaning up Async Read More...
More Posts Next page »

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