views:

797

answers:

2

I have written a simple WCF web service which is configured programmaticaly. It exposes three endpoints which bind different bindings to the same contract:

  • WebHttpBinding
  • WebHttpRelayBinding (over Microsoft azure)
  • myBinding (self-made binding in an additional DLL)

The configuration code is pretty straight-forward at the moment:

WebServiceHost host = new WebServiceHost(
    typeof(MyService), new Uri("http://localhost:80/"));

host.AddServiceEndpoint(typeof(MyService), new WebHttpBinding(), "");

ServiceEndpoint sbEndpoint = host.AddServiceEndpoint(
    typeof(MyService), new WebHttpRelayBinding(), "http://azureURL");
TransportClientEndpointBehavior sbBehavior =
    new TransportClientEndpointBehavior();
sbBehavior.CredentialType = TransportClientCredentialType.UserNamePassword;
sbBehavior.Credentials.UserName.UserName = "azureUserName";
sbBehavior.Credentials.UserName.Password = "azurePassword";
sbEndpoint.Behaviors.Add(sbBehavior);

host.AddServiceEndpoint(typeof(MyService), new MyBinding(), "http://someURL");

host.Open();

Now I want to export this configuration in a config file since I want to be able to change it without having to recompile.

My questions at this moment are:

  • Where can I find valuable information to achieve my goal? Most sites just speak about SOAP bindings - no word about how to include a non-standard binding.
  • Do I have to change MyBinding in order to accept configuration via app.config or does the ServiceModel call it exactly like my programmatic approach does when the config is fine?
+6  A: 

OK, so the most important things are:

  • address
  • binding
  • contract

and then some additional stuff thrown in.

1) Address:

Get this from here:

WebServiceHost host = new WebServiceHost(
    typeof(MyService), new Uri("http://localhost:80/"));
host.AddServiceEndpoint(typeof(MyService), new WebHttpBinding(), "");

and here:

ServiceEndpoint sbEndpoint = host.AddServiceEndpoint(
    typeof(MyService), new WebHttpRelayBinding(), "http://azureURL");

so you'll need something like:

<endpoint address=""
<endpoint address="http://azureURL"
<endpoint address=""http://someURL"

in your service.

2) Binding:

The first endpoint is a webHttpBinding, the second one uses a custom binding ("MyBinding") - so you have:

<endpoint address="" 
          binding="webHttpBinding"
<endpoint address="http://azureURL"
          binding="webRelayHttpBinding"
<endpoint address=""http://someURL"
          binding="myBinding"

and you'll need to define your custom binding:

<bindings>
  <customBinding>
    <binding name="MyBinding">
      .. define the parameters of your binding here
    </binding>
  </customBinding>
</bindings>

or create a <extensions> section for your binding stored in code in a separate assembly.

3) Contracts

I don't clearly see a contract anywhere - you only ever use the typeof(MyService), but usually, this is the concrete service instance, not the service contract which should be an interface (something like IMyService). Why don't you have an explicit service contract?

Anyway, if your service implementation is the contract, too, at the same time (not best practice! but possible), then you have your two endpoints like this:

<endpoint address="" 
          binding="webHttpBinding"
          contract="MyService" />
<endpoint address="http://azureURL"
          binding="webHttpRelayBinding"
          contract="MyService" />
<endpoint address="http://someURL"
          binding="myBinding"
          contract="MyService" />

You then need to add a few sprinkles here and there (define the "base address" of the service, give the service a name and so on), and should end up with something like:

<system.serviceModel>
    <bindings>
      <customBinding>
        <binding name="MyBinding">
          .. define the parameters of your binding here
        </binding>
      </customBinding>
    </bindings>
    <services>
      <service name="YourNameSpace.MyService">
        <host>
          <baseAddresses>
             <add baseAddress="http://localhost:80/" />
          </baseAddresses>
         </host>
         <endpoint address="" 
                   binding="webHttpBinding"
                   contract="MyService" />
         <endpoint address="http://azureURL"
                   binding="webHttpRelayBinding"
                   contract="MyService" />
         <endpoint address="http://someURL"
                   binding="myBinding"
                   contract="MyService" />
      </service>
    </services>
</system.serviceModel>

Now all you're missing is the behavior defined - I'll leave that as an exercise for the poster :-)

Does that help anything?

As for references - hmmm..... hard to say.... I guess the usual books ("Learning WCF" by M.L.Bustamante for beginner/intermediate, "Programming WCF" by Juval Lowy for intermediate/advanced) are my best bet, and lots of experience, really. I don't know of any source that explicitly shows and teaches how to convert between settings in code and config - the two books mentioned usually show both ways, and from this, you can figure it out yourself.

Marc

marc_s
Thanks for explaining the process of building the configuration file. As you expected, the service implementation is the contract, too. I've chosen this as the contract consists only of two very general function and is not reusable. I've tried first just with the webHttpBinding and commented out the existing programmatic setup. However, it doesn't work. The service isn't accessible when I open http://localhost in my web browser. Do I have to make some other settings for the application in VS? (It's a standard "Console application" at the moment.)
Etan
Well, one problem you have is: for the webHttp binding, you need the WebServiceHost - for the other bindings, you need the straight ServiceHost - so you probably won't be able to host all endpoints in a single (Web)ServiceHost
marc_s
A: 

The prolem with the config file not being recognized properly could be solved.

Just had to add

ServiceHost h = new ServiceHost(typeof(MyService));
h.Open();

to my code, I've thought that the ServiceModel will start the service automatically since it knows all information. It's somehow strange that you add information about "MyService" into the config file and afterwards also have to specify it in the code.

However, the real answer to my problem was given by marc_s, who described the whole process of conversion from programmatical approach to config file very well.

Etan