Class Library Configuration Files

P

Peter Bradley

OK. A bit behind the times, I know; but we're just moving over to .NET 2.0.
How on earth do you manage configuration settings in a class library in .NET
2.0? In version 1.1, we used a handy class called AssemblySettings that
someone (I forget his name) had written. When the class library was
finished, you deployed it to the GAC and put the configuration files in the
GAC with the class library assembly. This no longer works. In fact trying
to get configuration data from anywhere but the calling executable falls
over.

This can't be correct. Our n-Tier architecture uses class libraries to
access data from the database and needs to obtain a connection string from a
configuration file. It's no good putting the configuration in the calling
executable because the calling executable does not know anything about the
database, and anyway the whole point of a class library is that it can be
called by many other objects. We don't want the same configuration data
spread all over the place.


Surely there's a way to fix this. Does anyone know how?




Peter (spitting blood and calling Microsoft all sorts of things you don't
want to hear)
 
B

Brian Gideon

OK. A bit behind the times, I know; but we're just moving over to .NET 2.0.
How on earth do you manage configuration settings in a class library in .NET
2.0? In version 1.1, we used a handy class called AssemblySettings that
someone (I forget his name) had written. When the class library was
finished, you deployed it to the GAC and put the configuration files in the
GAC with the class library assembly. This no longer works. In fact trying
to get configuration data from anywhere but the calling executable falls
over.

This can't be correct. Our n-Tier architecture uses class libraries to
access data from the database and needs to obtain a connection string from a
configuration file. It's no good putting the configuration in the calling
executable because the calling executable does not know anything about the
database, and anyway the whole point of a class library is that it can be
called by many other objects. We don't want the same configuration data
spread all over the place.

Surely there's a way to fix this. Does anyone know how?

Peter (spitting blood and calling Microsoft all sorts of things you don't
want to hear)

There's another school of thought that says you should keep
configuration parameters out of the class library precisely because it
can be reused by multiple applications. Different applications might,
and often do, need to inject different parameters.

There are certainly cases where it makes sense to have a class libary
specific configuration file. I'm just not convinced this one of
them. And unfortunately I don't have an answer to your specific
question.

Brian
 
P

Peter Bradley

There's another school of thought that says you should keep
configuration parameters out of the class library precisely because it
can be reused by multiple applications. Different applications might,
and often do, need to inject different parameters.

There are certainly cases where it makes sense to have a class libary
specific configuration file. I'm just not convinced this one of
them. And unfortunately I don't have an answer to your specific
question.

Brian

Thanks for that Brian.

Perhaps I should expand a bit. The configuration file is for the data
access component of an n-Tier application. It is a class library that is
called by another class library (the business logic layer component). The
business logic layer component is a remote component hosted in a Windows
service. Therefore the nearest executable is the Windows service.

The real executable is the one on the client machine (or, more likely, Web
server). One of the main points about n-Tier development is that the client
is completely unaware of implementation details in respect of things like
data accesss. It just wants the data. We might, for example, decide to
move away from SqlServer to MySql. Such a move should leave the client
entirely untouched. The last thing we would want to do would be to pass a
connection string to the remote object: it's one of the things we're trying
to keep off the client - especially if the client is a Web server.

Whether or not configuration can be retrieved from the Windows service
AppConfig file, I don't know. I'll have to do some tests. It's not great,
though, doing it that way because it takes config away from the thing you
actually want to configure (the data access code).


Peter
 
A

Andy

Peter,

If you really need such a change, you may need to look into web
services or remoting. The client would get its data from the web
service or remoting interface. Of course the host (IIS) would need to
know what connection string to use, but your Windows Service would no
longer need to know.

Of course what would happen now if you needed to change databases?
Would you have to redeploy the data access layer to the client? If
the answer is yes, then just put the connection string into
the .config for the service. No need to add the overhead of remoting
or web services if you would need to redeploy your data layer anyway..
 
G

Guest

Hi Peter,

I have to admit that I belong to the school of thought that says config's
should generally be more closely aligned to the calling exe's - however I
recently had to build a system that supported the use of plug-in's (for
loose-coupling purposes) that could be dropped into a folder and be
immediately available for use by the rest of the system. These plug-ins were
contained in assemblies and so using config files that travelled with the
assembly was what I opted for.

I also use .NET v2.0 and also did find some v1.1 code on the web that
roughly did what I wanted, although I remember having to make a few changes
to it to get it to work (the changes I made were minimal) - like you, I can't
remember the name of the author though (who really ought to get credit for
the code)... Just in case this wasn't the same code you have, I'll quickly
cover the usage scenario...

To use it, you need to:
(a) name the config file the same as the assembly, but with ".config"
appended to the end ofthe name - so e.g., if your assembly is called
"Hello.World.dll" then the config should be named "Hello.World.dll.config"
(b) make sure the config is in the same folder as the assembly
(c) have your config look something like this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="assemblySettings"
type="System.Configuration.DictionarySectionHandler" />
<section name="otherAssemblySettings"
type="System.Configuration.DictionarySectionHandler" />
</configSections>
<assemblySettings>
<add key="SomeParam1" value="SomeValue1"/>
<add key="SomeParam2" value="SomeValue2"/>
<add key="SomeParam3" value="SomeValue3"/>
</assemblySettings>
<otherAssemblySettings>
<add key="SomeOtherParam1" value="SomeOtherValue1"/>
<add key="SomeOtherParam2" value="SomeOtherValue2"/>
<add key="SomeOtherParam3" value="SomeOtherValue3"/>
</otherAssemblySettings>
</configuration>

(d) from your code, you can get the two separate config sections doing
something like (not compiled/tested):

AssemblyConfig m_assemblyConfig = new
AssemblyConfig(Assembly.GetCallingAssembly());
AssemblyConfig m_otherConfig = new
AssemblyConfig(Assembly.GetCallingAssembly(), @"otherAssemblySettings");

string someSetting = m_assemblyConfig[@"SomeParam1"];
string someOtherSetting = m_otherConfig[@"SomeOtherParam1"];


Here's what the main code looks like (again not compiled/tested):
using System;
using System.Reflection;
using System.Collections;
using System.Xml;
using System.Configuration;
using System.Runtime.CompilerServices;

namespace Put.Your.Namespace.Here
{
public class AssemblyConfig
{
[MethodImpl(MethodImplOptions.NoInlining)]
public AssemblyConfig() : this(Assembly.GetCallingAssembly())
{
}

public AssemblyConfig(Assembly assembly)
{
m_settings = GetConfig(assembly);
}

public AssemblyConfig(string nodeName)
{
m_settings = GetConfig(Assembly.GetCallingAssembly(), nodeName);
}

public AssemblyConfig(Assembly assembly, string nodeName)
{
m_settings = GetConfig(assembly, nodeName);
}

public string this[string key]
{
get
{
string settingValue = null;
if (m_settings != null)
{
settingValue = m_settings[key] as string;
}
return (settingValue == null ? "" : settingValue);
}
}

public IDictionary Settings
{
get { return m_settings; }
}
protected IDictionary m_settings;

public static IDictionary GetConfig()
{
return GetConfig(Assembly.GetCallingAssembly());
}

public static IDictionary GetConfig(Assembly assembly)
{
return GetConfig(assembly, @"assemblySettings");
}

public static IDictionary GetConfig(Assembly assembly, string
nodeName)
{
IDictionary dictionary = null;
string configFile = assembly.CodeBase + @".config";
XmlDocument configDocument = new XmlDocument();
configDocument.Load(new XmlTextReader(configFile));
XmlNodeList nodes = configDocument.GetElementsByTagName(nodeName);
foreach (XmlNode node in nodes)
{
if (node.LocalName == nodeName)
{
DictionarySectionHandler sectionHandler = new
DictionarySectionHandler();
dictionary = (IDictionary)sectionHandler.Create(null,
null, node);
break;
}
}
return dictionary;
}
}
}

One last thing to mention - I have definitely got this working in .NET v2.0
with no probs, however this was in a non-GAC scenario (the plug-ins that used
this code were not hosted in the GAC) - if tihs code doesn't work in your
setup, then it may well have something to do with OS/.Net
permissioning/security and/or the fact that your assemblies are being hosted
inside of the GAC (i'm too tired to think why this late in the day :))..

Hope this helps you out...

Kind regards,
Patrick
 
N

Nicholas Paldino [.NET/C# MVP]

Peter,

That's exactly what you have to do. A windows service is just an EXE.
If you create an EXE in .NET that is run as a windows service, then you can
create a config file where you place all of your configuration settings.

I am definitely of the same mind as what Brian described, that because
it is a class library, you should be specifying in the EXE that calls the
class library what the configuration is. It's just a matter of knowing
where your configuration file is going to be.
 
P

Peter Bradley

Hmm. I thought I'd made this clear.

We *are* using Web services and remoting. That's why the difficulty arises.

The actual architecture is like this:

* A SQL table records transactions (students enrolling, withdrawing,
transferring courses, changing personal details, etc)
* A BizTalk SQL adapter feeds the transaction data, in order, to BizTalk
* BizTalk processes the transactions one-by-one and calls a SOAP adapter to
manipulate Active Directory
* The Web service called by the SOAP adapter connects to a remote object to
obtain lookup data from a remote database
* The remote object (a SAO hosted in a Windows service) obtains lookup data
and makes some changes to the remote database (because some of the lookup
data, such as the next mail server to allocate on a round robin basis, must
be looked up once and then incremented).
* Using the lookup data, the remote object manipulates AD and returns
status information to the Web service
* The Web service returns the status, or a SOAP Fault, to BizTalk
* BizTalk completes its transaction or takes compensating action (if any
errors have occurred)

That's why the config information cannot be with the executable (except,
perhaps, the Windows service). It's at several remote hops away, using an
SOA philosophy (i.e. the client (BizTalk) asks for a service (update AD),
but has no knowledge of how that service is performed. It doesn't even know
that the remote object has to get data from a database. Similarly, the
remote object's client (the Web service) simply asks the remote object to
manipulate AD. It has no idea how this might be carried out.

Sorry if I'd not been clear enough before.

Cheers


Peter
 
P

Peter Bradley

Nicholas Paldino said:
Peter,

That's exactly what you have to do. A windows service is just an EXE.
If you create an EXE in .NET that is run as a windows service, then you
can create a config file where you place all of your configuration
settings.

Yep. We're going with that, I think.

Thanks to everyone for their help.


Peter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top