|
|
Browse by Tags
All Tags » Service Model (RSS)
-
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...
|
-
Some tips for building support for versioning into the naming of data contracts. First, the primary route for versioning should be through the namespace part of the contract rather than the member name part of the contract. Versioning the contract through member names tends to leak across the service boundary more forcefully. The programming experience of the service often makes a member name directly visible while a namespace is more or less invisible. Second, choose a single consistent scheme for identifying the version. Two popular schemes are the date of the contract and a sequential numbering system of major and minor versions. Both schemes provide the basic element required of a versioning identity, which is an unambiguous total order among the different versions. However, multiple schemes should not be mixed together for a single contract and preferably not for a single system as well. The date scheme, http://company.com/year/month/name, has issues around granularity but can be very evocative since you probably already associate dates in your mind with other events. The issue with granularity is that you have to plan ahead for a maximum update frequency. In the previous example, two updates in the same month would collide with the same name, suggesting that a contract that is updated frequently might include additional levels of refinement, such as the day of the month. However, unnecessarily fine granularity makes the name cumbersome. The numbering scheme, http://company.com/major/minor/name, gives less of a clue about what the version corresponds to but has fewer issues with granularity. Updates can happen as frequently as you want since you can just keep picking new numbers. However, you still have to give some thought to granularity when deciding how many numbering components to include. For example, a single version number may be sufficient if no distinction is needed between major and minor updates. Next time: Finding a Client Channel Read More...
|
-
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...
|
-
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...
|
-
How do I get access to the service instance from inside of a service operation? Assuming that you're executing somewhere that a service operation is meaningful, you'll have access to the service operation context OperationContext.Current. From the service operation context you can get the service instance context and from the instance context you can ask it to GetServiceInstance. How does the context know how to give you a service instance? Early on in the execution of the operation, the service operation context was created. Shortly thereafter, a service instance context was created or one was reused if we happened to have it lying around and the service instance context was attached to the service operation context. The service instance context had attached to it an IInstanceProvider, which is the extensibility point for managing instances. There are a variety of instance providers for different uses that come with the system. An instance provider is able to cough up an instance based on a service instance context and optionally the message that is being processed. Next time: Building with Encoders Read More...
|
-
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...
|
-
I've previously talked about using WSDL extensions to provide custom modifications to the WSDL import and export process. Making modifications to an existing WSDL document or to the existing WSDL processing is a great way to make small changes when the default behavior almost gets you to where you want. However, you may have noticed that these mechanisms are a little cumbersome for providing a complete overhaul of metadata; that's because a WSDL extension is primarily a hook to supply user extensibility before or after certain steps in the WSDL import or export process. When making large-scale modifications, another tool in your toolbox is to entirely jump out of the built-in WSDL processing. The ExternalMetadataLocation property on the ServiceMetadataBehavior is one of the ways that you can opt out of the standard process. By supplying an external metadata location, you provide to clients a reference to an independent location for retrieving metadata. You might base the contents of that external metadata on the metadata files normally generated by the service (the disco.exe tool in the Windows SDK is a good way to generate all of those metadata files at once) or you might go completely wild and make the external metadata have no relationship at all to the normally generated metadata files. Next time: Avoid Exceptions in Faults Read More...
|
-
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...
|
-
What's the difference between the Name and ConfigurationName on service contracts and behaviors? The Name property sets the name of the service in metadata while the ConfigurationName property sets the name of the service in configuration. Metadata is the part of the service description that is transmitted to others when they interrogate your service. Configuration is the purely local settings for controlling the service. Since Name is more commonly used, I'll just run through a quick example focusing on setting ConfigurationName instead. Here I've got a service contract and implementation setting both the Name and ConfigurationName properties. [ServiceContract(Name= "NotIService" , ConfigurationName= "IService" )] public interface IMyService { [OperationContract] void DoNothing(); } [ServiceBehavior(Name = "NotService" , ConfigurationName = "Service" )] public class MyService : IMyService { public void DoNothing() { } } In my app.config file, the way I'd refer to that service and service endpoint is by the ConfigurationName. < service name ="Service" > < endpoint address ="http://localhost:8000/" binding ="basicHttpBinding" bindingConfiguration ="myBindingConfiguration" contract ="IService" /> </ service > On the other hand, everywhere in the metadata, generated proxy, and even the generated proxy configuration file the service will appear as NotIService and NotService. Next time: Disabling the Visual Studio Service Host Read More...
|
-
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...
|
-
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...
|
-
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...
|
-
Why do some serialization errors when sending a response not result in a fault being returned to the client? In the typical service operation, sending a response is comprised of returning the appropriate information from the service method to construct a message. You might think of that response as a single operation but performing the response is divided up into many different acts. As an example of a division you could make, one way to split a response into separate acts is to say that there is an act of thinking about whether to respond and what the response contains, an act of constructing the response, and an act of transmitting the response. There is a moment in time during those acts in which the system moves from thinking about a response to actually carrying out the response. Because there are many different ways to divide the operation into a series of acts, that moment in time does not always make up a clear line separating one part of the system from another. However, if you get to the point where you've started carrying out the response, then you've attempted to respond. As an example in WCF terms, RequestContext.Reply is one key point at which the act of responding is realized. There are a variety of different messaging patterns; the ones that you should be familiar with are the one-way, request-reply, and duplex patterns. One-way patterns don't have a response so we can ignore those in this discussion. Request-reply patterns have the property that for any given request there can only ever be one response. If you think about the act of responding now, then there is a point at which your single attempt to respond has fail. This point is totally divorced from what takes place on the wire. It instead is an internal artifact of how the particular system divides the response into acts. A failure before that point would permit an error response to be sent instead while a failure after that point means that no response can ever be sent. Next time: Hosting Identity Read More...
|
-
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...
|
-
My application needs to process messages from a queue in-order but multiple messages are being read at once. How do I make the service only use a single reader? There are two modes that control how many requests a service can process at once. InstanceContextMode controls when a new instance of your service is created; ConcurrencyMode controls the threading model for the service. When the InstanceContextMode is PerCall (or PerSession- the two are the same if you don't actually have a session), multiple instances of your service will be created with their own reader thread, up to a limit on the number of simultaneous instances. When the ConcurrencyMode is Multiple, multiple reader threads will be passing messages to a service instance, up to a limit on the number of simultaneous calls. If you want your service to be sequentially invoked with each of the messages in the queue, then you need to set both InstanceContextMode and ConcurrencyMode to Single. Next time: Getting Caught by Loopback Read More...
|
|
|
|