App configuration for a service when running multiple instances

  • Thread starter Thread starter nickdu
  • Start date Start date
N

nickdu

I have a .NET service which uses an app configuration file. That all works
fine. We would like to spin up another instance of this service, with a
different service name of course, because we need to run some additional
components under a different security context. By the way, this service is a
generic hosting process which by the use of configuration loads one or more
adapters. Is there any easy way to control (/change) the application
configuration file for a service? Maybe to have it use 'service
name'.exe.config as the application config file?
--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com
 
Good Morning, Nick. Thanks for using Microsoft Newsgroup Service. My name
is Hongye Sun [MSFT] and it is my pleasure to work with you on this issue.

Do you mean that, in your .NET service project, you have two service
classes with different service names?
They are installed in two different ServiceProcessInstallers, so that they
could be run in different processes with different security context?
You want two different configuration files for them because they have
different configuration contents. You are asking for the easiest way to
control the configuration files?
Please correct me if my understanding is incorrect.

In my opinion, the way you mentioned to use "service name".exe.config is
the best and easiest way for your situation. It has the benefits of easily
extending for more services and keeping multiple configurations transparent
to your service class. In the following section, I will focus on how to
implement this solution.

1. Create a new service base class named "ConfigServiceBase", which
inherits from ServiceBase class and with a property "Config". The code
looks like:
public class ConfigServiceBase : ServiceBase
{
public Configuration Config
{
get
{
return GetConfiguration(this.ServiceName);
}
}

public static Configuration GetConfiguration(string serviceName)
{
// Get the application configuration file path.
string exeFilePath = System.IO.Path.Combine(
Environment.CurrentDirectory, serviceName + ".exe.config");

// Map to the application configuration file.
ExeConfigurationFileMap configFile = new
ExeConfigurationFileMap();
configFile.ExeConfigFilename = exeFilePath;
Configuration config =
ConfigurationManager.OpenMappedExeConfiguration(configFile,
ConfigurationUserLevel.None);

return config;
}

}
The GetConfiguration static method first truncate the configuration file
path and then load the configuration file into Configuration type. In order
to use Configuration class, please add reference to System.Configuration
into project.

2. Implement Service classes inherited from ConfigServiceBase, and use
Config property to get configuration information. The code looks like:
public partial class Service1 : ConfigServiceBase
{
public Service1()
{
InitializeComponent();
}

protected override void OnStart(string[] args)
{
string name = this.Config.AppSettings.Settings["name"].Value;

// To use name here
}

protected override void OnStop()
{
}
}
In this way, the multiple configuration file logic is transparent to your
service class.

3. Add a Service1.exe.config file into the project and make it copy to
output folder in every build.
If there are more services, we just need to add more service configuration
files and don't need to change any existing code.

Please have a try of the solution and let me know the result. If you feel
this solution is not suitable for your business requirement, please don't
hesitate to tell me and let me know more about your project. Let us try to
find some better solutions for you.

Have a nice weekend.

Regards,
Hongye Sun ([email protected], remove 'online.')
Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/en-us/subscriptions/aa948868.aspx#notifications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://support.microsoft.com/select/default.aspx?target=assistance&ln=en-us.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
I don't think you understand my situation. First of all I don't create a
service project. I don't even use Visual Studio but instead use my own
editor and the .NET sdk compiler (via make). I do derive my service class
from ServiceBase.

The situation is I have one service, let's call it PipeService. However I
want to run several instances of this via creating several NT services
pointing all them to this one binary (pipeservice.exe). I would like each
service to use a unique configuration. And I need this unique configuration
to be built in at the .NET layer since components I'm loading into my
service, which I have no control over, need to see this unique configuration
simply by using the .NET configuration API's.

For instance, a component that wants to load configuration should be able to
do something like the following:

obj = (<some object>) ConfigurationManager.GetSection("<my objects section
name>");

If I have three instances of my pipeservice running as three different named
Windows services: PipeServer1, PipeServer2, and PipeServer3 I would like them
all to use different configuration files even though the binary for each
service point to the same binary. Is there a way to do this without creating
my own appdomains within my service?
--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com


"Hongye Sun [MSFT]" said:
Good Morning, Nick. Thanks for using Microsoft Newsgroup Service. My name
is Hongye Sun [MSFT] and it is my pleasure to work with you on this issue.

Do you mean that, in your .NET service project, you have two service
classes with different service names?
They are installed in two different ServiceProcessInstallers, so that they
could be run in different processes with different security context?
You want two different configuration files for them because they have
different configuration contents. You are asking for the easiest way to
control the configuration files?
Please correct me if my understanding is incorrect.

In my opinion, the way you mentioned to use "service name".exe.config is
the best and easiest way for your situation. It has the benefits of easily
extending for more services and keeping multiple configurations transparent
to your service class. In the following section, I will focus on how to
implement this solution.

1. Create a new service base class named "ConfigServiceBase", which
inherits from ServiceBase class and with a property "Config". The code
looks like:
public class ConfigServiceBase : ServiceBase
{
public Configuration Config
{
get
{
return GetConfiguration(this.ServiceName);
}
}

public static Configuration GetConfiguration(string serviceName)
{
// Get the application configuration file path.
string exeFilePath = System.IO.Path.Combine(
Environment.CurrentDirectory, serviceName + ".exe.config");

// Map to the application configuration file.
ExeConfigurationFileMap configFile = new
ExeConfigurationFileMap();
configFile.ExeConfigFilename = exeFilePath;
Configuration config =
ConfigurationManager.OpenMappedExeConfiguration(configFile,
ConfigurationUserLevel.None);

return config;
}

}
The GetConfiguration static method first truncate the configuration file
path and then load the configuration file into Configuration type. In order
to use Configuration class, please add reference to System.Configuration
into project.

2. Implement Service classes inherited from ConfigServiceBase, and use
Config property to get configuration information. The code looks like:
public partial class Service1 : ConfigServiceBase
{
public Service1()
{
InitializeComponent();
}

protected override void OnStart(string[] args)
{
string name = this.Config.AppSettings.Settings["name"].Value;

// To use name here
}

protected override void OnStop()
{
}
}
In this way, the multiple configuration file logic is transparent to your
service class.

3. Add a Service1.exe.config file into the project and make it copy to
output folder in every build.
If there are more services, we just need to add more service configuration
files and don't need to change any existing code.

Please have a try of the solution and let me know the result. If you feel
this solution is not suitable for your business requirement, please don't
hesitate to tell me and let me know more about your project. Let us try to
find some better solutions for you.

Have a nice weekend.

Regards,
Hongye Sun ([email protected], remove 'online.')
Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/en-us/subscriptions/aa948868.aspx#notifications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://support.microsoft.com/select/default.aspx?target=assistance&ln=en-us.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Thanks for your further clarification for the question especially on the
components part, Nick.

After performing research on ConfigurationManager.GetSection method, we
found it is impossible to change the current application configuration file
in runtime. The only way we found is what you have known: creating new
AppDomains. In order to better understand your situation, could you answer
the following questions for us? so that we could provide further help.

Could you share your problem with us why you do not want to create new
AppDomains in the service? Since I found that the component model in your
service is just like a plug-in or add-in models. In .NET framework, the
add in programming model is implemented in the way of creating different
AppDomain for different add-in. It is documented at:
http://msdn.microsoft.com/en-us/magazine/cc163476.aspx. "The AddIn
framework creates an AppDomain and loads the appropriate assemblies in the
AppDomain."

Is it possible to restrict that all components get the configuration by
using your own API instead of ConfigurationManager.GetSection? The API can
be a static method or a method from component base class.

Once we get the direction, we could do further research for you. Thanks in
advance for your cooperation.

Regards,
Hongye Sun ([email protected], remove 'online.')
Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).


This posting is provided "AS IS" with no warranties, and confers no rights.
 
I can't enforce that all components call my class to get the config section.

In terms of create my own appdomains, I could, but then that's just a bunch
more code I have to write, debug, maintain. I was hoping there was a minimal
solution for this. I guess not.
--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com
 
Thanks for your reply, Nick.

In my opinion, there is no easy solution for your situation. However, it is
possible for us to work out a relatively simple AppDomain solution.

In order to save your effort to write the code, I wrote a sample code to
illustrate how to use AppDomain to change the configuration file name. The
key concepts of the code are:
1. Create a new AppDomain with same application base and specified
configuration path.
2. Create a MarshalByRefObject object: internal worker. Call its work
method to run across different AppDomains.
3. Inside the internal worker, it will load and run the components based on
configuration file.
4. Since components are run in new AppDomain, they are also using specified
configuration file.
5. After service stop, unload the worker AppDomain.

Here is the Worker and InternalWorker classes:
//////////////////////////////////
public class Worker
{
private AppDomain mAD;

public void Work(string serviceName)
{
// Get the full name of the EXE assembly.
string exeAssembly = Assembly.GetEntryAssembly().FullName;

// Construct and initialize settings for a second AppDomain.
AppDomainSetup ads = new AppDomainSetup();
ads.ApplicationBase =
System.Environment.CurrentDirectory;
ads.DisallowBindingRedirects = false;
ads.DisallowCodeDownload = true;
ads.ConfigurationFile =
Path.Combine(Assembly.GetEntryAssembly().Location,
serviceName + ".exe.config");

// Create the second AppDomain.
mAD = AppDomain.CreateDomain("Worker AD " + new
Guid().ToString(), null, ads);

// Create internal worker in second domain.
InternalWorker innerWorker =
(InternalWorker)mAD.CreateInstanceAndUnwrap(
exeAssembly,
typeof(InternalWorker).FullName
);

// Run the internal worker to do real work.
innerWorker.Work();
}

public void Unload()
{
if (mAD != null)
{
AppDomain.Unload(mAD);
}
}

internal class InternalWorker : MarshalByRefObject
{
public void Work()
{
Console.WriteLine(

ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FileP
ath
);
// TODO: Load configuration data

// TODO: Load components

// TODO: Run components
}
}
}
//////////////////////////////////

Please use the Worker class as following:
When service starts:
m_worker = new Worker();
m_worker.Work(this.ServiceName);

When service stops:
m_worker.Unload();

The code was only tested by me in simple situation. In order to use the
code in your product, you still need to well debug and test on it. Please
let me know if you have any question or difficulty on it. I will try my
best to help.

Regards,
Hongye Sun ([email protected], remove 'online.')
Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

This posting is provided "AS IS" with no warranties, and confers no rights.
 
I'm starting to look into this again. Unfortunately it appears it's not
possible if you derive from System.ServiceProcess.ServiceBase because its
ServiceMain method removes the first command line argument which happens to
be the service name.

public unsafe void ServiceMainCallback(int argCount, IntPtr argPointer)
{
fixed (NativeMethods.SERVICE_STATUS* service_statusRef = &this.status)
{
string[] state = null;
if (argCount > 0)
{
char** chPtr = (char**) argPointer.ToPointer();
state = new string[argCount - 1];
for (int i = 0; i < state.Length; i++)
{
chPtr++;
state = Marshal.PtrToStringUni(*((IntPtr*) chPtr));
}
}
 
Hi Nick,

I noticed that you have also posted this question at
http://www.microsoft.com/communities/newsgroups/list/en-us/default.aspx?dg=m
icrosoft.public.dotnet.framework&mid=8a5e51c3-f8f4-4267-a299-ee7ffc0f5c5f&sl
oc=en-us.

In order that we are on the same track, I will answer your question on that
post. Thanks for your undstanding.

Regards,
Hongye Sun ([email protected], remove 'online.')
Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).
 
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Back
Top