Welcome to MSDN Blogs Sign in | Join | Help

Dynamic Proxy and Memory Footprint

A while back I published a post about dynamic programming with WCF using the dynamic proxy library that allows you to create WCF client dynamically at runtime. Thank you for using the sample and sending your comments. Frequently I get feedback about the memory usage of the applications using this library. It seems that if you create many dynamic proxies during the lifetime of your application, the memory footprint keeps growing. This is a common problem that you encounter when using any dynamically created assembly in your application. You need to aware of this issue and take appropriate measure to make sure that you are not leaking memory.

The dynamic proxy creates the proxy assembly at runtime; this assembly is stored in a temporary location on your file system and loaded in the memory of your app domain. Now if you do not need the dynamic proxy anymore, there is no way to unload this temporary assembly. There isn’t. You need to unload the entire app domain. So what should you do if you need to create and destroy many dynamic proxies in your application? Simply, isolate the code that is creating and using the dynamic proxy and run it in a different app domain. Once you are done, simply unload that app domain.

Here is a simple modification to the example program from the dynamic proxy library that runs the dynamic client in a new app domain. You will notice that the memory footprint of the application remains the same over large number of iterations.

class Program

{

    public static void Main(string[] args)

    {

        string serviceWsdlUri = "http://localhost:8080/WcfSamples/DynamicProxy?wsdl";

        if (args.Length > 0)

        {

            serviceWsdlUri = args[0];

        }

 

        for(int i = 0; i < 1000; i++)

        {

            AppDomain proxyDomain = AppDomain.CreateDomain("ProxyExecutionDomain");

            DynamicClient dynamicClient = new DynamicClient(serviceWsdlUri);

            proxyDomain.DoCallBack(

              new CrossAppDomainDelegate(dynamicClient.CrossAppDomainCallback));

            AppDomain.Unload(proxyDomain);

            GC.Collect();

        }

    }

}

 

[Serializable]

class DynamicClient

{

    string serviceWsdlUri;

 

    public DynamicClient(string serviceWsdlUri)

    {

        this.serviceWsdlUri = serviceWsdlUri;

    }

 

    public void CrossAppDomainCallback()

    {

        // create the dynamic proxy factory, that downloads the service metadata

        // and create the dynamic factory.

        Console.WriteLine("Creating DynamicProxyFactory for " + serviceWsdlUri);

        DynamicProxyFactory factory = new DynamicProxyFactory(serviceWsdlUri);

...

 

Posted by vipulm | 9 Comments

WS-Discovery Sample Implementation

The sample implementation of WS-Discovery protocol is now available at http://code.msdn.microsoft.com/Project/Download/FileDownload.aspx?ProjectName=netfxsamples&DownloadId=3940

Using this sample you can publish and find WCF services using WS-Discovery protocol. Please either submit your feedback directly on the sample page or send it to me (vipul.modi@microsoft.). Your feedback will help in designing the discovery APIs for the next release.

 

Posted by vipulm | 28 Comments

Dynamic Programming with WCF

Update: Please check out the post on memory footprint and dynamic proxy (http://blogs.msdn.com/vipulmodi/archive/2008/10/16/dynamic-proxy-and-memory-footprint.aspx

-----

Ever wonder what it would like to go from WSDL to code at runtime? Check out my WCF Dynamic Proxy tool that I posted on the wcf.nefx3.com community site. The WCF Dynamic Proxy downloads the WSDL, generate the code, compile the code and then allow you to invoke the web service operations using reflection, all at runtime. Here is the readme from the tool: http://code.msdn.microsoft.com/Project/Download/FileDownload.aspx?ProjectName=netfxsamples&DownloadId=3939

The DynamicProxy allows you to create the dynamic WCF client at runtime by specifying the WSDL URI of the service. The DynamicProxy does not depend on the precompiled proxy or configuration. The DynamicProxy uses the MetadataResolver to download the metadata from the service and WsdlImporter to create the contract and binding at runtime. The compiled dynamic proxy can be used to invoke the operations on the service using reflection.

The example shows how you can the dynamic proxy to invoke operations that use simple types and complex types. The flow of usage is as following.

1. Create the ProxyFactory specifying the WSDL URI of the service.

    DynamicProxyFactory factory = new DynamicProxyFactory("http://localhost:8080/WcfSamples/DynamicProxy?wsdl");

2. Browse the endpoints, metadata, contracts etc.
    factory.Endpoints
    factory.Metadata
    factory.Contracts
    factory.Bindings

3. Create DynamicProxy to an endpoint by specifying either the endpoint or
   contract name.
    DynamicProxy proxy = factory.CreateProxy("ISimpleCalculator");

    OR

    DynamicProxy proxy = factory.CreateProxy(endpoint);
   
  
4. Invoke operations on the DynamicProxy
    dobule result = (dobule)proxy.CallMethod("Add", 1d ,2d);

5. Close the DynamicProxy
    proxy.Close();

Posted by vipulm | 26 Comments

What's next

Every once in while after a big release, the question comes to your mind, what's next? Well for me it is WS-Discovery, yes I am going to work on design and implementation of the web service discovery APIs for our next release. I have been involved been Discovery for a while including the WS-discovery specification work. I plan to release the sample implementation of WS-Discovery on top of .NET 3.0 on the netfx3.com community site soon. I would love to hear about your disovery requirements and scenarios, so that we can consider them in our design.

Posted by vipulm | 0 Comments

Disabling JIT Debugging Dialog Box

When an executable throws unhandled exception by default JIT-debugging kicks in that allow you to attach a debugger to the dying process and investigate the issue. This useful feature can become annoying during the development where you just want to get the stack dump of the unhandled exception. The Visual Studio 2005 and Vista have JIT debugging enabled by default.

However it is really easy to turn it off. Change the value of the registry key  HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\DbgJITDebugLaunchSetting to 1 to get the stack dump, the process will be terminated after that.

For more information: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconenablingjit-attachdebugging.asp

Posted by vipulm | 3 Comments
Filed under:

Modifying the Body of the Message

The modification of the body is a common task required in many applications. The Message Fixer explained in "Fixed the Messages" article performs body modification before the after the request is received and before the response is sent on the server side or before sending the request and after receiving the response on the client side. As explained in the article for smaller message one can create the XMLDocument from the message body by getting the Body reader from the message as shown below.

 // Fix the request
XmlDocument doc = new XmlDocument();
doc.Load(request.GetBodyReader());
 

However recently while working on an Interop application I discovered that this quick an easy way of modifying the body of the messages did not work in cases when the body content had elements or attributes of QName type. Specifically if the namespace for the prefix used in the value of element or attribute was defined before the Body element. The XML processors typically do not poke at the values and hence they try their best to preserve the context at which the namespace was declared.  In my case I was creating XML document from the Body element onwards. Hence if the namespace declaration was before the Body element, i..e at the Envelope level, which XML document never saw, the re declaration of the namespace occurred. The namespace was re declared when it was first used in the name of the element or attribute and NOT when it was used as the value of the element or attribute.  For example, if an XML document was created from the Body reader of the Message object for the following message the namespace http://mynamespace was re declared after it was used in one of the attribute values. You can see how this can result in failure of parsing the values later on.

Original Message

 <s:Envelope xmlns:q="http://mynamespace" ...>
  <s:Body>
    <MyRequest xmlns="http://tempuri.org" MyAttr="q:myattrvalue">
      <q:value2> </q:value2>
    </MyRequest>
  </s:Body>
<s:Envelope>
 
Processed Message

<s:Envelope  ...>
  <s:Body>
    <MyRequest xmlns="http://tempuri.org" MyAttr="q:myattrvalue">
      <q:value2 xmlns:q="http://mynamespace"> </q:value2>
    </MyRequest>
  </s:Body>
<s:Envelope>

So how does on modifies the message? The easiest way to do this would be to preserve the complete message and then perform the modification on the body. Note that this is applicable to small messages that you can fit reasonably in memory.

  1. Write out the entire message as string
     StringBuilder builder = new StringBuilder();
     XmlWriter writer = XmlWriter.Create(builder);
     origMsg.WriteMessage(writer);
     writer.Close();
     string origMsgStr = builder.ToString()

  2. Perform the modifications on the string or create the DOM using XMLDocument.LoadXml method.
     XmlDocument origMsgDoc = new XmlDocument();
     origMsgDoc.LoadXml(origMsgStr);
        
     // modify the message
    

  3. Recreate the message
     XmlReader newMsgReader = XmlReader.Create(new StringReader(origMsgStr));
     Message newMsg = Message.CreateMessage(newMsgReader, int.MaxValue);
    

  4. Copy over the message properties
     newMsg.Properties.CopyProperties(origMsg.Properties);
Posted by vipulm | 0 Comments
Filed under:

Making it work - Fixing the Messages - Part 2

IProxyMessageInspector - Fixing Messages: Service Side

The article "Making it work - Fixing the Messages" describes how to fix the messages on the client side while trying to Interop with an alien service that sends invalid fault messages. This article describes fixing the messages either on the client side or service side. It also describes how to add these "MessageFixers" via config using the behavior extension feature of Indigo.

Just like IProxyMessageInspector that allows you to inspect and optionally modify the messages on the client side; the IStubMessageInspector allows you to inspect and optionally modify the messages on the service side. The IStubMessageInspector interface has the following methods.

    
    object AfterReceiveRequest(ref Message request, IProxyChannel channel, ServiceSite site);
    void BeforeSendReply(ref Message reply, object correlationState);

They are self explanatory, AfterReceiveRequest allows you to fix the requests received from the client before it is processed by the Indigo runtime. The object returned from this method will be returned as "correlationState" object in the BeforeSendReply method. This allows you to correlate request and responses. The BeforeSendReply allows you to fix the replies before they are sent to the client.

The IServiceBehavior implementation provides an opportunity to add message inspectors in the service processing pipeline. The easiest way to add a behavior to service or channel factory is via code as shown below. The service can have more than one endpoint. This example adds the IStubMessageInspector on all those endpoints.

  
    public class ServiceMessageFixer : IServiceBehavior, IStubMessageInspector
    {
        #region IServiceBehavior Members

        public void ApplyBehavior(ServiceDescription description, Collection<DispatchBehavior> behaviors)
        {
            foreach (DispatchBehavior dispatchBehavior in behaviors)
                dispatchBehavior.MessageInspectors.Add(this);
        }

        #endregion

Adding Message Fixers via Config

We add the message inspector as behavior of the channel or the service using IChannelBehavior and IServiceBehavior implementations respectively. Since the behaviors can be added via config, it is possible to add message inspector implementations via config. Hence adding message inspectors via config is same as adding custom behaviors via config.

The behaviors for the services and clients are specified under the "behaviors" section in the config. These behaviors are named and are referred by the "behaviorConfiguration" attribute on the "service" element.  The named behavior element lists the behaviors and their configuration. Each config section under the behavior element is handled by a BehaviorExtensionSection. The user defined config sections are registered by listed then under the behaviorExtensions element in extensions section.  

Following is the configuration for the custom behavior config handler that instantiates and adds the specified IServiceBehavior implementation to the service. The "type" attribute on the "CustomServiceBehavior" provides the IServiceBehavior implementation.    

  
    <system.serviceModel>

        <services>
            <service 
                serviceType="HelloWorld.HelloWorldService"
                behaviorConfiguration="ServiceBehavior">
                <endpoint address="http://localhost:8080/HelloWorld/Service"
                          bindingSectionName="wsProfileBinding"
                          contractType="HelloWorld.IHelloWorldService" />
            </service>
        </services>

        <behaviors>
            <behavior  
                configurationName="ServiceBehavior">
                <CustomServiceBehavior type="MessageFixer.ServiceMessageFixer, MessageFixer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
            </behavior>
        </behaviors>

        <extensions>
            <behaviorExtensions>
                <add name="CustomServiceBehavior" 
                    type="CustomBehaviorConfig.CustomServiceBehaviorSection,CustomBehaviorConfig, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
            </behaviorExtensions>
        </extensions>

    </system.serviceModel>

 

The Complete Example

The following sample demonstrate the implementation of IProxyMessageInspector, IStubMessageInspector for fixing the messages on the client side and service side respectively via code and config. The MessageFixer classes should be compiled in to the MessageFixer.dll assembly and the BehaviorConfig classes should be compiled in to CustomBehaviorConfig.dll assembly, as the App.config file for HelloWorld program that demonstrate the use of these message fixers uses these assembly names. Please do send your feedback!

Message Fixer Classes   - MessageFixer.dll

ClientMessageFixer.cs      
      
// Copyright Notice 
//
// Use of this sample code is subject to the terms specified at 
// http://www.microsoft.com/info/cpyright.htm
//

namespace MessageFixer
{
    using System;
    using System.Xml;
    using System.ServiceModel;

    public class ClientMessageFixer : IChannelBehavior, IProxyMessageInspector
    {
        #region IChannelBehavior Members

        public void ApplyBehavior(ChannelDescription description, ProxyBehavior behavior)
        {
            behavior.MessageInspectors.Add(this);
        }

        #endregion

        #region IProxyMessageInspector Members


        public object BeforeSendRequest(ref Message request, IProxyChannel channel)
        {
            // Decide if you need to fix the request
            // if "I need to fix the message" 
            {
                // Fix the request
                XmlDocument doc = new XmlDocument();
                doc.Load(request.GetBodyReader());

                // Perform the modification
                FixRequestMessage(doc);

                // Create new message
                XmlNodeReader reader = new XmlNodeReader(doc.DocumentElement);
                Message newMsg = Message.CreateMessage(request.Version,
                    null, reader);

                // Preserve the headers of the original message
                newMsg.Headers.CopyHeadersFrom(request);

                foreach (string propertyKey in request.Properties.Keys)
                    newMsg.Properties.Add(propertyKey, request.Properties[propertyKey]);

                // Close the original message and return new message
                request.Close();
                request = newMsg;
            }
            // end if - "I need to fix the message"

            // The returned value can be used for request-reply correlation.
            //
            // The return object from this method will be supplied as the 
            // correlationState parameter of the AfterReceiveReply method. 
            return null;
        }

        public void AfterReceiveReply(ref Message reply, object correlationState)
        {
            // Decide if you need to fix the reply
            // if "I need to fix the message" 
            {
                // Fix the reply
                XmlDocument doc = new XmlDocument();
                doc.Load(reply.GetBodyReader());

                // Perform the modification
                FixReplyMessage(doc);

                // Create new message
                XmlNodeReader reader = new XmlNodeReader(doc.DocumentElement);
                Message newMsg = Message.CreateMessage(reply.Version,
                    null, reader);

                if (reply.IsFault)
                {
                    MessageFault fault = MessageFault.CreateFault(newMsg);
                    newMsg.Close();

                    newMsg = Message.CreateMessage(reply.Version, fault,
                        null);
                }

                // Preserve the headers of the original message
                newMsg.Headers.CopyHeadersFrom(reply);

                foreach (string propertyKey in reply.Properties.Keys)
                    newMsg.Properties.Add(propertyKey, reply.Properties[propertyKey]);

                // Close the original message and return new message
                reply.Close();
                reply = newMsg;

            }

            // end if - "I need to fix the message"
        }

        private void FixRequestMessage(XmlDocument doc)
        {
            // Fix the request message here.
            Console.WriteLine("Fixing the request to be sent to the server.");
        }

        private void FixReplyMessage(XmlDocument doc)
        {
            // Fix the reply message here.
            Console.WriteLine("Fixing the reply received from the server.");
        }

        #endregion
    }
}

ServiceMessageFixer.cs

// Copyright Notice 
//
// Use of this sample code is subject to the terms specified at 
// http://www.microsoft.com/info/cpyright.htm
//

namespace MessageFixer
{
    using System;
    using System.Xml;
    using System.ServiceModel;
    using System.Collections.ObjectModel;

    public class ServiceMessageFixer : IServiceBehavior, IStubMessageInspector
    {
        #region IServiceBehavior Members

        public void ApplyBehavior(ServiceDescription description, Collection<DispatchBehavior> behaviors)
        {
            foreach (DispatchBehavior dispatchBehavior in behaviors)
                dispatchBehavior.MessageInspectors.Add(this);
        }

        #endregion

        #region IStubMessageInspector Members

        public object AfterReceiveRequest(ref Message request, IProxyChannel channel, ServiceSite site)
        {
            // Decide if you need to fix the request
            // if "I need to fix the message" 
            {
                // Fix the request
                XmlDocument doc = new XmlDocument();
                doc.Load(request.GetBodyReader());

                // Perform the modification
                FixRequestMessage(doc);

                // Create new message
                XmlNodeReader reader = new XmlNodeReader(doc.DocumentElement);
                Message newMsg = Message.CreateMessage(request.Version,
                    null, reader);

                // Preserve the headers of the original message
                newMsg.Headers.CopyHeadersFrom(request);

                foreach (string propertyKey in request.Properties.Keys)
                    newMsg.Properties.Add(propertyKey, request.Properties[propertyKey]);

                // Close the original message and return new message
                request.Close();
                request = newMsg;
            }
            // end if - "I need to fix the message"

            // The returned value can be used for request-reply correlation.
            //
            // The return object from this method will be supplied as the 
            // correlationState parameter of the BeforeSendReply method. 
            return null;
        }

        public void BeforeSendReply(ref Message reply, object correlationState)
        {
            // Decide if you need to fix the reply
            // if "I need to fix the message" 
            {
                // Fix the reply
                XmlDocument doc = new XmlDocument();
                doc.Load(reply.GetBodyReader());

                // Perform the modification
                FixReplyMessage(doc);

                // Create new message
                XmlNodeReader reader = new XmlNodeReader(doc.DocumentElement);
                Message newMsg = Message.CreateMessage(reply.Version,
                    null, reader);

                if (reply.IsFault)
                {
                    MessageFault fault = MessageFault.CreateFault(newMsg);
                    newMsg.Close();

                    newMsg = Message.CreateMessage(reply.Version, fault,
                        null);

                    Console.WriteLine("It is a fault !!!!!");
                }

                // Preserve the headers of the original message
                newMsg.Headers.CopyHeadersFrom(reply);

                foreach (string propertyKey in reply.Properties.Keys)
                    newMsg.Properties.Add(propertyKey, reply.Properties[propertyKey]);

                // Close the original message and return new message
                reply.Close();
                reply = newMsg;

            }

            // end if - "I need to fix the message"
        }

        private void FixRequestMessage(XmlDocument doc)
        {
            // Fix the request message here.
            Console.WriteLine("Fixing the request received from the client.");
        }

        private void FixReplyMessage(XmlDocument doc)
        {
            // Fix the reply message here.
            Console.WriteLine("Fixing the reply to be sent to the client.");
        }

        #endregion
    }
}

AssemblyInfo.cs

using System.Reflection;

[assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Custom Config Classes   - CustomBehaviorConfig.dll


CustomClientBehaviorSection.cs

// Copyright Notice 
//
// Use of this sample code is subject to the terms specified at 
// http://www.microsoft.com/info/cpyright.htm
//

namespace CustomBehaviorConfig
{
    using System;
    using System.Configuration;
    using System.ServiceModel;
    using System.ServiceModel.Configuration;

    /// <summary>
    /// Adds the custom behavior to the client. 
    /// </summary>
    class CustomClientBehaviorSection : BehaviorExtensionSection
    {
        [ConfigurationProperty("type")]
        public string TypeName
        {
            get
            {
                return (string)base["type"];
            }
        }

        protected override object CreateBehavior()
        {
            object behavior = null;

            if ((this.TypeName != null) && (this.TypeName.Trim().Length > 0))
            {
                Type channelBehaviorImplType = Type.GetType(this.TypeName, true);
                if (typeof(IChannelBehavior).IsAssignableFrom(channelBehaviorImplType))
                {
                    behavior = Activator.CreateInstance(channelBehaviorImplType);
                }
            }

            return behavior;
        }

        public override string ConfiguredSectionName
        {
            get { return "CustomClientBehavior"; }
        }
    }
}

CustomServiceBehaviorSection.cs

// Copyright Notice 
//
// Use of this sample code is subject to the terms specified at 
// http://www.microsoft.com/info/cpyright.htm
//

namespace CustomBehaviorConfig
{
    using System;
    using System.Configuration;
    using System.ServiceModel;
    using System.ServiceModel.Configuration;

    /// <summary>
    /// Adds the custom behavior to the service. 
    /// </summary>
    class CustomServiceBehaviorSection : BehaviorExtensionSection
    {
        [ConfigurationProperty("type")]
        public string TypeName
        {
            get
            {
                return (string)base["type"];
            }
        }

        protected override object CreateBehavior()
        {
            object behavior = null;

            if ((this.TypeName != null) && (this.TypeName.Trim().Length > 0))
            {
                Type serviceBehaviorImplType = Type.GetType(this.TypeName, true);
                if (typeof(IServiceBehavior).IsAssignableFrom(serviceBehaviorImplType))
                {
                    behavior = Activator.CreateInstance(serviceBehaviorImplType);
                }
            }

            return behavior;
        }

        public override string ConfiguredSectionName
        {
            get { return "CustomServiceBehavior"; }
        }
    }
}

AssemblyInfo.cs

using System.Reflection;

[assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]


HelloWorld Sample using MessageFixers


HelloWorld.cs

// Copyright Notice 
//
// Use of this sample code is subject to the terms specified at 
// http://www.microsoft.com/info/cpyright.htm
//

namespace HelloWorld
{
    using System;
    using System.Collections.ObjectModel;
    using System.Xml;
    using System.ServiceModel;

    [ServiceContract]
    public interface IHelloWorldService
    {
        [OperationContract]
        string Greetings(string name);
    }

    [ServiceBehavior]
    public class HelloWorldService : IHelloWorldService
    {
        public string Greetings(string name)
        {
            return "Hello " + name;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            StartService();
            StartClient();

            Console.WriteLine("\nPress Enter to exit.");
            Console.ReadLine();
        }

        private static void StartService()
        {
            HelloWorldService serviceObj = new HelloWorldService();
            ServiceHost<HelloWorldService> serviceHost =
                new ServiceHost<HelloWorldService>();

            // Uncomment the following line to add the ServiceMessageFixer via code
            // serviceHost.Description.Behaviors.Add(new MessageFixer.ServiceMessageFixer());

            serviceHost.Open();

            Console.WriteLine("HelloWorldService is ready");
        }

        private static void StartClient()
        {
            Console.WriteLine("Connecting the HelloWorld server.");

            ChannelFactory<IHelloWorldService> factory =
                new ChannelFactory<IHelloWorldService>("HelloWorldClientConfig");

            // Uncomment the following line to add ClientMessageFixer via code 
            // factory.Description.Behaviors.Add(new MessageFixer.ClientMessageFixer());
            IHelloWorldService proxy = factory.CreateChannel();

            Console.WriteLine(proxy.Greetings("Indigo Developer!"));
            factory.Close();
        }
    }
}

App.config

<?xml version="1.0" encoding="utf-8" ?>
<!-- 
    Copyright Notice 

    Use of this sample code is subject to the terms specified at 
    http://www.microsoft.com/info/cpyright.htm
-->
<configuration>
    <system.serviceModel>

        <services>
            <service 
                serviceType="HelloWorld.HelloWorldService"
                behaviorConfiguration="ServiceBehavior">
                <endpoint address="http://localhost:8080/HelloWorld/Service"
                          bindingSectionName="wsProfileBinding"
                          contractType="HelloWorld.IHelloWorldService" />
            </service>
        </services>

        <client>
            <endpoint 
                configurationName="HelloWorldClientConfig" 
                address="http://localhost:8080/HelloWorld/Service" 
                bindingSectionName="wsProfileBinding" 
                contractType="HelloWorld.IHelloWorldService"
                behaviorConfiguration="ClientBehavior"
            />
        </client>

        <behaviors>
            <behavior  
                configurationName="ServiceBehavior">
                <!-- Uncomment the following line to add the ServiceMessageFixer via config. -->
                <!-- 
                <CustomServiceBehavior type="MessageFixer.ServiceMessageFixer, MessageFixer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
                -->
            </behavior>

            <behavior  
                configurationName="ClientBehavior">
                <!-- Uncomment the following line to add the ClientMessageFixer via config. -->
                <!-- 
                <CustomClientBehavior type="MessageFixer.ClientMessageFixer, MessageFixer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
                -->
            </behavior>
        </behaviors>

        <extensions>
            <behaviorExtensions>
                <add name="CustomServiceBehavior" 
                    type="CustomBehaviorConfig.CustomServiceBehaviorSection,CustomBehaviorConfig, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>

                <add name="CustomClientBehavior" 
                type="CustomBehaviorConfig.CustomClientBehaviorSection,CustomBehaviorConfig, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
            </behaviorExtensions>
        </extensions>

    </system.serviceModel>
</configuration>


This posting is provided "AS IS" with no warranties, and confers no rights. Use of included script samples are subject to the terms specified at http://www.microsoft.com/info/cpyright.htm.

Posted by vipulm | 2 Comments
Filed under:

Making it work - Fixing the Messages

Indigo provides great interoperability with various web services stacks. However one or the other time you will encounter an implementation that in certain scenario either produces or accepts invalid messages. For example, messages which are not complaint with SOAP schema. In such situations Indigo will do the right thing and reject the message. What if you don’t want to reject the message and want to Interop with the invalid implementation! What should you do?

How about fixing the invalid message before sending or receiving on the Indigo side? That sound neat, but how can you do such a thing? Let's explore from the client side. As a client you want to fix the request before sending it to the alien service and/or fix the response received from the alien service. The IProxyMessageInspector is the way to go. The MessageInspector in Indigo allows you to inspect and optionally modify the messages generated by runtime or consumed by runtime. The IProxyMessageInspector is a client side message inspector that has following methods

    
    object BeforeSendRequest(ref Message request, IProxyChannel channel);
    void AfterReceiveReply(ref Message reply, object correlationState);

They are self explanatory, BeforeSendRequest allows you to fix the requests before sending them and AfterReceieveReply allow you to fix the reply before it is processed by the Indigo runtime. For example consider trying to Interop with an alien service that sends invalid fault messages, the fault messages that are not complaint with SOAP schema. We want the Indigo runtime to process these faults and return appropriate faults to our client application. Following is the simple implementation of AfterReceiveReply that achieves the purpose. The FixMessage method performs the required modification on the message. This implementation works fine for simple fixes on small messages.

    public class FaultFixer : IProxyMessageInspector
    {
        #region IProxyMessageInspector Members

        public void AfterReceiveReply(ref Message reply, object correlationState)
        {
            if (reply.IsFault)
            {
                // Load the reply message in DOM for easier modification
                XmlDocument doc = new XmlDocument();
                doc.Load(reply.GetBodyReader());

                // Perform the modification
                FixMessage(doc);

                // Create new message
                XmlNodeReader reader = new XmlNodeReader(doc.DocumentElement);
                Message tempMsg = Message.CreateMessage(reply.Version,
                    reply.Headers.Action, reader);
                MessageFault fault = MessageFault.CreateFault(tempMsg);
                tempMsg.Close();

                Message newMsg = Message.CreateMessage(reply.Version, fault,
                    reply.Headers.Action);

                // Preserve the headers of the original message
                newMsg.Headers.CopyHeadersFrom(reply);

                foreach(string propertyKey in reply.Properties.Keys)
                    newMsg.Properties.Add(propertyKey, reply.Properties[propertyKey]);

                // Close the original message and return new message
                reply.Close();
                reply = newMsg;
            }
        }

        private void FixMessage(XmlDocument doc)
        {
            // Fix the fault message here.
            Console.WriteLine("Fixing the fault received from the alien service ..");
        }

        public object BeforeSendRequest(ref Message request, IProxyChannel channel)
        {
            return null;
        }

        #endregion
    }

We now have the code that fixes the replies from the alien service, but how can we bring this code to life? We must hook it up to the message processing pipeline of Indigo. There are few ways of doing this; however the easiest one is to do it imperatively in code. We add the IChannelBehavior implementation to the contract description. When the runtime is created, the ApplyBehavior method of IChannelBehavior implementation will be called, giving us the opportunity to modify the behavior of the created proxy. We add our IMessageInspector implementation to the proxy behavior.  

public class FaultFixerCreator : IChannelBehavior
{
    #region IChannelBehavior Members

    public void ApplyBehavior(ChannelDescription description, ProxyBehavior behavior)
    {
        behavior.MessageInspectors.Add(new FaultFixer());
    }

    #endregion
}

As mentioned above we add our FaultFixerCreator to the contract description of the channel factory. 


    proxy.ChannelFactory.Description.Behaviors.Add(new FaultFixerCreator());
    

We are done, from now on whenever a fault message is sent from the alien service; our code in the FaultFixer will be invoked. Following is the complete implementation. I have combined the implementation of IChannelBehavior in the FaultFixer thus avoiding the need for additional FaultFixerCreator class.

In the future posting we will learn about fixing the messages on the server side as well as fixing the messages on the client side for DuplexContract. Do send me your feedback!


namespace HelloWorld
{
    using System;
    using System.Xml;
    using System.ServiceModel;

    public class Constants
    {
        public const string Address = "http://localhost:8089/HelloWorld/Server";
        public static Binding binding;
        static Constants()
        {
            binding = new WSProfileBinding();
        }
    }

    [ServiceContract]
    public interface IHelloWorldService
    {
        [OperationContract]
        string Greetings(string name);
    }


    [ServiceBehavior(ReturnUnknownExceptionsAsFaults = true)]
    public class HelloWorldService : IHelloWorldService
    {
        public string Greetings(string name)
        {
            throw new ApplicationException("Just like that!");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            StartService();
            StartClient();

            Console.WriteLine("\nPress Enter to exit.");
            Console.ReadLine();
        }

        private static void StartService()
        {
            HelloWorldService serviceObj = new HelloWorldService();
            ServiceHost<HelloWorldService> serviceHost = new ServiceHost<HelloWorldService>(
                    new Uri(Constants.Address));
            serviceHost.AddEndpoint(typeof(IHelloWorldService), Constants.binding);
            serviceHost.Open();
            Console.WriteLine("HelloWorldService is ready");
        }

        private static void StartClient()
        {
            Console.WriteLine("Connecting to server at " + Constants.Address);

            ChannelFactory<IHelloWorldService> factory = new ChannelFactory<IHelloWorldService>(
                new Uri(Constants.Address), Constants.binding);

            // Add the fault fixer
            factory.Description.Behaviors.Add(new FaultFixer());

            IHelloWorldService proxy = factory.CreateChannel();

            Console.WriteLine(proxy.Greetings("Indigo Developer!"));
            factory.Close();
        }
    }

    public class FaultFixer : IChannelBehavior, IProxyMessageInspector
    {
        #region IChannelBehavior Members

        public void ApplyBehavior(ChannelDescription description, ProxyBehavior behavior)
        {
            behavior.MessageInspectors.Add(this);
        }

        #endregion

        #region IProxyMessageInspector Members

        public void AfterReceiveReply(ref Message reply, object correlationState)
        {
            if (reply.IsFault)
            {
                // Load the reply message in DOM for easier modification
                XmlDocument doc = new XmlDocument();
                doc.Load(reply.GetBodyReader());

                // Perform the modification
                FixMessage(doc);

                // Create new message
                XmlNodeReader reader = new XmlNodeReader(doc.DocumentElement);
                Message newMsg = Message.CreateMessage(reply.Version,
                    reply.Headers.Action, reader);
                MessageFault fault = MessageFault.CreateFault(newMsg);
                newMsg.Close();

                newMsg = Message.CreateMessage(reply.Version, fault,
                    reply.Headers.Action);

                // Preserve the headers of the original message
                newMsg.Headers.CopyHeadersFrom(reply);

                foreach(string propertyKey in reply.Properties.Keys)
                    newMsg.Properties.Add(propertyKey, reply.Properties[propertyKey]);

                // Close the original message and return new message
                reply.Close();
                reply = newMsg;
            }
        }

        private void FixMessage(XmlDocument doc)
        {
            // Fix the fault message here.
            Console.WriteLine("Fixing the fault received from the alien service ..");
        }

        public object BeforeSendRequest(ref Message request, IProxyChannel channel)
        {
            return null;
        }

        #endregion
    }
}

This posting is provided "AS IS" with no warranties, and confers no rights. Use of included script samples are subject to the terms specified at http://www.microsoft.com/info/cpyright.htm.

Posted by vipulm | 3 Comments
Filed under:

Enumerating the Enums

While writing the code today I needed to enumerate all the values of an enum. I am sure many of you know about this, but I did not and hence here it is.

Suppose you have enum defined as follows and you want to enumerate all the possible values of Fruit.

      enum Fruit
    {
        Cherry,
        Apple,
        Orange,
        Banana
    }

Use the Enum.GetValues(Type T) method to achieve this. Here is the complete program:

using System;

public class EnumerateEnum
{
    enum Fruit
    {
        Cherry,
        Apple,
        Orange,
        Banana
    }

    public static void Main(string[] args)
    {
        foreach(Fruit f in Enum.GetValues(typeof(Fruit)))
        {
            Console.WriteLine(f);
        }
    }
}
Posted by vipulm | 1 Comments

Indigo is hiring smart people like you!

Indigo is hiring:

Do you want to...
  • Work on the next generation web services platform?
  • Work with the industry experts like Don Box?
  • Work with the creators of SOAP, WSDL and XML Gurus?
  • Contribute to the next generation web services protocol specifications?

Do you consider yourself to be smart, talented? Do you have passion for building the distributed computing platforms?

The Indigo team is hiring Software Design Engineer in Test (SDET), so send your resume our way. Wait, before you question the “Test” word in the above title, read what people have to say about SDET positions at Microsoft.

Gretchen's 3 reasons to consider a career as a Microsoft SDET: http://blogs.msdn.com/jobsblog/archive/2005/01/19/356412.aspx

Steve Rowe's 3 reasons to consider being a test developer: http://blogs.msdn.com/steverowe/archive/2005/01/19/356361.aspx

The SDET are not only great developers but they are great testers as well, they are creative, they think about using the programs in way the original developer never envisioned, they think about breaking the software, they are the first developers of Indigo.

For detailed information on the position visit
http://www.microsoft.com/careers/search/details.aspx?jobid=32de915e-346f-40fc-8bee-a180e5782ce3 and
http://www.microsoft.com/careers/search/details.aspx?jobid=6286bb1e-b6bc-469c-a300-f04dc8b13ff2

My friend George has nice XML markup of Indigo Jobs. Don't forget to take a look at it. http://www.jroller.com/page/ssge/20050309#indigo_needs_testers

Posted by vipulm | 3 Comments
Filed under:

Using Non Data Contract Types in Indigo

The Data Contract provides the features that were not possible with the earlier serialization technologies, features such as Versioning, Object Identity and Object Graph preservation. This is a great way to go if you are starting fresh. However there might be situations in which using the Data Contract is either not possible or not practical.

For example,

  1. You are an Indigo service and you want to Interop with existing or down level clients with minimal code changes to the client
  2. You are an Indigo client who want to consume the existing or down level service and hence non Data Contract types
  3. You already have existing types that you do not want to or can not covert to data contract types

In all such cases you want to use XmlSerializer to serialize and de-serialize types instead of Indigo XmlFormatter. The ServiceContract attribute has a property called FormatMode, this property instructs the runtime to use either XmlFormatter or XmlSerializer.

[ServiceContract(FormatMode=ContractFormatMode.XmlSerializer)]
public interface MyInteroperableContract
{

Over the next few posts we will get to the details of achieving Interoperability in the scenarios mentioned above and their implications on wire messages, WSDL and programming model. If you can’t wait, here is the brief description of what you need to do.

If you are a service and want to Interop with down level client most probably the only thing that you need to do it to set the FormatMode proeprty of ServiceContract to ContractFormatMode.XmlSerializer. If you are trying to Interop with the existing service, use /useXmlSerializer or /uxs option to svcutil. In some cases you may also have to use /typedMessages or /tm option as well.
 

Posted by vipulm | 1 Comments
Filed under:

Find The Median Of Infinite Stream Of Numbers

A friend of mine who works in a big Bay Area company recently asked me this question. “There is an infinite stream of numbers and you have to provide a median of those numbers a any given time.” At first it sounds impossible (BTW, do not confuse median with mean or average). When asking further about the problem domain he provided me some more information that helped me solve the problem in couple of minutes. I am not going to give you the answer, but I will provide you the domain information. The infinite stream of numbers is actually the response time of pages served by the web server. At any time you are supposed to find median response time. The obvious limited resource constraint applies.
Posted by vipulm | 11 Comments
Filed under:

The “A”, “B”, “C” of Indigo Services

An Indigo service is a collection of one or more service endpoints. One of our team member pointed out after the initial design phase that understanding Indigo service endpoint is as simple as A, B, C. The A-Address means “Where”, B-Binding means “How” and C-Contact means “What”.

A-Address defines “where” the service is located,
B-Binding defines “how” to communicate to that service and

C-Contract defines “what” to communicate.
 
To learn more about this read the "The Conceptual Overview of Indigo" at
http://winfx.msdn.microsoft.com/library/default.asp?url=/library/en-us/indigo_con/html/3e7e0afd-7913-499d-bafb-eac7caacbc7a.asp?frame=true 

Posted by vipulm | 0 Comments
Filed under:

Get Indigo Today

"Indigo" is a set of .NET technologies for building and running connected systems. For more information on Indigo visit the "Longhorn" development center at http://msdn.microsoft.com/Longhorn/understanding/pillars/Indigo/default.aspx. We have just released the Community Technology Preview (CTP) version of Indigo as part of WinFX SDK on MSDN subscriber download, at http://msdn.microsoft.com/subscriptions/. Look under Tools, SDKs, DDKs | Platform Tools | WinFX SDK. It contains documentation, samples, command-line compilers, and tools designed to help you develop managed-code applications and libraries using WinFX. You can view the documentation at http://winfx.msdn.microsoft.com/library/.
 

Posted by vipulm | 0 Comments
Filed under:

Hi There

Yes, I work in Interop team of Indigo making sure that Indigo works with Microsoft web services stack such as ASMX and WSE, as well as the web services stack from other vendors. You will find information related to Indigo interoperability, Indigo in general as well as some random stuff about .Net and life in general on this blog. I look forward for your feedback and comments.
Posted by vipulm | 3 Comments
 
Page view tracker