<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6499533565333397518</id><updated>2011-11-27T17:20:21.821-08:00</updated><title type='text'>ranamauro</title><subtitle type='html'>misadventures in code</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.ranamauro.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6499533565333397518/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://blog.ranamauro.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>ranamauro</name><uri>http://www.blogger.com/profile/15530915772846152266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_zf0sK_VBG10/Sr0we2J9q9I/AAAAAAAAABw/Uk2Wehapb3o/s1600-R/avatar.php%3Fgravatar_id%3D5d2e38471ff66b883fa530bb4966b891%26default%3Didenticon'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>6</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6499533565333397518.post-8311665758370565596</id><published>2010-02-26T18:17:00.001-08:00</published><updated>2010-03-10T13:13:51.542-08:00</updated><title type='text'>WCF Service with Multiple Site Bindings: automatic multiplexing</title><content type='html'>&lt;p&gt;I’ve previously posted an article where I provide a solution for dealing with the limitation of hosting a WCF Service on an IIS site with multiple site bindings. Unfortunately such solution requires manually changing the configuration file and is not really portable, so needs to be kept in synch with any changes made to the site bindings themselves.&lt;/p&gt;  &lt;p&gt;So I recently had an idea for a smarter implementation of a similar idea that would be much more portable and require no manual intervention when applied to different deployment environments. This implementation is also based on a custom ServiceHostFactory but doesn’t require any changes to your configuration file, instead it will figure out the adjustments and fix-up steps that need to be made do the service’s endpoint at runtime, based on the deployment environment so you don’t have to do that. Based on my previous &lt;a href="http://blog.ranamauro.com/2008/07/hosting-wcf-service-on-iis-site-with_25.html" target="_blank"&gt;post&lt;/a&gt;, it’s clear that the problem with having multiple site bindings is that WCF doesn’t know how to create physical absolute addresses for endpoints that have relative addresses. That’s not a very hard problem to solve, all we need to do is make sure that all endpoints have reasonable absolute addresses and we’re good to go. But the WCF hooks that are available to us to inject that code, are called too late in the game when all addresses have already been converted to absolute addresses or, a failure to do so (the notorious “This collection already contains an address with scheme http...&amp;quot;) has already occurred.&lt;/p&gt;  &lt;p&gt;The basic approach that I came up with is to make utilitarian calls to CreateServiceHost with some specially massaged inputs such that I can discover the form of the original address by inspecting the results. I then dispose of the ServiceHost (which I never open) and go ahead and build the real one and then I replace all the endpoints with ones that have the addresses I computed base on the information I gathered. Here’s some more detail on how this:&lt;/p&gt;  &lt;p&gt;When the CreateServiceHost is invoked by the WCF runtime, the ServiceHostFactory will inspect the list of base addresses it gets passed. It will then go through the list and detect if any Uri scheme has more than a single base address in the list. If none are found, we have nothing to do and can call the base implementation. If, however, it finds one it will create a dummy address for that scheme and replace any additional base addresses with that dummy address, otherwise it will just keep the base address as-is. So, if you had the following 4 base addresses:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://foo.com:8080/"&gt;http://foo.com:8080/&lt;/a&gt;     &lt;br /&gt;&lt;a href="http://localhost/"&gt;http://localhost/&lt;/a&gt;     &lt;br /&gt;&lt;a href="http://10.0.0.1:8181/"&gt;http://10.0.0.1:8181/&lt;/a&gt;     &lt;br /&gt;&lt;a href="http://localhost:80"&gt;net.tcp://localhost/&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;this will result in the following list of 2 base addresses, a real one and a dummy one:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://i-am-a-dummy-host/"&gt;http://i-am-a-dummy-host/&lt;/a&gt;     &lt;br /&gt;&lt;a href="http://localhost:80"&gt;net.tcp://localhost/&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The code for this looks more or less like this:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: green"&gt;// count base addresses per scheme and create a filtered collection where we skip all but the 1st address for each scheme
&lt;/span&gt;&lt;span style="color: #2b91af"&gt;Dictionary&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;string&lt;/span&gt;, &lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Uri&lt;/span&gt;&amp;gt;&amp;gt; allBaseAddresses = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Dictionary&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;string&lt;/span&gt;, &lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Uri&lt;/span&gt;&amp;gt;&amp;gt;();
&lt;span style="color: #2b91af"&gt;Dictionary&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;string&lt;/span&gt;, &lt;span style="color: #2b91af"&gt;Uri&lt;/span&gt;&amp;gt; dummyBaseAddresses = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Dictionary&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;string&lt;/span&gt;, &lt;span style="color: #2b91af"&gt;Uri&lt;/span&gt;&amp;gt;();
&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Uri&lt;/span&gt;&amp;gt; filteredBaseAddresses = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Uri&lt;/span&gt;&amp;gt;();
&lt;span style="color: blue"&gt;bool &lt;/span&gt;filtered = &lt;span style="color: blue"&gt;false&lt;/span&gt;;
&lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Uri &lt;/span&gt;address &lt;span style="color: blue"&gt;in &lt;/span&gt;baseAddresses)
{
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(!allBaseAddresses.ContainsKey(address.Scheme))
    {
        filteredBaseAddresses.Add(address);
        dummyBaseAddresses.Add(address.Scheme, address);
        allBaseAddresses.Add(address.Scheme, &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Uri&lt;/span&gt;&amp;gt;());
    }
    &lt;span style="color: blue"&gt;else
    &lt;/span&gt;{
        &lt;span style="color: green"&gt;// an address for this scheme was already added, that means we're going to filter them out
        &lt;/span&gt;&lt;span style="color: #2b91af"&gt;UriBuilder &lt;/span&gt;ub = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;UriBuilder&lt;/span&gt;(address.Scheme, &lt;span style="color: #2b91af"&gt;MbaServiceHostFactory&lt;/span&gt;.DummyHost);
        dummyBaseAddresses[address.Scheme] = ub.Uri;
        filtered = &lt;span style="color: blue"&gt;true&lt;/span&gt;;
    }
    allBaseAddresses[address.Scheme].Add(address);
}
&lt;span style="color: blue"&gt;if &lt;/span&gt;(!filtered)
{
    &lt;span style="color: green"&gt;// if no filtering occured we don't need to perform fixups, so call the base implementation
    &lt;/span&gt;&lt;span style="color: blue"&gt;return &lt;span style="color: blue"&gt;base.&lt;/span&gt;&lt;/span&gt;CreateServiceHost(constructorString, baseAddresses);
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;With the resulting collection, we now call the base CreateServiceHost implementation with the sole purpose of building the ServiceDescription which contains a collection of ServiceEndpoint instances. The code for this is trivial, it looks more or less like this:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// if filtering occured, we need to multiplex some of the endpoints, to find out which ones
// we create a temporary ServiceHost and feed it the dummy base addresses we just computed
&lt;/span&gt;&lt;span style="color: #2b91af"&gt;ServiceHostBase &lt;/span&gt;dummyService = &lt;span style="color: blue"&gt;&lt;span style="color: blue"&gt;base.&lt;/span&gt;&lt;/span&gt;CreateServiceHost(constructorString, dummyBaseAddresses.Values.ToArray());&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;We now go through the list of ServiceEndpoint and whenever we hit an endpoint that has an address containing the dummy address, we are sure that this endpoint had a relative address in configuration so we remove this endpoint, reverse engineer its original relative address and replace it with a collection of endpoints that are all equal, except for their addresses. Such addresses are the result of combining the relative address with all the base addresses (which we remember from our initial inspection) that have a matching scheme. So, if we found 2 endpoints with the following addresses:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://i-am-a-dummy-host/Service1.svc"&gt;http://i-am-a-dummy-host/Service1.svc&lt;/a&gt; 

  &lt;br /&gt;&lt;a href="http://i-am-a-dummy-host/Service1.svc/mex"&gt;http://i-am-a-dummy-host/Service1.svc/mex&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;we’d replace them with the following 6:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://foo.com:8080/"&gt;http://foo.com:8080/&lt;/a&gt;&lt;a href="http://i-am-a-dummy-host/Service1.svc"&gt;Service1.svc&lt;/a&gt; 

  &lt;br /&gt;&lt;a href="http://localhost/Service1.svc"&gt;http://localhost/&lt;/a&gt;&lt;a href="http://i-am-a-dummy-host/Service1.svc"&gt;Service1.svc&lt;/a&gt;&lt;/a&gt; 

  &lt;br /&gt;&lt;a href="http://10.0.0.1:8181/Service1.svc"&gt;http://10.0.0.1:8181/&lt;/a&gt;&lt;a href="http://i-am-a-dummy-host/Service1.svc"&gt;Service1.svc&lt;/a&gt;&lt;/a&gt; 

  &lt;br /&gt;&lt;a href="http://foo.com:8080/Service1.svc/mex"&gt;http://foo.com:8080/Service1.svc/mex&lt;/a&gt; 

  &lt;br /&gt;&lt;a href="http://localhost/Service1.svc/mex"&gt;http://localhost/Service1.svc/mex&lt;/a&gt; 

  &lt;br /&gt;&lt;a href="http://10.0.0.1:8181/Service1.svc/mex"&gt;http://10.0.0.1:8181/Service1.svc/mex&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All other endpoints remain as-is, and since at this point all endpoints have real absolute addresses, we’re ready for Open() and can just return this ServiceHost to WCF. The code for this is a little complex, I’ve decided to break it down in two phases, one where I just divide these endpoints in the two classes I’ve just described:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// now we simply go over the endpoints and figure out which ones need to be multiplexed by
// checking if they were bound to any of the dummy base addresses
&lt;/span&gt;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;ServiceEndpoint&lt;/span&gt;&amp;gt; singleEndpoints = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;ServiceEndpoint&lt;/span&gt;&amp;gt;();
&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;ServiceEndpoint&lt;/span&gt;&amp;gt; multiplexEndpoints = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;ServiceEndpoint&lt;/span&gt;&amp;gt;();
&lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;ServiceEndpoint &lt;/span&gt;endpoint &lt;span style="color: blue"&gt;in &lt;/span&gt;dummyService.Description.Endpoints)
{
    &lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Uri&lt;/span&gt;&amp;gt; bas = allBaseAddresses[endpoint.Binding.Scheme];
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(bas != &lt;span style="color: blue"&gt;null &lt;/span&gt;&amp;amp;&amp;amp; bas.Count &amp;gt; 1)
    {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(endpoint.ListenUri != &lt;span style="color: blue"&gt;null&lt;/span&gt;)
        {
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(endpoint.ListenUri.Host == &lt;span style="color: #2b91af"&gt;MbaServiceHostFactory&lt;/span&gt;.DummyHost)
            {
                multiplexEndpoints.Add(endpoint);
                &lt;span style="color: blue"&gt;continue&lt;/span&gt;;
            }
        }
        &lt;span style="color: blue"&gt;else if &lt;/span&gt;(endpoint.Address != &lt;span style="color: blue"&gt;null &lt;/span&gt;&amp;amp;&amp;amp; endpoint.Address.Uri.Host == &lt;span style="color: #2b91af"&gt;MbaServiceHostFactory&lt;/span&gt;.DummyHost)
        {
            multiplexEndpoints.Add(endpoint);
            &lt;span style="color: blue"&gt;continue&lt;/span&gt;;
        }
    }
    singleEndpoints.Add(endpoint);
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;The second phase, is preceded by the creation of the actual ServiceHost and clearing of the endpoints from its Description, this is trivial:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// now we can create the real ServiceHost, but we'll use the
// filteredBaseAddresses to guarantee exactly one address per scheme
&lt;/span&gt;&lt;span style="color: #2b91af"&gt;ServiceHostBase &lt;/span&gt;service = &lt;span style="color: blue"&gt;&lt;span style="color: blue"&gt;base.&lt;/span&gt;&lt;/span&gt;CreateServiceHost(constructorString, filteredBaseAddresses.ToArray());&lt;br /&gt;&lt;span style="color: green"&gt;// for simplicity we'll completely rewrite the endpoint collection
// so we don't have to break enumeration everytime we change the collection
&lt;/span&gt;service.Description.Endpoints.Clear();&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;We then rebuild the collection of endpoints from scratch by simply adding the ones that required no change:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// add the endpoints that need no modification
&lt;/span&gt;&lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;ServiceEndpoint &lt;/span&gt;endpoint &lt;span style="color: blue"&gt;in &lt;/span&gt;singleEndpoints)
{
    service.Description.Endpoints.Add(endpoint);
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;And now, for each of the ones in the other set, we create multiple endpoints for each base addresses we had found with a matching scheme:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// multiplex the endpoints that correspond to multiple base addresses
&lt;/span&gt;&lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;ServiceEndpoint &lt;/span&gt;endpoint &lt;span style="color: blue"&gt;in &lt;/span&gt;multiplexEndpoints)
{
    &lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Uri&lt;/span&gt;&amp;gt; bas = allBaseAddresses[endpoint.Binding.Scheme];
    &lt;span style="color: blue"&gt;int &lt;/span&gt;count = 0;
    &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Uri &lt;/span&gt;ba &lt;span style="color: blue"&gt;in &lt;/span&gt;bas)
    {
        &lt;span style="color: #2b91af"&gt;ServiceEndpoint &lt;/span&gt;copy = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ServiceEndpoint&lt;/span&gt;(endpoint.Contract)
        {
            Address = endpoint.Address,
            Binding = endpoint.Binding,
            ListenUri = endpoint.ListenUri,
            ListenUriMode = endpoint.ListenUriMode,
            Name = endpoint.Name + &lt;span style="color: #a31515"&gt;&amp;quot;_&amp;quot; &lt;/span&gt;+ count.ToString()
        };
        count++;
        &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;IEndpointBehavior &lt;/span&gt;eb &lt;span style="color: blue"&gt;in &lt;/span&gt;endpoint.Behaviors)
        {
            copy.Behaviors.Add(eb);
        }
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(endpoint.Address != &lt;span style="color: blue"&gt;null &lt;/span&gt;&amp;amp;&amp;amp; endpoint.Address.Uri.Host == &lt;span style="color: #2b91af"&gt;MbaServiceHostFactory&lt;/span&gt;.DummyHost)
        {
            &lt;span style="color: #2b91af"&gt;EndpointAddressBuilder &lt;/span&gt;endpointAddressBuilder = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;EndpointAddressBuilder&lt;/span&gt;(endpoint.Address);
            endpointAddressBuilder.Uri = &lt;span style="color: #2b91af"&gt;MbaServiceHostFactory&lt;/span&gt;.GetNormalizedUri(ba, endpoint.Address.Uri.PathAndQuery);
            copy.Address = endpointAddressBuilder.ToEndpointAddress();
        }
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(endpoint.ListenUri != &lt;span style="color: blue"&gt;null &lt;/span&gt;&amp;amp;&amp;amp; endpoint.ListenUri.Host == &lt;span style="color: #2b91af"&gt;MbaServiceHostFactory&lt;/span&gt;.DummyHost)
        {
            copy.ListenUri = &lt;span style="color: #2b91af"&gt;MbaServiceHostFactory&lt;/span&gt;.GetNormalizedUri(ba, endpoint.ListenUri.PathAndQuery); ;
        }
        &lt;span style="color: green"&gt;// make sure we're not re-adding an endpoint that already exists because it had an absolute Uri in config
        &lt;/span&gt;&lt;span style="color: blue"&gt;if &lt;/span&gt;(!singleEndpoints.Exists(se =&amp;gt; se.Address.Uri == copy.Address.Uri))
        {
            service.Description.Endpoints.Add(copy);
        }
    }
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;For brevity I’ve left out the helper method (GetNormalizedUri) that this uses to build the absolute addresses that you can find in the solution code. All you need to do now is use this as your factory in your .svc file and you’re good to go. In the provided code, I’ve called this factory MbaServiceHostFactory (Mba: Multiple Base Addresses) and I’ve also implemented a MbaServiceHostFactory&amp;lt;T&amp;gt; for cases in which you want the base implementation to be something other than ServiceHostFactory, so if you want a WebServiceHostFactory as your base behavior you can either change MbaServiceHostFactory to inherit from it, or use MbaServiceHostFactory&amp;lt;WebServiceHostFactory&amp;gt; instead.&lt;/p&gt;

&lt;p&gt;The WCF team is building an out of the box solution for this issue in the .NET 4.0 release, but if for whatever reason you can’t upgrade, I strongly recommend using this approach.&lt;/p&gt;

&lt;p&gt;You can download the sources &lt;a href="http://ranamauro.com/files/MultipleBaseAddresses2.zip"&gt;here&lt;/a&gt;.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6499533565333397518-8311665758370565596?l=blog.ranamauro.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ranamauro.com/feeds/8311665758370565596/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6499533565333397518&amp;postID=8311665758370565596' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6499533565333397518/posts/default/8311665758370565596'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6499533565333397518/posts/default/8311665758370565596'/><link rel='alternate' type='text/html' href='http://blog.ranamauro.com/2010/02/wcf-service-with-multiple-site-bindings.html' title='WCF Service with Multiple Site Bindings: automatic multiplexing'/><author><name>ranamauro</name><uri>http://www.blogger.com/profile/15530915772846152266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_zf0sK_VBG10/Sr0we2J9q9I/AAAAAAAAABw/Uk2Wehapb3o/s1600-R/avatar.php%3Fgravatar_id%3D5d2e38471ff66b883fa530bb4966b891%26default%3Didenticon'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6499533565333397518.post-8663649006889255803</id><published>2010-02-05T11:22:00.003-08:00</published><updated>2010-02-05T11:25:16.914-08:00</updated><title type='text'>Wire-first testing WebApiEnabled applications using in-proc hosting and HttpClient (2)</title><content type='html'>&lt;h4&gt;Part 2: using in-proc hosting&lt;/h4&gt;  &lt;p&gt;&lt;em&gt;In this post I describe how to host your web application/service in the test infrastructure for wire-first testing.      &lt;br /&gt;I’ve discussed the client side of the world in &lt;a href="http://blog.ranamauro.com/2010/02/wire-first-testing-webapienabled.html" target="_blank"&gt;part 1&lt;/a&gt; of the post.&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;In the previous part of this post we didn’t go into the details of hosting the applications and services that we set out to test. In fact there are two main issues with the approach we described: 1) it doesn’t help us start/stop the service when we do a test run 2) when we want to debug our tests, we are debugging the HttpClient code, not the server side code which is what we are trying to test (BTW, this also breaks code coverage). This post describes an approach that addresses these two issues.&lt;/p&gt;  &lt;p&gt;Some services framework, such as WCF, allow you to host services in your own process. You can easily write tests for such a service using a Visual Studio Test Project: I usually would have a TestClass with an AssemblyInitialize method where I start the service and and AssemblyCleanup method where I stop the service. This would look more or less like this:&lt;/p&gt;  &lt;pre class="code"&gt;[&lt;span style="color: #2b91af"&gt;TestClass&lt;/span&gt;]
&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;TestCommon &lt;/span&gt;{
    &lt;span style="color: blue"&gt;static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ServiceHost &lt;/span&gt;service;

    [&lt;span style="color: #2b91af"&gt;AssemblyInitialize&lt;/span&gt;]
    &lt;span style="color: blue"&gt;public static void &lt;/span&gt;AssemblyInit(&lt;span style="color: #2b91af"&gt;TestContext &lt;/span&gt;context) {
        service = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;WebServiceHost&lt;/span&gt;(&lt;span style="color: blue"&gt;typeof&lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Service&lt;/span&gt;), &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Uri&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;http://localhost/Temporary_Listen_Address/Service&amp;quot;&lt;/span&gt;));
        service.Open();
    }

    [&lt;span style="color: #2b91af"&gt;AssemblyCleanup&lt;/span&gt;]
    &lt;span style="color: blue"&gt;public static void &lt;/span&gt;AssemblyCleanup() {
        service.Close();
    }
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Very often, though, your service in production will be hosted in IIS/ASP.NET which makes the approach I just described less desirable as the test environment would be farther away from your production environment, and if you actually have a hard dependency on ASP.NET like you would have if you’re using WebApiEnabled (or in WCF if your AspNetCompatibilityRequirementsMode is set to Required), the approach will not work at all.&lt;/p&gt;

&lt;p&gt;While hosting your service on Cassini or on the local IIS server will bring you much closer to the production environment, it makes testing more tedious because you might have to manually deploy to and start/stop those hosts manually, it also makes debugging very time consuming as you’ll have to manually attach to different processes.&lt;/p&gt;

&lt;p&gt;The approach I describe below alleviates this problem and, while not being perfect, strikes a good balance of fidelity, in terms of emulating the production environment, and convenience, in terms of fast, easy and integrated the experience is for running and debugging tests. The key to the proposed approach is to host ASP.NET in the same process as the one that is used to run the tests and to hook the start/stop in the same way as we did above for WCF.&lt;/p&gt;

&lt;p&gt;Not everyone knows that the Cassini ASP.NET host that Visual Studio uses, is a reusable component that can also run outside of Visual Studio itself, all we need is to write some infrastructure code to hook it all of this together. We will start by adding a reference to one of the Cassini assemblies in the test project. In Visual Studio 2008 SP1, the assembly is called WebDev.WebHost.dll and while it is in the GAC, I doesn’t show up in Visual Studio’s list of assembly references by default. While there might be a way to change that, I just add it manually by opening the .csproj file and adding the following to the list of references:&lt;/p&gt;

&lt;p&gt;&amp;lt;Reference Include=&amp;quot;WebDev.WebHost&amp;quot; /&amp;gt;&lt;/p&gt;

&lt;p&gt;We can now use an approach similar the one I describe above for WCF. Instead of using the ServiceHost class, we will be using the Server class in the Microsoft.VisualStudio.WebHost namespace. &lt;/p&gt;

&lt;pre class="code"&gt;[&lt;span style="color: #2b91af"&gt;TestClass&lt;/span&gt;]
&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;TestCommon &lt;/span&gt;{
    &lt;span style="color: blue"&gt;public static int &lt;/span&gt;AspPort = 59341; &lt;span style="color: green"&gt;// just pick one 
    &lt;/span&gt;&lt;span style="color: blue"&gt;public static string &lt;/span&gt;AspVirtualPath = &lt;span style="color: #a31515"&gt;&amp;quot;/MyMvcApp&amp;quot;&lt;/span&gt;;
    &lt;span style="color: blue"&gt;public static string &lt;/span&gt;AspBaseAddress = &lt;span style="color: #a31515"&gt;&amp;quot;http://localhost:&amp;quot; &lt;/span&gt;+ AspPort + AspVirtualPath + &lt;span style="color: #a31515"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;;
    &lt;span style="color: blue"&gt;static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Server &lt;/span&gt;server;

    [&lt;span style="color: #2b91af"&gt;AssemblyInitialize&lt;/span&gt;]
    &lt;span style="color: blue"&gt;public static void &lt;/span&gt;AssemblyInit(&lt;span style="color: #2b91af"&gt;TestContext &lt;/span&gt;context) {
        server = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Server&lt;/span&gt;(&lt;span style="color: #2b91af"&gt;TestCommon&lt;/span&gt;.AspPort, &lt;span style="color: #2b91af"&gt;TestCommon&lt;/span&gt;.AspVirtualPath, hostedRoot, &lt;span style="color: blue"&gt;false&lt;/span&gt;, &lt;span style="color: blue"&gt;false&lt;/span&gt;);
        server.Start();
    }

    [&lt;span style="color: #2b91af"&gt;AssemblyCleanup&lt;/span&gt;]
    &lt;span style="color: blue"&gt;public static void &lt;/span&gt;AssemblyCleanup() {
        server.Stop();
    }
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Now we can also change our HttpClient to use a matching addess, so these are always in sync in case we decide to change them. In fact we normally just have the client be a member of the TestClass, so all TestMethods can share it w/out having to create one each time:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #2b91af"&gt;HttpClient &lt;/span&gt;client = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;HttpClient&lt;/span&gt;(&lt;span style="color: #2b91af"&gt;TestCommon&lt;/span&gt;.AspBaseAddress);&lt;/pre&gt;

&lt;p&gt;There is, however, extra complexity that has to do with the fact that ASP .NET needs a physical directory from which to load files, so we need to write code to “&lt;em&gt;find it&lt;/em&gt;”. In fact, you might have observed that i am using a string hostedRoot that I didn’t even define, here’s the code that I use to compute that:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// make relative to where TestResults are dropped
&lt;/span&gt;&lt;span style="color: blue"&gt;string &lt;/span&gt;hostedRoot = &lt;span style="color: #2b91af"&gt;Path&lt;/span&gt;.Combine(context.TestDir, &lt;span style="color: #a31515"&gt;@&amp;quot;..\..&amp;quot;&lt;/span&gt;);
&lt;span style="color: green"&gt;// make relative to where the MvcRestTest folder is located
&lt;/span&gt;hostedRoot = &lt;span style="color: #2b91af"&gt;Path&lt;/span&gt;.Combine(hostedRoot, &lt;span style="color: #a31515"&gt;@&amp;quot;/* web app project folder */&amp;quot;&lt;/span&gt;);
hostedRoot = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DirectoryInfo&lt;/span&gt;(hostedRoot).FullName;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;I certainly am not happy about how it depends on the physical layout of the folders in your solution, but it’s the best I could come up with.&lt;/p&gt;

&lt;p&gt;Unfortunately the complexity is not over yet: in order to make code coverage work, we will need to write code to propagate the instrumented binaries that Visual Studio copies to the TestResults folder, to the bin folder under the web application project folder, the code looks like:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;string &lt;/span&gt;hostedLocation = &lt;span style="color: #2b91af"&gt;Path&lt;/span&gt;.Combine(hostedRoot, &lt;span style="color: #a31515"&gt;&amp;quot;bin&amp;quot;&lt;/span&gt;);
&lt;span style="color: blue"&gt;string &lt;/span&gt;location = &lt;span style="color: #2b91af"&gt;Path&lt;/span&gt;.GetDirectoryName(&lt;span style="color: blue"&gt;typeof&lt;/span&gt;(&lt;span style="color: #2b91af"&gt;TestCommon&lt;/span&gt;).Assembly.Location);
&lt;span style="color: #2b91af"&gt;TestCommon&lt;/span&gt;.MergeBinaries(location, hostedLocation);&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;Where the (pretty naive) implementation for MergeBinaries, looks like this:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;static void &lt;/span&gt;MergeBinaries(&lt;span style="color: blue"&gt;string &lt;/span&gt;location, &lt;span style="color: blue"&gt;string &lt;/span&gt;hostedLocation) {
    &lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;string&lt;/span&gt;&amp;gt; sources = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;string&lt;/span&gt;&amp;gt;();
    sources.AddRange(&lt;span style="color: #2b91af"&gt;Directory&lt;/span&gt;.GetFiles(location, &lt;span style="color: #a31515"&gt;&amp;quot;*.dll&amp;quot;&lt;/span&gt;));
    sources.AddRange(&lt;span style="color: #2b91af"&gt;Directory&lt;/span&gt;.GetFiles(location, &lt;span style="color: #a31515"&gt;&amp;quot;*.exe&amp;quot;&lt;/span&gt;));
    sources.AddRange(&lt;span style="color: #2b91af"&gt;Directory&lt;/span&gt;.GetFiles(location, &lt;span style="color: #a31515"&gt;&amp;quot;*.pdb&amp;quot;&lt;/span&gt;));
    &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: blue"&gt;string &lt;/span&gt;source &lt;span style="color: blue"&gt;in &lt;/span&gt;sources) {
        &lt;span style="color: blue"&gt;string &lt;/span&gt;destination = &lt;span style="color: #2b91af"&gt;Path&lt;/span&gt;.Combine(hostedLocation, &lt;span style="color: #2b91af"&gt;Path&lt;/span&gt;.GetFileName(source));
        &lt;span style="color: blue"&gt;bool &lt;/span&gt;overwrite = &lt;span style="color: blue"&gt;false&lt;/span&gt;;
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;File&lt;/span&gt;.Exists(destination)) {
            &lt;span style="color: #2b91af"&gt;FileInfo &lt;/span&gt;s = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;FileInfo&lt;/span&gt;(source);
            &lt;span style="color: #2b91af"&gt;FileInfo &lt;/span&gt;d = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;FileInfo&lt;/span&gt;(destination);
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(s.CreationTime &amp;lt;= d.CreationTime) {
                &lt;span style="color: blue"&gt;continue&lt;/span&gt;;
            }
            overwrite = &lt;span style="color: blue"&gt;true&lt;/span&gt;;
        }
        &lt;span style="color: #2b91af"&gt;File&lt;/span&gt;.Copy(source, destination, overwrite);
    }
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;That’s pretty much it, now we can hit Run Test, Debug Test, look at code coverage and it should all work and we should get a very nice integrated experience.&lt;/p&gt;

&lt;p&gt;In AssemblyInit you can also add checks for pre-requisites of your tests, so if anything wrong is detected, you can fail early and avoid getting bogus results. For example I add a check that verifies that my mdf and ldf files exist in the expected location and that they are writeable, otherwise when I run tests that update the data in the database, they would fail and I would be left wondering whether I just introduced a regression.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;P.S.: though I say applications in the title, you are much more likely to do wire-first testing for services. That said, keep in mind that this approach will work for applications too. Also, a lot of what I discuss here is actually applicable to a larger class of services, beyond ones that are built using WebApiEnabled, such as WCF SOAP, WCF REST and ASMX services.&lt;/em&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6499533565333397518-8663649006889255803?l=blog.ranamauro.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ranamauro.com/feeds/8663649006889255803/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6499533565333397518&amp;postID=8663649006889255803' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6499533565333397518/posts/default/8663649006889255803'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6499533565333397518/posts/default/8663649006889255803'/><link rel='alternate' type='text/html' href='http://blog.ranamauro.com/2010/02/wire-first-testing-webapienabled_05.html' title='Wire-first testing WebApiEnabled applications using in-proc hosting and HttpClient (2)'/><author><name>ranamauro</name><uri>http://www.blogger.com/profile/15530915772846152266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_zf0sK_VBG10/Sr0we2J9q9I/AAAAAAAAABw/Uk2Wehapb3o/s1600-R/avatar.php%3Fgravatar_id%3D5d2e38471ff66b883fa530bb4966b891%26default%3Didenticon'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6499533565333397518.post-1966152590236024670</id><published>2010-02-05T11:22:00.001-08:00</published><updated>2010-02-05T11:24:57.758-08:00</updated><title type='text'>Wire-first testing WebApiEnabled applications using in-proc hosting and HttpClient (1)</title><content type='html'>&lt;h4&gt;Part 1: using HttpClient&lt;/h4&gt;  &lt;p&gt;&lt;em&gt;In this post I describe how you can use HttpClient to write wire-first tests.      &lt;br /&gt;I’ll be discussing the service side of the world in &lt;a href="http://blog.ranamauro.com/2010/02/wire-first-testing-webapienabled_05.html" target="_blank"&gt;part 2&lt;/a&gt; of the post.&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;So you’ve added WebApiEnabled to your controller class, your unit tests continue to work but how do you know that the web api is also working as expected? Also, since your web api sets out to be compatible with an heterogeneous array of web clients, how do you verify that the wire format is the one I expect?&lt;/p&gt;  &lt;p&gt;The approach I suggest here is to make use of the HttpClient API that is available for download on Codeplex as part of the WCF REST Starter Kit Preview 2 (get it &lt;a href="http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=24644" target="_blank"&gt;here&lt;/a&gt;). The HttpClient API is an expressive and clean API that lets you write tests that are very concise, readable and easy to write and maintain.&lt;/p&gt;  &lt;p&gt;So, let’s assume your controller looks like so:&lt;/p&gt;  &lt;pre class="code"&gt;[&lt;span style="color: #2b91af"&gt;WebApiEnabled&lt;/span&gt;]
&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MoviesController &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;Controller &lt;/span&gt;{
    &lt;span style="color: blue"&gt;static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Movie&lt;/span&gt;&amp;gt; Movies = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Movie&lt;/span&gt;&amp;gt; {
        &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Movie &lt;/span&gt;{ Id = 1, Title = &lt;span style="color: #a31515"&gt;&amp;quot;La vita è bella&amp;quot;&lt;/span&gt;, Director = &lt;span style="color: #a31515"&gt;&amp;quot;Roberto Benigni&amp;quot;&lt;/span&gt;, DateReleased = &lt;span style="color: #2b91af"&gt;DateTime&lt;/span&gt;.Parse(&lt;span style="color: #a31515"&gt;&amp;quot;20/12/1997&amp;quot;&lt;/span&gt;) }, 
        &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Movie &lt;/span&gt;{ Id = 2, Title = &lt;span style="color: #a31515"&gt;&amp;quot;Avatar&amp;quot;&lt;/span&gt;, Director = &lt;span style="color: #a31515"&gt;&amp;quot;James Cameron&amp;quot;&lt;/span&gt;, DateReleased = &lt;span style="color: #2b91af"&gt;DateTime&lt;/span&gt;.Parse(&lt;span style="color: #a31515"&gt;&amp;quot;18/12/2009&amp;quot;&lt;/span&gt;) }, 
        &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Movie &lt;/span&gt;{ Id = 3, Title = &lt;span style="color: #a31515"&gt;&amp;quot;The Godfather&amp;quot;&lt;/span&gt;, Director = &lt;span style="color: #a31515"&gt;&amp;quot;Francis Ford Coppola&amp;quot;&lt;/span&gt;, DateReleased = &lt;span style="color: #2b91af"&gt;DateTime&lt;/span&gt;.Parse(&lt;span style="color: #a31515"&gt;&amp;quot;20/12/1997&amp;quot;&lt;/span&gt;) } 
    };

    &lt;span style="color: green"&gt;// GET: /Movies/Details/5 
    &lt;/span&gt;&lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ActionResult &lt;/span&gt;Details(&lt;span style="color: blue"&gt;int &lt;/span&gt;id) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;movieToDisplay = (&lt;span style="color: blue"&gt;from &lt;/span&gt;m &lt;span style="color: blue"&gt;in &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MoviesController&lt;/span&gt;.Movies
                              &lt;span style="color: blue"&gt;where &lt;/span&gt;m.Id == id
                              &lt;span style="color: blue"&gt;select &lt;/span&gt;m).FirstOrDefault();
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(movieToDisplay == &lt;span style="color: blue"&gt;null&lt;/span&gt;) {
            &lt;span style="color: blue"&gt;throw new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;HttpException&lt;/span&gt;((&lt;span style="color: blue"&gt;int&lt;/span&gt;)&lt;span style="color: #2b91af"&gt;HttpStatusCode&lt;/span&gt;.NotFound, &lt;span style="color: #a31515"&gt;&amp;quot;No movie matching '&amp;quot; &lt;/span&gt;+ id + &lt;span style="color: #a31515"&gt;&amp;quot;'&amp;quot;&lt;/span&gt;);
        }
        &lt;span style="color: blue"&gt;return &lt;/span&gt;View(movieToDisplay);
    }
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;And suppose you’re hosting this at the “http://localhost/MyMvcApp” address (a lot more about this in Part 2), here’s what a test that verifies that asking for a non existent movie gets you a 404 response looks like:&lt;/p&gt;

&lt;pre class="code"&gt;[&lt;span style="color: #2b91af"&gt;TestMethod&lt;/span&gt;]
&lt;span style="color: blue"&gt;public void &lt;/span&gt;Movies404() {
    &lt;span style="color: #2b91af"&gt;HttpClient &lt;/span&gt;client = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;HttpClient&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;http://localhost/MyMvcApp&amp;quot;&lt;/span&gt;);
    &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;HttpResponseMessage &lt;/span&gt;response = client.Get(&lt;span style="color: #a31515"&gt;&amp;quot;Movies/9999&amp;quot;&lt;/span&gt;)) {
        &lt;span style="color: #2b91af"&gt;Assert&lt;/span&gt;.AreEqual(&lt;span style="color: #2b91af"&gt;HttpStatusCode&lt;/span&gt;.NotFound, response.StatusCode);
    }
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;Because HttpClient is very easy to extend via extension methods, you can add one that lets you easily specify an Accept header, like so:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public static class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;HttpClientExtensions &lt;/span&gt;{
    &lt;span style="color: blue"&gt;public static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;HttpResponseMessage &lt;/span&gt;Get(&lt;span style="color: blue"&gt;this &lt;/span&gt;&lt;span style="color: #2b91af"&gt;HttpClient &lt;/span&gt;client, &lt;span style="color: blue"&gt;string &lt;/span&gt;uri, &lt;span style="color: blue"&gt;string &lt;/span&gt;acceptContentType) {
        &lt;span style="color: #2b91af"&gt;HttpRequestMessage &lt;/span&gt;request = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;HttpRequestMessage&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;, uri);
        request.Headers.Accept.AddString(acceptContentType);
        &lt;span style="color: blue"&gt;return &lt;/span&gt;client.Send(request);
    }
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;And you can start testing the multi-format behavior that WebApiEnabled provides out of the box:&lt;/p&gt;

&lt;pre class="code"&gt;[&lt;span style="color: #2b91af"&gt;TestMethod&lt;/span&gt;]
&lt;span style="color: blue"&gt;public void &lt;/span&gt;Movies404Xml() {
    &lt;span style="color: #2b91af"&gt;HttpClient &lt;/span&gt;client = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;HttpClient&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;http://localhost/MyMvcApp&amp;quot;&lt;/span&gt;);
    &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;HttpResponseMessage &lt;/span&gt;response = client.Get(&lt;span style="color: #a31515"&gt;&amp;quot;Movies/9999&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;text/xml&amp;quot;&lt;/span&gt;)) {
        &lt;span style="color: #2b91af"&gt;Assert&lt;/span&gt;.AreEqual(&lt;span style="color: #2b91af"&gt;HttpStatusCode&lt;/span&gt;.NotFound, response.StatusCode);
        &lt;span style="color: #2b91af"&gt;Assert&lt;/span&gt;.AreEqual(&lt;span style="color: #a31515"&gt;&amp;quot;application/xml; charset=utf-8&amp;quot;&lt;/span&gt;, response.Content.ContentType);
    }
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;And, of course, you can write more complex test cases that test the behavior of multiple related requests. Here’s an example testing that you can add an item usng the json format and then check that it was added using the xml format. It makes 3 subsequent requests:&lt;/p&gt;

&lt;pre class="code"&gt;[&lt;span style="color: #2b91af"&gt;TestMethod&lt;/span&gt;]
&lt;span style="color: blue"&gt;public void &lt;/span&gt;MoviesCrudJsonXml() {
    &lt;span style="color: #2b91af"&gt;MoviesComparer &lt;/span&gt;moviesComparer = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MoviesComparer&lt;/span&gt;();
    &lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Movie&lt;/span&gt;&amp;gt; originalMovieList;
    &lt;span style="color: #2b91af"&gt;HttpClient &lt;/span&gt;client = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;HttpClient&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;http://localhost/MyMvcApp&amp;quot;&lt;/span&gt;);
    &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;HttpResponseMessage &lt;/span&gt;response = client.Get(&lt;span style="color: #a31515"&gt;&amp;quot;Movies&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;application/xml&amp;quot;&lt;/span&gt;)) {
        &lt;span style="color: #2b91af"&gt;Assert&lt;/span&gt;.AreEqual(&lt;span style="color: #2b91af"&gt;HttpStatusCode&lt;/span&gt;.OK, response.StatusCode);
        &lt;span style="color: #2b91af"&gt;Assert&lt;/span&gt;.AreEqual(&lt;span style="color: #a31515"&gt;&amp;quot;application/xml; charset=utf-8&amp;quot;&lt;/span&gt;, response.Content.ContentType);
        originalMovieList = response.Content.ReadAsDataContract&amp;lt;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Movie&lt;/span&gt;&amp;gt;&amp;gt;();
    }
    &lt;span style="color: blue"&gt;string &lt;/span&gt;director = &lt;span style="color: #a31515"&gt;&amp;quot;Nichols&amp;quot;&lt;/span&gt;;
    &lt;span style="color: #2b91af"&gt;DateTime &lt;/span&gt;dateReleased = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DateTime&lt;/span&gt;(1967, 12, 21);
    &lt;span style="color: blue"&gt;string &lt;/span&gt;title = &lt;span style="color: #a31515"&gt;&amp;quot;The Graduate&amp;quot;&lt;/span&gt;;
    &lt;span style="color: #2b91af"&gt;Movie &lt;/span&gt;movieToInsert = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Movie &lt;/span&gt;{ Director = director, DateReleased = dateReleased, Title = title };
    &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;HttpResponseMessage &lt;/span&gt;response = client.Post(&lt;span style="color: #a31515"&gt;&amp;quot;Movies/Create&amp;quot;&lt;/span&gt;, &lt;span style="color: #2b91af"&gt;HttpContentExtensions&lt;/span&gt;.CreateJsonDataContract(movieToInsert))) {
        &lt;span style="color: #2b91af"&gt;Assert&lt;/span&gt;.AreEqual(&lt;span style="color: #2b91af"&gt;HttpStatusCode&lt;/span&gt;.Created, response.StatusCode);
        &lt;span style="color: #2b91af"&gt;Assert&lt;/span&gt;.AreEqual(&lt;span style="color: #a31515"&gt;&amp;quot;application/json; charset=utf-8&amp;quot;&lt;/span&gt;, response.Content.ContentType);
    }
    &lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Movie&lt;/span&gt;&amp;gt; updatedMovieList;
    &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;HttpResponseMessage &lt;/span&gt;response = client.Get(&lt;span style="color: #a31515"&gt;&amp;quot;Movies&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;application/json&amp;quot;&lt;/span&gt;)) {
        &lt;span style="color: #2b91af"&gt;Assert&lt;/span&gt;.AreEqual(&lt;span style="color: #2b91af"&gt;HttpStatusCode&lt;/span&gt;.OK, response.StatusCode);
        &lt;span style="color: #2b91af"&gt;Assert&lt;/span&gt;.AreEqual(&lt;span style="color: #a31515"&gt;&amp;quot;application/json; charset=utf-8&amp;quot;&lt;/span&gt;, response.Content.ContentType);
        updatedMovieList = response.Content.ReadAsJsonDataContract&amp;lt;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Movie&lt;/span&gt;&amp;gt;&amp;gt;();
    }
    &lt;span style="color: #2b91af"&gt;Movie &lt;/span&gt;insertedMovie = updatedMovieList.Except(originalMovieList, moviesComparer).SingleOrDefault();
    &lt;span style="color: #2b91af"&gt;Assert&lt;/span&gt;.IsTrue(moviesComparer.Equals(movieToInsert, insertedMovie));
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;which uses the following comparer for the Movie class:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MoviesComparer &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;EqualityComparer&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Movie&lt;/span&gt;&amp;gt; {
    &lt;span style="color: blue"&gt;public override bool &lt;/span&gt;Equals(&lt;span style="color: #2b91af"&gt;Movie &lt;/span&gt;x, &lt;span style="color: #2b91af"&gt;Movie &lt;/span&gt;y) {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;x.Director == y.Director &amp;amp;&amp;amp; x.Title == y.Title &amp;amp;&amp;amp; x.DateReleased == y.DateReleased;
    }
    &lt;span style="color: blue"&gt;public override int &lt;/span&gt;GetHashCode(&lt;span style="color: #2b91af"&gt;Movie &lt;/span&gt;obj) {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;obj.Director.GetHashCode() ^ obj.Title.GetHashCode() ^ obj.DateReleased.GetHashCode();
    }
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;The technique used here is in fact the same technique we ended up using to write tests while developing the WebApiEnabled support itself. In the near future we’re looking at making the tests available for you to download.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6499533565333397518-1966152590236024670?l=blog.ranamauro.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ranamauro.com/feeds/1966152590236024670/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6499533565333397518&amp;postID=1966152590236024670' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6499533565333397518/posts/default/1966152590236024670'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6499533565333397518/posts/default/1966152590236024670'/><link rel='alternate' type='text/html' href='http://blog.ranamauro.com/2010/02/wire-first-testing-webapienabled.html' title='Wire-first testing WebApiEnabled applications using in-proc hosting and HttpClient (1)'/><author><name>ranamauro</name><uri>http://www.blogger.com/profile/15530915772846152266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_zf0sK_VBG10/Sr0we2J9q9I/AAAAAAAAABw/Uk2Wehapb3o/s1600-R/avatar.php%3Fgravatar_id%3D5d2e38471ff66b883fa530bb4966b891%26default%3Didenticon'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6499533565333397518.post-1784401194507740407</id><published>2010-02-05T10:34:00.001-08:00</published><updated>2010-02-08T13:54:23.205-08:00</updated><title type='text'>Getting source code from a source server from Mdbg</title><content type='html'>&lt;p&gt;I deal mostly withy managed code, and while Visual Studio is a great environment, I often find myself in situations where a lightweight, command-line style debugger is all I really need to get to what i want. Mdbg is such a debugger, with a 4 MB working set, it often fires up in just under a second.&lt;/p&gt;  &lt;p&gt;Mdbg has support for symbol server but not for source server, and when you’re dealing with 6 released versions and all kinds of service packs and qfes, you really don’t want to go spelunking to find the source code. Since Mdbg supports extensions, I decided to write an extension that could find the source automatically using the DbgHelp APIs implemented by DbgHelp.dll (docs available &lt;a href="http://msdn.microsoft.com/en-us/library/ms680641(VS.85).aspx" target="_blank"&gt;here&lt;/a&gt;). I decided to share the code and some of the experience hoping it will be useful to others.&lt;/p&gt;  &lt;p&gt;I based my implementation on the sample posted by Mike Stall (available &lt;a href="http://blogs.msdn.com/jmstall/articles/Sample_Mdbg_IronPython.aspx" target="_blank"&gt;here&lt;/a&gt;), and I decided to call my command “ls” for LoadSource, so I started off creating a Class Library project, added references to MdbgCore.dll, and started off with the following skeleton:&lt;/p&gt;  &lt;pre class="code"&gt;[&lt;span style="color: #2b91af"&gt;CommandDescription&lt;/span&gt;(CommandName = &lt;span style="color: #a31515"&gt;&amp;quot;ls&amp;quot;&lt;/span&gt;,
                    ShortHelp = &lt;span style="color: #a31515"&gt;&amp;quot;Load Sources from source server&amp;quot;&lt;/span&gt;,
                    MinimumAbbrev = 2,
                    LongHelp = &lt;span style="color: #a31515"&gt;@&amp;quot;
Usage: ls [frames]
    load sources using the SRC* source server&amp;quot;&lt;/span&gt;)]
        &lt;span style="color: blue"&gt;public static void &lt;/span&gt;LoadSourceCmd(&lt;span style="color: blue"&gt;string &lt;/span&gt;arguments) {
        }&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;The support for symbol server works just as one would expect, so in my command I assume that the user has already taken care of getting the right symbols loaded so the path information is already available. The API in MdbgCore is fairly straightforward, I didn’t need docs to find what I needed, just a few hops using intellisense.&lt;/p&gt;

&lt;p&gt;What I do is look at the Path in the SourcePostion of the CurrentFrame, look it up using the FileLocator, and if I find that the file is not available, I will go and fetch it using the source server. If the source server returns a location the value of fileLocation and the contents of the FileLocator will be updated. Regardless, I then execute the Show command:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #2b91af"&gt;MDbgFrame &lt;/span&gt;currentFrame = &lt;span style="color: #2b91af"&gt;CommandBase&lt;/span&gt;.Debugger.Processes.Active.Threads.Active.CurrentFrame;
stringsourceFile = currentFrame.SourcePosition.Path;
stringfileLocation = &lt;span style="color: #2b91af"&gt;CommandBase&lt;/span&gt;.Shell.FileLocator.GetFileLocation(sourceFile);
&lt;span style="color: blue"&gt;if &lt;/span&gt;(!&lt;span style="color: #2b91af"&gt;File&lt;/span&gt;.Exists(fileLocation)) {
    fileLocation = GetPathFromSourceServer(arguments, currentFrame, sourceFile);
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;File&lt;/span&gt;.Exists(fileLocation)) {
&lt;span style="color: #2b91af"&gt;        CommandBase&lt;/span&gt;.Shell.FileLocator.Associate(sourceFile, fileLocation);&lt;br /&gt;    }
}
&lt;span style="color: #2b91af"&gt;IMDbgCommand &lt;/span&gt;show;
stringargs;
&lt;span style="color: #2b91af"&gt;CommandBase&lt;/span&gt;.Shell.Commands.ParseCommand(&lt;span style="color: #a31515"&gt;&amp;quot;sh&amp;quot;&lt;/span&gt;, outshow, outargs);
show.Execute(&lt;span style="color: blue"&gt;string&lt;/span&gt;.Empty);&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;Since DbgHelp is a native code library, calling from managed required me to write a bunch of tedious P/Invoke code, I will share that code but won’t say much about it as there isn’t anything very interesting about it. I also removed most of the error checking from the snippets below to make it more readable, don’t think I do that in real life, the attached code should have plenty of error checking!&lt;/p&gt;

&lt;p&gt;DbgHelp requires a initialization, so I addded a static initializer that I call at the beginning of the GetPathFromSourceServer method so I can initialize if I haven’t already. Before initializing one can also set options using the SymSetOptions API, but other than that it’s just a straight call to SymInitializeW to which i pass the handle of the current process:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #2b91af"&gt;IntPtr &lt;/span&gt;hProcess = &lt;span style="color: #2b91af"&gt;Process&lt;/span&gt;.GetCurrentProcess().Handle;
&lt;span style="color: #2b91af"&gt;Dbghelp&lt;/span&gt;.SymInitializeW(hProcess, &lt;span style="color: blue"&gt;null&lt;/span&gt;, &lt;span style="color: blue"&gt;false&lt;/span&gt;);&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;After that, we need to make sure DbgHelp has loaded the module we’re finding sources for. I added a static cache where I track which modules I already loaded so I don’t end up loading a module twice. So I first check my cache and if it comes back empty, I will load the module with a call to SymLoadModuleExW and save the result in my cache:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #2b91af"&gt;MDbgModule &lt;/span&gt;module = currentFrame.Function.Module;
&lt;span style="color: blue"&gt;string &lt;/span&gt;symbolFile = module.SymbolFilename;
&lt;span style="color: blue"&gt;ulong &lt;/span&gt;baseAddress;
&lt;span style="color: #2b91af"&gt;CorModule &lt;/span&gt;corModule = module.CorModule;
baseAddress = (&lt;span style="color: blue"&gt;ulong&lt;/span&gt;)corModule.BaseAddress;
&lt;span style="color: blue"&gt;uint &lt;/span&gt;size = (&lt;span style="color: blue"&gt;uint&lt;/span&gt;)corModule.Size;
&lt;span style="color: blue"&gt;string &lt;/span&gt;moduleName = &lt;span style="color: #2b91af"&gt;Path&lt;/span&gt;.GetFileNameWithoutExtension(sourceFile);
baseAddress = &lt;span style="color: #2b91af"&gt;Dbghelp&lt;/span&gt;.SymLoadModuleExW(hProcess, &lt;span style="color: #2b91af"&gt;IntPtr&lt;/span&gt;.Zero, symbolFile, moduleName, baseAddress, size, (&lt;span style="color: #2b91af"&gt;Dbghelp&lt;/span&gt;.&lt;span style="color: #2b91af"&gt;MODLOAD_DATA&lt;/span&gt;*)&lt;span style="color: blue"&gt;null&lt;/span&gt;, 0);
moduleCache.Add(symbolFile, baseAddress);&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;At this point there really isn’t much left for us to do, our last API call is the real deal, the one that finds the source code and brings a copy of it down, and all of that work is just a simple call to SymGetSourceFileW:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #2b91af"&gt;StringBuilder &lt;/span&gt;path = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;StringBuilder&lt;/span&gt;(MAX_PATH * 8);
&lt;span style="color: #2b91af"&gt;Dbghelp&lt;/span&gt;.SymGetSourceFileW(hProcess, baseAddress, &lt;span style="color: #2b91af"&gt;IntPtr&lt;/span&gt;.Zero, sourceFile, path, (&lt;span style="color: blue"&gt;uint&lt;/span&gt;)path.Capacity);
&lt;span style="color: blue"&gt;return &lt;/span&gt;path.ToString();&lt;/pre&gt;

&lt;p&gt;After playing with the new command, I discovered that my command and Visual Studio (which uses the same core implementation as DbgHelp) were putting files in different places and having two separate caches was really irritating me, so I decided to fix that. Unfortunately, after calling the SymSetHomeDirectoryW API to point DbgHelp to the Visual Studio location (which on my Win7 box happens to be “%LOCALAPPDATA%\SourceServer”), my files were being put under a subfolder of that same location, called “src”. Thanks to a suggestion from Pat Styles, I found that if the SYMOPT_FLAT_DIRECTORY option was set, then the “src” folder would not be appended when calling SymSetHomeDirectoryW. So I added these few lines before the call to SymInitializeW and my problem was solved:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #2b91af"&gt;Dbghelp&lt;/span&gt;.SymSetOptions(&lt;span style="color: #2b91af"&gt;Dbghelp&lt;/span&gt;.&lt;span style="color: #2b91af"&gt;SYMOPT&lt;/span&gt;.SYMOPT_FLAT_DIRECTORY);
&lt;span style="color: blue"&gt;string &lt;/span&gt;localApplicationData = &lt;span style="color: #2b91af"&gt;Environment&lt;/span&gt;.GetFolderPath(&lt;span style="color: #2b91af"&gt;Environment&lt;/span&gt;.&lt;span style="color: #2b91af"&gt;SpecialFolder&lt;/span&gt;.LocalApplicationData) + &lt;span style="color: #a31515"&gt;@&amp;quot;\SourceServer&amp;quot;&lt;/span&gt;;
&lt;span style="color: #2b91af"&gt;Dbghelp&lt;/span&gt;.SymSetHomeDirectoryW(hProcess, localApplicationData);
&lt;span style="color: #2b91af"&gt;Dbghelp&lt;/span&gt;.SymSetOptions((&lt;span style="color: #2b91af"&gt;Dbghelp&lt;/span&gt;.&lt;span style="color: #2b91af"&gt;SYMOPT&lt;/span&gt;)0);&lt;/pre&gt;

&lt;p&gt;You can download the sources &lt;a href="http://ranamauro.com/files/LoadSource.zip" target="_blank"&gt;here&lt;/a&gt;, they contain two sets of sln/csproj files that share the same code, you can use LoadSource2.0.sln with Visual Studio 2008, and LoadSource.sln Visual Studio 2010 (note that you can also just use msbuild.exe to build the LoadSource.dll, so Visual Studio isn’t required).&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6499533565333397518-1784401194507740407?l=blog.ranamauro.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ranamauro.com/feeds/1784401194507740407/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6499533565333397518&amp;postID=1784401194507740407' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6499533565333397518/posts/default/1784401194507740407'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6499533565333397518/posts/default/1784401194507740407'/><link rel='alternate' type='text/html' href='http://blog.ranamauro.com/2010/02/getting-source-code-from-source-server.html' title='Getting source code from a source server from Mdbg'/><author><name>ranamauro</name><uri>http://www.blogger.com/profile/15530915772846152266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_zf0sK_VBG10/Sr0we2J9q9I/AAAAAAAAABw/Uk2Wehapb3o/s1600-R/avatar.php%3Fgravatar_id%3D5d2e38471ff66b883fa530bb4966b891%26default%3Didenticon'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6499533565333397518.post-3197250117613057395</id><published>2009-08-27T09:42:00.001-07:00</published><updated>2009-09-21T23:48:47.036-07:00</updated><title type='text'>Rest For ASP.NET MVC, integrating with Json.NET</title><content type='html'>&lt;p&gt;Rest For ASP.NET MVC isolates the MVC developer from the concern of rendering data in machine readable formats for wire transmission. One nice thing about it is how easy it is to go in and customize or replace the handling for any given format. In this post I will walk you through replacing the built-in Json format handler (built on DataContractJsonSerializer from System.ServiceModel.Web.dll) with one that instead uses the Json.NET library (this project is hosted &lt;a href="http://www.codeplex.com/Json" target="_blank"&gt;here&lt;/a&gt; on Codeplex).&lt;/p&gt;  &lt;p&gt;First of all download and unzip Json.NET (I used the Beta 4 drop which you can find &lt;a href="http://json.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=29756#DownloadId=74062" target="_blank"&gt;here&lt;/a&gt;). &lt;/p&gt;  &lt;p&gt;Then download and unzip the Rest for ASP.NET MVC sample (you can find it &lt;a href="http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=24471#DownloadId=79561" target="_blank"&gt;here&lt;/a&gt;) and open the Product\Product.sln solution in Visual Studio.&lt;/p&gt;  &lt;p&gt;We will be modifying the sdk\MovieApp\MovieApp_EdmSample project which already shows a similar technique but uses the JavaScriptSerializer, so make sure you can run that project.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_zf0sK_VBG10/Spa3fxPo5YI/AAAAAAAAABM/P2kjcmrf4zY/s1600-h/image%5B5%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_zf0sK_VBG10/Spa3gcKktcI/AAAAAAAAABU/tEiwTqxZxKA/image_thumb%5B1%5D.png?imgmax=800" width="244" height="227" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;If you set this project as default and hit F5, your browser should pop up and display the following page:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_zf0sK_VBG10/Spa3g1LLlyI/AAAAAAAAABY/LR8D74-L4Es/s1600-h/image%5B11%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_zf0sK_VBG10/Spa3hCfEVEI/AAAAAAAAABc/vVX-ydIEeRw/image_thumb%5B3%5D.png?imgmax=800" width="244" height="179" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Now, if you had control over the Accept header, you could ask for a json representation of this same data (a collection of movies), but let’s just go and hit the Json button at the top right, this will return “application/json” content which the browser doesn’t know how to render, so let’s save it to disk and open it, it should look like this:&lt;/p&gt;  &lt;p&gt;[{&amp;quot;Id&amp;quot;:1,&amp;quot;Title&amp;quot;:&amp;quot;Star Wars&amp;quot;,&amp;quot;Director&amp;quot;:&amp;quot;Lucas&amp;quot;,&amp;quot;DateReleased&amp;quot;:&amp;quot;\/Date(251884800000)\/&amp;quot;,&amp;quot;EntityState&amp;quot;:2,&amp;quot;EntityKey&amp;quot;:{&amp;quot;EntitySetName&amp;quot;:&amp;quot;MovieSet&amp;quot;,&amp;quot;EntityContainerName&amp;quot;:&amp;quot;MoviesDBEntities&amp;quot;,&amp;quot;EntityKeyValues&amp;quot;:[{&amp;quot;Key&amp;quot;:&amp;quot;Id&amp;quot;,&amp;quot;Value&amp;quot;:1}],&amp;quot;IsTemporary&amp;quot;:false}},{&amp;quot;Id&amp;quot;:2,&amp;quot;Title&amp;quot;:&amp;quot;Memento&amp;quot;,&amp;quot;Director&amp;quot;:&amp;quot;Nolan&amp;quot;,&amp;quot;DateReleased&amp;quot;:&amp;quot;\/Date(1038988800000)\/&amp;quot;,&amp;quot;EntityState&amp;quot;:2,&amp;quot;EntityKey&amp;quot;:{&amp;quot;EntitySetName&amp;quot;:&amp;quot;MovieSet&amp;quot;,&amp;quot;EntityContainerName&amp;quot;:&amp;quot;MoviesDBEntities&amp;quot;,&amp;quot;EntityKeyValues&amp;quot;:[{&amp;quot;Key&amp;quot;:&amp;quot;Id&amp;quot;,&amp;quot;Value&amp;quot;:2}],&amp;quot;IsTemporary&amp;quot;:false}},{&amp;quot;Id&amp;quot;:3,&amp;quot;Title&amp;quot;:&amp;quot;Pulp Fiction&amp;quot;,&amp;quot;Director&amp;quot;:&amp;quot;Tarantino&amp;quot;,&amp;quot;DateReleased&amp;quot;:&amp;quot;\/Date(982483200000)\/&amp;quot;,&amp;quot;EntityState&amp;quot;:2,&amp;quot;EntityKey&amp;quot;:{&amp;quot;EntitySetName&amp;quot;:&amp;quot;MovieSet&amp;quot;,&amp;quot;EntityContainerName&amp;quot;:&amp;quot;MoviesDBEntities&amp;quot;,&amp;quot;EntityKeyValues&amp;quot;:[{&amp;quot;Key&amp;quot;:&amp;quot;Id&amp;quot;,&amp;quot;Value&amp;quot;:3}],&amp;quot;IsTemporary&amp;quot;:false}},{&amp;quot;Id&amp;quot;:4,&amp;quot;Title&amp;quot;:&amp;quot;Raiders&amp;quot;,&amp;quot;Director&amp;quot;:&amp;quot;Spielberg&amp;quot;,&amp;quot;DateReleased&amp;quot;:&amp;quot;\/Date(251020800000)\/&amp;quot;,&amp;quot;EntityState&amp;quot;:2,&amp;quot;EntityKey&amp;quot;:{&amp;quot;EntitySetName&amp;quot;:&amp;quot;MovieSet&amp;quot;,&amp;quot;EntityContainerName&amp;quot;:&amp;quot;MoviesDBEntities&amp;quot;,&amp;quot;EntityKeyValues&amp;quot;:[{&amp;quot;Key&amp;quot;:&amp;quot;Id&amp;quot;,&amp;quot;Value&amp;quot;:4}],&amp;quot;IsTemporary&amp;quot;:false}}]&lt;/p&gt;  &lt;p&gt;Now that we’ve verified that everything works, let’s close the browser and move to writing some code.&lt;/p&gt;  &lt;p&gt;Open the Infrastructure folder and find the JavaScriptSerializerFormatHandler.cs file. Right click on it, copy and paste it in the same folder, you should see the following:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_zf0sK_VBG10/Spa3heLX7aI/AAAAAAAAABg/BUXeF6jjfiI/s1600-h/image%5B23%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_zf0sK_VBG10/Spa3h4P9P1I/AAAAAAAAABk/YER_bYRWABg/image_thumb%5B7%5D.png?imgmax=800" width="244" height="159" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;You will also start seeing a bunch of build errors because we now have duplicate copies of the same classes, we’ll fix that in a minute. Rename the file to JsonNetFormatHandler.cs and open it. Find an replace JavaScriptSerializer with JsonNet so we end up with the a matching class name.&lt;/p&gt;  &lt;p&gt;Now add a reference to the Json.NET library, we’ll need Newtonsoft.Json.dll, and replace the using statement for JavaScriptSerializer “using System.Web.Script.Serialization;” with the Json.NET equivalent “using Newtonsoft.Json;”.&lt;/p&gt;  &lt;p&gt;In the JsonNetFormatHandler we’ll need to modify the Deserialize method to use the JsonConvert.DeserializeObject API. This is pretty trivial, it’s a one line single method call! Your method should now look like the following:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public object &lt;/span&gt;Deserialize(&lt;span style="color: #2b91af"&gt;ControllerContext &lt;/span&gt;controllerContext, &lt;span style="color: #2b91af"&gt;ModelBindingContext &lt;/span&gt;bindingContext, &lt;span style="color: #2b91af"&gt;ContentType &lt;/span&gt;requestFormat)&lt;br /&gt;{&lt;br /&gt;&lt;span style="color: blue"&gt;    string &lt;/span&gt;input = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;StreamReader&lt;/span&gt;(controllerContext.HttpContext.Request.InputStream).ReadToEnd();&lt;br /&gt;&lt;span style="color: blue"&gt;&lt;/span&gt;&lt;span style="color: blue"&gt;    return &lt;/span&gt;&lt;span style="color: #2b91af"&gt;JsonConvert&lt;/span&gt;.DeserializeObject(input, bindingContext.ModelType);&lt;br /&gt;}&lt;/pre&gt;

&lt;p&gt;Notice that the first line hasn’t changed. If Json.NET were to add an API overload that reads its input from a Stream rather than from a string, this whole method could be a single line of code!&lt;/p&gt;

&lt;p&gt;To fix the serialization path, let’s move to the JsonNetActionResult.ExecuteResult() method were we’ll need to cleanup some code that we don’t need anymore and use the JsonConvert.SerializeObject API. This is also pretty trivial, your method is now a couple lines shorter and look like the following:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public override void &lt;/span&gt;ExecuteResult(&lt;span style="color: #2b91af"&gt;ControllerContext &lt;/span&gt;context)&lt;br /&gt;{&lt;br /&gt;&lt;span style="color: #2b91af"&gt;    Encoding &lt;/span&gt;encoding = &lt;span style="color: #2b91af"&gt;Encoding&lt;/span&gt;.UTF8;&lt;br /&gt;&lt;span style="color: #2b91af"&gt;&lt;/span&gt;&lt;span style="color: #2b91af"&gt;&lt;/span&gt;&lt;span style="color: blue"&gt;    if &lt;/span&gt;(!&lt;span style="color: blue"&gt;string&lt;/span&gt;.IsNullOrEmpty(&lt;span style="color: blue"&gt;this&lt;/span&gt;.ContentType.CharSet))&lt;br /&gt;    {&lt;br /&gt;&lt;span style="color: blue"&gt;        try&lt;br /&gt;&lt;/span&gt;        {&lt;br /&gt;            encoding = &lt;span style="color: #2b91af"&gt;Encoding&lt;/span&gt;.GetEncoding(&lt;span style="color: blue"&gt;this&lt;/span&gt;.ContentType.CharSet);&lt;br /&gt;        }&lt;br /&gt;&lt;span style="color: blue"&gt;        catch &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;ArgumentException&lt;/span&gt;)&lt;br /&gt;        {&lt;br /&gt;&lt;span style="color: blue"&gt;            throw new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;HttpException&lt;/span&gt;((&lt;span style="color: blue"&gt;int&lt;/span&gt;)&lt;span style="color: #2b91af"&gt;HttpStatusCode&lt;/span&gt;.NotAcceptable, &lt;span style="color: blue"&gt;string&lt;/span&gt;.Format(&lt;span style="color: #2b91af"&gt;CultureInfo&lt;/span&gt;.CurrentCulture, &lt;span style="color: #a31515"&gt;&amp;quot;Format {0} not supported&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;this&lt;/span&gt;.ContentType));&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;span style="color: blue"&gt;    this&lt;/span&gt;.ContentType.CharSet = encoding.HeaderName;&lt;br /&gt;    context.HttpContext.Response.ContentType = &lt;span style="color: blue"&gt;this&lt;/span&gt;.ContentType.ToString();&lt;br /&gt;&lt;span style="color: blue"&gt;    string &lt;/span&gt;json = &lt;span style="color: #2b91af"&gt;JsonConvert&lt;/span&gt;.SerializeObject(&lt;span style="color: blue"&gt;this&lt;/span&gt;.Data, &lt;span style="color: #2b91af"&gt;Formatting&lt;/span&gt;.Indented);&lt;br /&gt;&lt;span style="color: blue"&gt;    byte&lt;/span&gt;[] bytes = encoding.GetBytes(json);&lt;br /&gt;    context.HttpContext.Response.OutputStream.Write(bytes, 0, bytes.Length);&lt;br /&gt;}&lt;/pre&gt;

&lt;p&gt;Notice that the change is again tiny. If Json.NET were to add an API overload that writes the output to a Stream rather than converting to a string, this method could be further simplified! Also notice that I used Formatting.Indented, this isn’t required, but it has the nice side effect of helping us see a difference when we test this.&lt;/p&gt;

&lt;p&gt;The last thing we need to do is to register this new handler as the one the system will use for Json. To do so is trivial, just open the Global.asax file and replace the type used in the MyFormatManager contructor for jsonHandler from JavaScriptSerializerFormatHandler to JsonNetFormatHandler. The code will now look as follows:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public &lt;/span&gt;MyFormatManager()&lt;br /&gt;{&lt;br /&gt;&lt;span style="color: #2b91af"&gt;    XmlFormatHandler &lt;/span&gt;xmlHandler = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;XmlFormatHandler&lt;/span&gt;();&lt;br /&gt;&lt;span style="color: #2b91af"&gt;    JsonNetFormatHandler &lt;/span&gt;jsonHandler = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;JsonNetFormatHandler&lt;/span&gt;();&lt;br /&gt;&lt;span style="color: blue"&gt;    this&lt;/span&gt;.RequestFormatHandlers.Add(xmlHandler);&lt;br /&gt;&lt;span style="color: blue"&gt;    this&lt;/span&gt;.RequestFormatHandlers.Add(jsonHandler);&lt;br /&gt;&lt;span style="color: blue"&gt;    this&lt;/span&gt;.ResponseFormatHandlers.Add(xmlHandler);&lt;br /&gt;&lt;span style="color: blue"&gt;    this&lt;/span&gt;.ResponseFormatHandlers.Add(jsonHandler);&lt;br /&gt;}&lt;/pre&gt;

&lt;p&gt;We’re done. Let’s hit F5, and when we hit the Json button and save to disk, the data we ge will look like the following:&lt;/p&gt;

&lt;pre&gt;[&lt;br /&gt;  {&lt;br /&gt;    &amp;quot;Id&amp;quot;: 1,&lt;br /&gt;    &amp;quot;Title&amp;quot;: &amp;quot;Star Wars&amp;quot;,&lt;br /&gt;    &amp;quot;Director&amp;quot;: &amp;quot;Lucas&amp;quot;,&lt;br /&gt;    &amp;quot;DateReleased&amp;quot;: &amp;quot;\/Date(251884800000-0800)\/&amp;quot;,&lt;br /&gt;    &amp;quot;EntityKey&amp;quot;: {&lt;br /&gt;      &amp;quot;EntitySetName&amp;quot;: &amp;quot;MovieSet&amp;quot;,&lt;br /&gt;      &amp;quot;EntityContainerName&amp;quot;: &amp;quot;MoviesDBEntities&amp;quot;,&lt;br /&gt;      &amp;quot;EntityKeyValues&amp;quot;: [&lt;br /&gt;        {&lt;br /&gt;          &amp;quot;Key&amp;quot;: &amp;quot;Id&amp;quot;,&lt;br /&gt;          &amp;quot;Value&amp;quot;: 1&lt;br /&gt;        }&lt;br /&gt;      ]&lt;br /&gt;    }&lt;br /&gt;  },&lt;br /&gt;  {&lt;br /&gt;    &amp;quot;Id&amp;quot;: 2,&lt;br /&gt;    &amp;quot;Title&amp;quot;: &amp;quot;Memento&amp;quot;,&lt;br /&gt;    &amp;quot;Director&amp;quot;: &amp;quot;Nolan&amp;quot;,&lt;br /&gt;    &amp;quot;DateReleased&amp;quot;: &amp;quot;\/Date(1038988800000-0800)\/&amp;quot;,&lt;br /&gt;    &amp;quot;EntityKey&amp;quot;: {&lt;br /&gt;      &amp;quot;EntitySetName&amp;quot;: &amp;quot;MovieSet&amp;quot;,&lt;br /&gt;      &amp;quot;EntityContainerName&amp;quot;: &amp;quot;MoviesDBEntities&amp;quot;,&lt;br /&gt;      &amp;quot;EntityKeyValues&amp;quot;: [&lt;br /&gt;        {&lt;br /&gt;          &amp;quot;Key&amp;quot;: &amp;quot;Id&amp;quot;,&lt;br /&gt;          &amp;quot;Value&amp;quot;: 2&lt;br /&gt;        }&lt;br /&gt;      ]&lt;br /&gt;    }&lt;br /&gt;  },&lt;br /&gt;  {&lt;br /&gt;    &amp;quot;Id&amp;quot;: 3,&lt;br /&gt;    &amp;quot;Title&amp;quot;: &amp;quot;Pulp Fiction&amp;quot;,&lt;br /&gt;    &amp;quot;Director&amp;quot;: &amp;quot;Tarantino&amp;quot;,&lt;br /&gt;    &amp;quot;DateReleased&amp;quot;: &amp;quot;\/Date(982483200000-0800)\/&amp;quot;,&lt;br /&gt;    &amp;quot;EntityKey&amp;quot;: {&lt;br /&gt;      &amp;quot;EntitySetName&amp;quot;: &amp;quot;MovieSet&amp;quot;,&lt;br /&gt;      &amp;quot;EntityContainerName&amp;quot;: &amp;quot;MoviesDBEntities&amp;quot;,&lt;br /&gt;      &amp;quot;EntityKeyValues&amp;quot;: [&lt;br /&gt;        {&lt;br /&gt;          &amp;quot;Key&amp;quot;: &amp;quot;Id&amp;quot;,&lt;br /&gt;          &amp;quot;Value&amp;quot;: 3&lt;br /&gt;        }&lt;br /&gt;      ]&lt;br /&gt;    }&lt;br /&gt;  },&lt;br /&gt;  {&lt;br /&gt;    &amp;quot;Id&amp;quot;: 4,&lt;br /&gt;    &amp;quot;Title&amp;quot;: &amp;quot;Raiders&amp;quot;,&lt;br /&gt;    &amp;quot;Director&amp;quot;: &amp;quot;Spielberg&amp;quot;,&lt;br /&gt;    &amp;quot;DateReleased&amp;quot;: &amp;quot;\/Date(251020800000-0800)\/&amp;quot;,&lt;br /&gt;    &amp;quot;EntityKey&amp;quot;: {&lt;br /&gt;      &amp;quot;EntitySetName&amp;quot;: &amp;quot;MovieSet&amp;quot;,&lt;br /&gt;      &amp;quot;EntityContainerName&amp;quot;: &amp;quot;MoviesDBEntities&amp;quot;,&lt;br /&gt;      &amp;quot;EntityKeyValues&amp;quot;: [&lt;br /&gt;        {&lt;br /&gt;          &amp;quot;Key&amp;quot;: &amp;quot;Id&amp;quot;,&lt;br /&gt;          &amp;quot;Value&amp;quot;: 4&lt;br /&gt;        }&lt;br /&gt;      ]&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;]&lt;/pre&gt;

&lt;p&gt;Overall pretty simple, yet powerful!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6499533565333397518-3197250117613057395?l=blog.ranamauro.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ranamauro.com/feeds/3197250117613057395/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6499533565333397518&amp;postID=3197250117613057395' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6499533565333397518/posts/default/3197250117613057395'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6499533565333397518/posts/default/3197250117613057395'/><link rel='alternate' type='text/html' href='http://blog.ranamauro.com/2009/08/rest-for-aspnet-mvc-integrating-with.html' title='Rest For ASP.NET MVC, integrating with Json.NET'/><author><name>ranamauro</name><uri>http://www.blogger.com/profile/15530915772846152266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_zf0sK_VBG10/Sr0we2J9q9I/AAAAAAAAABw/Uk2Wehapb3o/s1600-R/avatar.php%3Fgravatar_id%3D5d2e38471ff66b883fa530bb4966b891%26default%3Didenticon'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_zf0sK_VBG10/Spa3gcKktcI/AAAAAAAAABU/tEiwTqxZxKA/s72-c/image_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6499533565333397518.post-340580312652916223</id><published>2008-07-25T18:01:00.001-07:00</published><updated>2010-02-04T13:08:07.549-08:00</updated><title type='text'>Hosting a WCF Service on an IIS site with Multiple Bindings</title><content type='html'>&lt;p&gt;If you ever tried hosting a WCF Service on an IIS site with Multiple Bindings you will be familiar with the error &amp;quot;This collection already contains an address with scheme http...&amp;quot;. The workarounds you can find on the internet and the recent introduction of baseAddressPrefixFilters in WCF can help get passed this error, but they won't allow you to receive requests on all the configured bindings which is usually what people I talked to actually want to do. So here's how you would go by making your service accessible from all available bindings:&lt;/p&gt;&lt;p&gt;1) Use a custom Factory, here's what this looks like in your .svc file:&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="background: #ffee62"&gt;&amp;lt;%&lt;/span&gt;&lt;span style="color: blue"&gt;@ &lt;/span&gt;&lt;span style="color: #a31515"&gt;ServiceHost &lt;/span&gt;&lt;span style="color: red"&gt;Language&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;C#&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;Debug&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;true&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;Service&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;MyService.MyService&amp;quot;
&lt;/span&gt;&lt;span style="color: red"&gt;    Factory&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;MyService.MultipleHostsFactory&amp;quot; &lt;/span&gt;&lt;span style="background: #ffee62"&gt;%&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;2) The implementation, rather than selecting a single address from the list that is passed in, overrides this with a completely empty list:&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MultipleHostsFactory &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;ServiceHostFactory
&lt;/span&gt;{
&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;protected override &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ServiceHost &lt;/span&gt;CreateServiceHost(&lt;span style="color: #2b91af"&gt;Type &lt;/span&gt;serviceType, &lt;span style="color: #2b91af"&gt;Uri&lt;/span&gt;[] baseAddresses)
&amp;#160;&amp;#160;&amp;#160; {
&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;return base&lt;/span&gt;.CreateServiceHost(serviceType, &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Uri&lt;/span&gt;[] { });
&amp;#160;&amp;#160;&amp;#160; }
}&lt;/pre&gt;&lt;p&gt;3) Your web.config will now need to specify the full list of absolute addresses you're effectively listening to. This needs to exactly match your IIS configuration or this won't work:&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;configuration&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
  &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;system.serviceModel&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
    &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;services&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
      &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;service &lt;/span&gt;&lt;span style="color: red"&gt;name&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;MyService.MyService&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;&amp;gt;
        &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;endpoint &lt;/span&gt;&lt;span style="color: red"&gt;address&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;http://hiring.contoso.com/WcfMultipleHosts/test.svc&lt;/span&gt;&amp;quot; &lt;span style="color: red"&gt;binding&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;basicHttpBinding&lt;/span&gt;&amp;quot; &lt;span style="color: red"&gt;contract&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;MyService.IServiceContract&lt;/span&gt;&amp;quot; &lt;span style="color: blue"&gt;/&amp;gt;
        &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;endpoint &lt;/span&gt;&lt;span style="color: red"&gt;address&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;http://hiring.contoso.com.uk/WcfMultipleHosts/test.svc&lt;/span&gt;&amp;quot; &lt;span style="color: red"&gt;binding&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;basicHttpBinding&lt;/span&gt;&amp;quot; &lt;span style="color: red"&gt;contract&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;MyService.IServiceContract&lt;/span&gt;&amp;quot; &lt;span style="color: blue"&gt;/&amp;gt;
      &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;service&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
    &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;services&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
  &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;system.serviceModel&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;configuration&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;Done.&lt;/p&gt;&lt;p&gt;Be aware that anything that relies on base addresses will now also need to be converted to explicit full addresses, here's an example of how you'd enable the help page for httpGet and enable service metadata:&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;configuration&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
  &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;system.serviceModel&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
    &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;behaviors&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
      &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;serviceBehaviors&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
        &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;behavior &lt;/span&gt;&lt;span style="color: red"&gt;name&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;diagnose&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;&amp;gt;
          &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;serviceMetadata &lt;/span&gt;&lt;span style="color: red"&gt;httpGetEnabled&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;true&lt;/span&gt;&amp;quot; &lt;span style="color: red"&gt;httpGetUrl&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;http://hiring.contoso.com/WcfMultipleHosts/test.svc&lt;/span&gt;&amp;quot; &lt;span style="color: red"&gt;httpsGetEnabled&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;false&lt;/span&gt;&amp;quot; &lt;span style="color: blue"&gt;/&amp;gt;
          &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;serviceDebug &lt;/span&gt;&lt;span style="color: red"&gt;httpHelpPageUrl&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;http://hiring.contoso.com.uk/WcfMultipleHosts/test.svc&lt;/span&gt;&amp;quot; &lt;span style="color: red"&gt;httpsHelpPageEnabled&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;false&lt;/span&gt;&amp;quot; &lt;span style="color: red"&gt;includeExceptionDetailInFaults&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;true&lt;/span&gt;&amp;quot; &lt;span style="color: blue"&gt;/&amp;gt;
        &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;behavior&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
      &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;serviceBehaviors&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
    &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;behaviors&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
    &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;services&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
      &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;service &lt;/span&gt;&lt;span style="color: red"&gt;behaviorConfiguration&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;diagnose&lt;/span&gt;&amp;quot; &lt;span style="color: red"&gt;name&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;MyService.MyService&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;&amp;gt;
        &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;endpoint &lt;/span&gt;&lt;span style="color: red"&gt;address&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;http://hiring.contoso.com/WcfMultipleHosts/test.svc&lt;/span&gt;&amp;quot; &lt;span style="color: red"&gt;binding&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;basicHttpBinding&lt;/span&gt;&amp;quot; &lt;span style="color: red"&gt;contract&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;MyService.IServiceContract&lt;/span&gt;&amp;quot; &lt;span style="color: blue"&gt;/&amp;gt;
        &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;endpoint &lt;/span&gt;&lt;span style="color: red"&gt;address&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;http://hiring.contoso.com.uk/WcfMultipleHosts/test.svc&lt;/span&gt;&amp;quot; &lt;span style="color: red"&gt;binding&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;basicHttpBinding&lt;/span&gt;&amp;quot; &lt;span style="color: red"&gt;contract&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;MyService.IServiceContract&lt;/span&gt;&amp;quot; &lt;span style="color: blue"&gt;/&amp;gt;
      &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;service&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
    &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;services&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
  &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;system.serviceModel&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;configuration&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6499533565333397518-340580312652916223?l=blog.ranamauro.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ranamauro.com/feeds/340580312652916223/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6499533565333397518&amp;postID=340580312652916223' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6499533565333397518/posts/default/340580312652916223'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6499533565333397518/posts/default/340580312652916223'/><link rel='alternate' type='text/html' href='http://blog.ranamauro.com/2008/07/hosting-wcf-service-on-iis-site-with_25.html' title='Hosting a WCF Service on an IIS site with Multiple Bindings'/><author><name>ranamauro</name><uri>http://www.blogger.com/profile/15530915772846152266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_zf0sK_VBG10/Sr0we2J9q9I/AAAAAAAAABw/Uk2Wehapb3o/s1600-R/avatar.php%3Fgravatar_id%3D5d2e38471ff66b883fa530bb4966b891%26default%3Didenticon'/></author><thr:total>7</thr:total></entry></feed>
