I have a three tier web app that produces three separate dlls:
Web.UI.dll
Web.Application.dll
Web.DAL.dll
Both Web.Application.dll and Web.DAL.dll have their own app.configs. I need to access a specific setting that lives in web.config. I am using the following code:
[CacheUtil.cs]
string cacheName = ConfigurationManager.AppSettings.Get("CacheName");
I have verified that this setting exists. So, why then, when I run am I getting NULL? Does CacheName need to exist in all of .config files?
Here is the setting in web.config:
<appSettings>
<add key="CacheName" value="staging"/>
FYI, CacheUtil is a singleton that is lazy initialized upon first access. First access is happening in the DAL.dll project.
Thanks!
Only web.config is being read. The other config files (Web.Application.dll.config and Web.DAL.config) are not read.
Config is not attached to libraries; only to the ultimate executable being run (the AppName.exe.config or web.config)
Projects in Visual Studio that produce an EXE can have an app.config file, and this file is treated specially upon build; It is renamed to ProgramName.exe.config and copied to the output directory. app.config in class library or Web application projects will have no effect.
Try this:
cachename = ConfigurationManager.AppSettings["CacheName"];
This is provided that you are trying to read AppSettings out of the project's app.config file. You can't read config from another project unless you use some other logic, but it wouldn't be through the ConfigurationManager class. It'd have to be normal I/O.
Related
I have an ASP.NET web application written in C# 4.0. The application references a class library that comes with its own configuration file. At runtime, the class library uses similar to the following code to load this specific configuration:
var exeConfigPath = this.GetType().Assembly.Location;
var config = ConfigurationManager.OpenExeConfiguration(exeConfigPath);
This is done because the library has to load its bundled configuration rather than the application configuration. The application configuration should not be concerned of the library's settings and should not be able to alter them.
Now, there are a few other things that need to be done for this concept to work. I have to set the library's configuration file build operation as Content in the properties window and the Copy to be Copy Always or Copy If Newer. So far so good - the file gets automatically both into the class library's bin directory, and the web applications's bin directory, and is correctly renamed from App.config to CustomLibrary.dll.config (as supposed, the library's dll is CustomLibrary.dll).
Now I am facing two issues.
1) When I publish the web application to a filesystem location (mapped in IIS), the CustomLibrary.dll.config appears back as App.config in the bin folder of the published app. OK - I will rename it in the class library project to match the expected convention - and problem solved.
2) Even when published, the IIS compiles the application again and stores it in the ASP.NET Temporary Files. There is a fancy directory structure with a folder dedicated for each assembly referenced. The folder corresponding to the CustomLibrary.dll does not contain the config file in it. Since this.GetType().Assembly.Location will return the path to the temp folder, the application fails to load the configuration and crashes as it should.
I need to preserve the pattern of having the configuration in the class library, and be able to make it work in the web application. When manually copying the .config to the temp folder, the app works, but see, I really hate manual copying to randomly-named folders.
Is there a way to either prevent IIS from using the temp folders, or to make it copy along the config files? I believe the problem I am facing is configuration-related rather than conceptual since the application works as expected when the config file is in place. I'd prefer not to mess with using hard-coded physical paths to the config file either.
Edit:
To make it clearer, I will point out what and why I want to achieve. The idea is that the library and the web project will be developed as separate products - there will be no user or application specific information in the configuration of the library, so it will not change for different use scenarios. It is also rather specific to the class library functionality rather than the end application. It makes sense for me to keep the library's configuration information bundled within it (similar to Java, where a spring context xml file, or a properties file, get bundled with the jar of the library). I'd like to avoid having to copy the configuration in each app/web config of the consumer application. There will be cases where the consumer application is developed by third parties, and I do not want to rely on them doing their configuration right for my stuff to work. Again, the only issue here is not having the config file copied to the right place.
If those are static, internal settings that nobody should see or change, wouldn't you be better off having a file with the configuration included within the class library as an embedded resource? Either that or just a static class with the settings.
That way you'd be certain that nobody alters it, which in your scenario seems to be a plus.
I have come along a way to work arround the described issue, still not a very pleasant one to my requirements.
The solution is to take advantage of the application configuration (web.config in web apps, or app.config) which is always available. I have added as settings the absolute paths to the config file for each library. So I ended up with:
<!--
THIS IS IN THE WEB.CONFIG FILE
-->
<appSettings>
<add key ="ClassLibrary_ConfigPath"
value ="{My Publish Output Folder}\ClassLibrary.dll.config"/>
</appSettings>
and the class library now uses the following code to load its configuration:
Configuration config = null;
try
{
var exeConfigPath = this.GetType().Assembly.Location;
config = ConfigurationManager.OpenExeConfiguration(exeConfigPath);
}
catch (Exception e)
{
if (!IsConfigurationNotFoundError(e))
{
// IsConfigurationNotFoundError logic skipped for brevity
var exeConfigPath =
ConfigurationManager.AppSettings["ClassLibrary_ConfigPath"];
if (exeConfigPath != null)
{
config = ConfigurationManager.OpenExeConfiguration(exeConfigPath);
}
}
else
{
throw;
}
}
While this works, I will wait for a better solution if possible. Still, I do not have to copy the entire ClassLibrary.dll.config into the web.config file, but now I must manage filesystem locations and be aware of app-setting names. What I really want is the consumer app of the ClassLibrary.dll not to deal with its configuration in any way. If it were a desktop app, I have this covered, as Visual Studio copies the ClassLibary.dll.config appropriately. I hope there is a way to make it work smoothly for web apps.
The short answer is: you can't. You have to merge both configuration sections and place all settings in the main configuration file of your application. In case of the web application it would be the web.config. Read this
Most of my previous web applications have built using the pyramid (python) framework. With pyramid, it is possible to run the application in either development or production mode. This is achieved by providing the location of a production.ini or a development.ini settings file as one of the start up arguments to the application.
This file contains settings relating to logging levels, error reporting and database connections. The equivalent of these files in the .net world would appear to be web.config files. I assume i can create different execution environments, by simply creating a new web.config, renaming it to something appropriate, and restarting the application.
My question is, how can i get my app to target the correct *.config file (hypothetically call them production.config and development.config)?
You may want to look at MSDeploy. I use this for similar situations and it's helped me establish a single parameters file (of what is to be replaced, regardless of if it's XML, a filepath, text file, etc.) and multiple set parameter files [you can read about declareParam and setParam here.
Without knowing too much of your structure you are basically going to do something to the effect of this.
msdeploy -verb:sync -source:contentPath=c:\pathtosite\root -dest:package=c:\package.zip -declareParamFile=c:\packageparameters.xml"
Create an associated parameters file for each environment such as
development.xml
staging.xml
production.xml
(Here is a good explanation of parameter file setup)
then for your deploy you would have something (depending on the providers you want to use)
msdeploy -verb:sync -source:package=c:\package.zip -dest:contentPath=c:\inetpub\production -setParamFile=c:\<environment>.xml
The # of providers is pretty extensive. Content Path is just an easy example, but you can pull directly from IIS, to IIS, etc. Pretty robust.
There's no way that I know of to specify a different web.config file. Since .NET 4.0, however, you can use something called web.config transforms, which modify your base web.config on build. You can have a Web.Debug.Config and Web.Release.Config, which get applied depending on which build configuration you're using. Keep in mind these aren't normal web.config files, but rather transform files that modify the base web.config.
For more info and a walkthrough of the basic idea involved, see here on MSDN. The basic example given of a web.config transform file is this, which modifies a connection string to use a different connection string for the release build:
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<connectionStrings>
<add name="MyDB" connectionString="ReleaseSQLServer"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
</configuration>
if you have a class library project that acts as ur DAL and it has an App.Config file with connectionstrings, how can I force it to use that config file? It keep getting values from the web.config in my Web Application project.
The DAL project uses LinqToSql. When I instansiate a DataContext object in my Web Application, from the referenced DAL Class Library project, can I make it use it's app.Config connectionstrings? It seems to ignore this file and tries to pick up connectionstrings from the web.Config connectionstrings node. There are no connectionstrings present there.
Any help is appreciated. A colleague mentioned making the app.Config in the DAL and embedded resource. Does that sounds like a good idea?
Thanks,
~ck in San Diego
Web applications always use web.config. Desktop applications always use app.config.
how can I force it to use that config file? It keep getting values from the web.config in my Web Application project.
You can't. If you use the System.Configuration classes, they will always pull from the active application's .config file (app.config for executables, web.config for asp.net websites).
Workarounds include using file i/o for reading your settings out (as opposed to the System.Configuration namespace) or putting your DAL configuration information in the appropriate .config file (the more common choice).
I'm not sure, but take a look at that:
using System.Configuration;
ExeConfigurationFileMap Map = new ExeConfigurationFileMap();
Map.ExeConfigFilename = FileName;
Configuration Conf = ConfigurationManager.OpenMappedExeConfiguration(Map, ConfigurationUserLevel.None);
AppSettingsSection section = (AppSettingsSection)Conf.GetSection("???");
Here's how I think of a *.config file. Say you have a method in your DAL:
DoSomething(connectString, SqlDialect, businessObject)
Since connectString and SqlDialect don't change with every call to this method, it would be nice to be able to remove those parameters and get those through some other means.
Well, *.config files are there for that reason--they are not only environment-specific, they are also app-specific. It's so your Web app can say, "Hey everybody, connectString = "..." and SqlDialect = "...", for every method call, until I say otherwise."
Let's say you want one app to log into SQL with one set of credentials, and another app to use another set of credentials (with different perms if necessary) so that the DBA can keep track of which app is doing what (if he/she so chooses). Well, *.config files make this happen.
That's why the app that you're running is the one that provides the *.config file. So just cut all the contents from your DAL's app.config file and paste it into your Web.config file, then delete App.config. You should be good-to-go after that.
I am using the built in test framework in VS2008 and I would like be able to write a test that makes sure all the expected web.config settings have been defined so that if by accident one is removed or changed my suite of tests will detect it and not have to be tested in a runtime scenario. How would I set this up?
I do not want to setup a mockup of my web.config since I don't want to maintain two versions and this would make my test invalid anyways since I am really trying to capture the fact that the project's web.config is correct.
Any suggestions, alternatives, hints?
Solution: I ended up using the copy in the pre-build that was suggested with one change. On copy I rename the web.config to app.config so that the test project would automatically pick it up.
I tried to split out the config files as suggested as well but the problem I ran into was when the test project ran, it actually didn't run out of the bin directory (which setting the config files to 'Content' type would copy to) but instead to a results directory that has been pre defined. I could not figure out how to make it copy thos extra files to this results directory so the config files could never be found.
I'am using the pre-build event to copy working web.config to your test project directory.
Set the command line of the pre-build event of test project to string like this:
copy $(SolutionDir)\YourWebAppDir\web.config $(ProjectDir) /y
After that your tests will always run with actual web.config version.
Comment to pcampbell's answer:
I think if you use the configSource attribute you can just set it to the same path in web.config of your web app and app.config of test project and that makes not necessary to use build events.
sorry, I can't leave comments yet.
To expand on bniwredyc's answer, perhaps consider:
refactoring your web.config to reference a new config file like appSettings.config or similar.
modify your project's web.config to:
<appSettings configSource="appSettings.config" />
modify your Unit Test project's app.config to use this file as well.
modify your post or pre-build events to copy just this file.
this also helps ease of deployment in Test/Staging/Prod
Ultimately, the web.config is an XML file. You could generate a schema to validate the sections required are present and that required values have been populated. Obviously, you couldn't contextually validate any sort of business logic that the configuration might contain, but you could use a combination of an XSD validation plus a lightweight class that is used to parse conditions within the file.
Used in conjunction with a copy pre-build event you actually create a very nice test harness for your production quality configurations.
In our web applications, we seperate our Data Access Layers out into their own projects.
This creates some problems related to settings.
Because the DAL will eventually need to be consumed from perhaps more than one application, web.config does not seem like a good place to keep the connection strings and some of the other DAL-related settings.
To solve this, on some of our recent projects we introduced a third project just for settings. We put the setting in a system of .Setting files... With a simple wrapper, the ability to have different settings for various enviroments (Dev, QA, Staging, Production, etc) was easy to achieve.
The only problem there is that the settings project (including the .Settings class) compiles into an assembly, so you can't change it without doing a build/deployment, and some of our customers want to be able to configure their projects without Visual Studio.
So, is there a best practice for this? I have that sense that I'm reinventing the wheel.
Some solutions such as storing settings in a fixed directory on the server in, say, our own XML format occurred to us. But again, I would rather avoid having to re-create encryption for sensitive values and so on. And I would rather keep the solution self-contained if possible.
EDIT: The original question did not contain the really penetrating reason that we can't (I think) use web.config ... That puts a few (very good) answers out of context, my bad.
System.Configuration.ConfigurationManager.ConnectionStrings and System.Configuration.ConfigurationManager.AppSettings
Contain settings from the executing application so in your DAL you can get the settings stored in your web.config file.
For your system you can create a custom configuration section that will sit in your web.config file or your DAL's consumer*.config file In these config files you can specify that they load from a separate config file of your design and location.
Referencing external config files from Web.Config
How to: Create Custom Configuration Sections Using ConfigurationSection
Alternativly you can manualy load your DAL configuration data from any file using ConfigurationManager.OpenExeConfiguration
You can add the equivalent to a web.config file called app.config that compiles into a file named for the dll or exe project of your code behind. This is completely changeable without having to recompile. You can use the standard settings for connection strings and various app settings that can be defined in a key/value pair - or with a little more work you can define your own custom config settings class and section. You can even reference settings in your app config - so you could have 3 settings stored in your app (DEV, QA, PROD) and then only reference the one you want at runtime in your app.config file. Here is an example of one that was created for a webs service setting.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<section name="{Project}.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</sectionGroup>
<section name="microsoft.web.services3" type="Microsoft.Web.Services3.Configuration.WebServicesConfiguration, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</configSections>
<applicationSettings>
<{Project}.Properties.Settings>
<setting name="{SettingName}" serializeAs="String">
<value>{SettingValue}</value>
</setting>
</{Project}.Properties.Settings>
</applicationSettings>
<microsoft.web.services3>
<security>
<securityTokenManager>
<add type="Microsoft.Web.Services3.Security.Tokens.UsernameTokenManager, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" namespace="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" localName="UsernameToken" />
</securityTokenManager>
</security>
</microsoft.web.services3>
</configuration>
It sounds like you do not understand how web.config/app.config work, if I'm reading you correctly. Let's say you have a structure like the following:
DAL Project
References:
Some core libraries
Miscellaneous references
Classes:
DatabaseHelper
ObjectClass1
ObjectClass2
etc...
Web Project
References:
Some core libraries
DAL Project
Miscellaneous references
Pages:
Default.aspx
SomePage1.aspx
etc...
Web.config
In your DatabaseHelper class, you might reference the connection string like so:
string connString = ConfigurationManager
.ConnectionStrings["myConnString"]
.ConnectionString;
When this happens at runtime, your DatabaseHelper class will be running under the same app domain as your web page, and thus, any calls to ConfigurationManager will load the request from the web.config file provided by the web project.
Therefore, you only need the one config file in your web/console/winforms/etc... project, and do not need to worry about having one at design time in each of your class library projects.
If you actually run your DAL as a service or a separate console application or something, then and only then would you need to give the DAL project it's own app.config / web.config file.
Split it up. Use the fixed-XML storage file solution for the database connection, encrypted with .NET's built-in encryptor functions (do not roll your own). Then, using the resultant database connection, look up your 'settings table' in the database. This way you can modify your settings without a redeploy. If your customers need to be able to change the database connection string without visual studio, just write a small Windows Forms app that is capable of generating the encrypted connection string and saving the fixed-XML storage file, and, if necessary, also can connect to DB (via that same file) and modify the Settings table as the user needs.
A completely different approach would be to use SQLite and store all your application settings in there. You can password protect the database in question, if that is of importance to the application, and you can create some simple property/value tables to store the data.
Using the SQLite ADO adapter would only require 1 additional DLL into the projects to access the settings and the SQLite DB itself would be accessible to those folks that don't want to use Visual Studio. There is even a plugin for Firefox to interact with SQLite databases.
You could store the settings in any old Xml file and use the XmlSerializer to take your class and convert it to < - > from Xml. In another answer I wrote some code that did just that. The linked answer serializes a list of simple objects, but it also works to serialize one large configuration object.
Because the XmlSerializer serializes to/from public properties, if you don't want to allow values to change, you might need make the class itself immutable (popsicle style) or have a read-only facade that sits in front of the deserialized one.
It's a handy trick. You can set it up via ConfigurationManager.AppSettings[] with it's own config section and external file references, or you can alternatively just hardcode a specifc xml filename per configuration class.
Take a look of Config.Net - the easiest configuration framework for .NET developers.
A comprehensive easy to use and powerful .NET configuration library, fully covered with unit tests and tested in the wild on thousands of servers and applications.
You could have a Interface that mapped you settings that is used on your DAL. Then on the App you could just use IoC to feed the settings to the DAL.
If you're using a DI framework (like Unity) you can specify constructor arguments. So, hypothetically, your DAL provider could have a constructor that takes its connection string.
I know you can't enforce constructors in interfaces, but that's something we have to deal with. I know the framework has a few places where there are unspoken dependencies on constructor signatures...
Have a look at DslConfig. It seems this solves what you are looking for.