none
Configure ServiceHost with different config defined bindings

    Question

  • I need to be able to initialise a ServiceHost that uses two different binding configurations; one for normal network operation and one for standalone demo operation. Essentially I need to be able to run our application on a laptop for demonstration purposes. But it normally uses Windows authentication and therefore needs to be on a domain. I would like to be able to startup the client and server with a "/standalone" switch that would select alternative binding configurations that do not use Windows auth.

     

    On the client, this is easy as I can specify an alternative binding endpoint configuration name that uses a different binding configuration when creating the proxy.

     

    I haven't found a way to do this on the server when creating the ServiceHost. I would like to create a ServiceHost and specify which endpoint configuration to use from App.config. I want to leave the endpoint and behaviour configuration in the App.config file rather than code it

     

    Is this possible?

     

    Thursday, November 01, 2007 3:19 AM

Answers

  • GlennM2,

     

    - another option for your solution is to create one more config file for your demo. In order having this configuration from this file, the method ApplyConfiguration in the ServiceHost must be overrided. The following code snippet is an example of this implementation:

     

    Code Block

     protected override void ApplyConfiguration()
     {
          // workaround for passing a custom configFilename
          string configFilename = (string)CallContext.GetData("_config");

     

          ExeConfigurationFileMap filemap = new ExeConfigurationFileMap();

     

          filemap.ExeConfigFilename =
                    string.IsNullOrEmpty(configFilename) ?
                    AppDomain.CurrentDomain.SetupInformation.ConfigurationFile : configFilename;

     

          Configuration config =

              ConfigurationManager.OpenMappedExeConfiguration(filemap, ConfigurationUserLevel.None);

     

          ServiceModelSectionGroup serviceModel = ServiceModelSectionGroup.GetSectionGroup(config);

     

           foreach (ServiceElement se in serviceModel.Services.Services)
           {
                if (se.Name == this.Description.ConfigurationName)
                {
                    base.LoadConfigurationSection(se);
                    return;
                }
          }

          throw new ArgumentException("ServiceElement doesn't exist");         
    }

     

     

    Note, the ServiceHost is calling an override method (what is not a good pattern) in the constructor, therefore the value of the custom config path is passed via the thread context. If the context doesn't exist, the default application config file is used.

     

    Other option to avoid using the thread context is using a hard coded extension for your second config file, for instance .democonfig.

     

    Thanks

     

    Roman

    Monday, November 12, 2007 5:32 PM

All replies

  • The easy way to do this is to have the /standalone option parsed before the ServiceModel host comes up. Assuming the following services config,

     

    Code Block

    <services>

    <service name="MSDNForumsReceiver.TestService.Standalone">

    <endpoint address="net.tcp://127.0.0.1:10000/" binding="netTcpBinding"

    bindingConfiguration="" contract="MSDNForumsReceiver.ITestService" />

    </< FONT>service>

    <service name="MSDNForumsReceiver.TestService.Integrated">

    <endpoint address="net.tcp://127.0.0.1:9000/" binding="netTcpBinding"

    bindingConfiguration="" contract="MSDNForumsReceiver.ITestService" />

    </< FONT>service>

    </< FONT>services>

     

     

    This code will setup things correctly for your application (I recommend putting this as the first thing that happens in your Main method):

    Code Block

    Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

    ServiceModelSectionGroup sectionGroup = ServiceModelSectionGroup.GetSectionGroup(config);

    string regularServiceName = typeof(TestService).FullName;

    string integratedServiceName = regularServiceName + ".Integrated";

    string standaloneServiceName = regularServiceName + ".Standalone";

    // First, normalize the name in config.

    if (sectionGroup.Services.Services.ContainsKey(regularServiceName))

    {

    if (sectionGroup.Services.Services.ContainsKey(integratedServiceName))

    {

    sectionGroup.Services.Services[regularServiceName].Name = standaloneServiceName;

    }

    else if (sectionGroup.Services.Services.ContainsKey(standaloneServiceName))

    {

    sectionGroup.Services.Services[regularServiceName].Name = integratedServiceName;

    }

    // else, we have never changed the name, so we are OK.

    }

    if (args.Length > 0 && string.Compare(args[0], "/standalone") == 0)

    {

    sectionGroup.Services.Services[standaloneServiceName].Name = regularServiceName;

    }

    else

    {

    sectionGroup.Services.Services[integratedServiceName].Name = regularServiceName;

    }

    config.Save();

    ConfigurationManager.RefreshSection("system.serviceModel/services");

     

     

    Thursday, November 01, 2007 2:42 PM
  • Thanks Scott.

     

    So your solution is basically modifying the config file before it is actually used. Neat but a little scary!

     

    Is the config.Save() necessary? Would this actually write out any config changes? I know it will write user settings which is ok

     

    Monday, November 12, 2007 4:02 AM
  • The Save is necessary and does write out config changes. The Configuration object has its own copy of the config file which is NOT shared with the ConfigurationManager.GetSection(string) method/infrastructure.

     

    Monday, November 12, 2007 12:46 PM
  • GlennM2,

     

    - another option for your solution is to create one more config file for your demo. In order having this configuration from this file, the method ApplyConfiguration in the ServiceHost must be overrided. The following code snippet is an example of this implementation:

     

    Code Block

     protected override void ApplyConfiguration()
     {
          // workaround for passing a custom configFilename
          string configFilename = (string)CallContext.GetData("_config");

     

          ExeConfigurationFileMap filemap = new ExeConfigurationFileMap();

     

          filemap.ExeConfigFilename =
                    string.IsNullOrEmpty(configFilename) ?
                    AppDomain.CurrentDomain.SetupInformation.ConfigurationFile : configFilename;

     

          Configuration config =

              ConfigurationManager.OpenMappedExeConfiguration(filemap, ConfigurationUserLevel.None);

     

          ServiceModelSectionGroup serviceModel = ServiceModelSectionGroup.GetSectionGroup(config);

     

           foreach (ServiceElement se in serviceModel.Services.Services)
           {
                if (se.Name == this.Description.ConfigurationName)
                {
                    base.LoadConfigurationSection(se);
                    return;
                }
          }

          throw new ArgumentException("ServiceElement doesn't exist");         
    }

     

     

    Note, the ServiceHost is calling an override method (what is not a good pattern) in the constructor, therefore the value of the custom config path is passed via the thread context. If the context doesn't exist, the default application config file is used.

     

    Other option to avoid using the thread context is using a hard coded extension for your second config file, for instance .democonfig.

     

    Thanks

     

    Roman

    Monday, November 12, 2007 5:32 PM