asp.net managing multiple web.config files - asp.net

Can anyone suggest a good way to manage multiple web.config files?
For example, I have a development web.config I use on my localmachine named "localWeb.config" and then one I use for production called "prodWeb.config". I keep the .config extensions so no one can access them from the web server.
localWeb.config will for example have a connection string to a development database while prodWeb.config has one to the production database.
Currently, I am using a postbuild event that will copy the contents of localWeb.config or prodWeb.config to web.config. This works well but I was wondering if anyone knew of any better ways.

You could see if the method outlined on this CodeCarnage page suits your needs better. It utilizes the configsource attribute to point to DB and APP settings, each of which are seperated by environment. This could perhaps allow you to keep the consistent stuff in web.config and keep environment specific "stuff" in other config files.

Check out the Web Deployment Tool:
Here are just a few of the tasks that
be accomplished using the tool:
Create a package that contains content, configuration and SQL
databases for deployment or sharing
with others.
Use the package as a way to version your application or create backups.
Add parameters to replace a connection string or other value in a
file during install of a package.
Enable non-administrators to deploy packages and granularly control their
access.
Synchronize or migrate both sites and servers running IIS 6.0 and IIS
7.0.

this will continue to be a hack until asp.net 4 arrives http://www.asp.net/learn/whitepapers/aspnet40/#_Toc223325501

I keep a single config file and use logic at runtime to detect of which subset of configuration to use. If the hostname is "prodsrvr-*" then I use the production server configuration, including db connections, etc. If the hostname is "test-*" then I use the test config data. And etc.
EDIT: here's some code that does what I described.
public static string GetConnString()
{
string connString = ConfigurationSettings.AppSettings[GetConfigKey("database")];
return connString;
}
public static string GetConfigKey(string baseKey)
{
string str = baseKey;
if (Dns.GetHostName().StartsWith("dinoch"))
{
str = str + "-dev";
}
else if (Dns.GetHostName().StartsWith("prodsrvr"))
{
str = str + "-prod";
}
return str;
}
<configuration>
<appSettings>
<add key="database-dev" value="server=(local)\vsdotnet;database=ASPXAPPS;Integrated Security=SSPI" />
<add key="database-prod" value="server=(local)\vsdotnet;database=ASPXAPPS;Integrated Security=SSPI" />
</appSettings>
</configuration>
I use a simple StartsWith() , with some "magic strings" hardcoded.
To avoid that, I could imagine defining a map of regex's to suffixes in the web.config file. Where, if regex1 is a match on the hostname, then use suffix1. If regex2, then use suffix2. Etc. You would load the map just once, and then just to a foreach() enumerating through the regex's and returning if they match the hostname.
Or, maybe there is a different criterion you want to use, to distinguish prod from dev from test servers, other than hostname. It's up to you.

Related

customising web.config based on domain name

Most of my websites include one LIVE (production) and two TEST environments which are accessible via three different domain names e.g.
www.mysite.com
test1.mysite.com
test2.mysite.com
Each of the above are IIS Websites which point to the same physical versioned folder when they are all running the same version of the website.
What I typically do when releasing a new version is to place the new version into a new physical folder e.g. /inetpub/wwwroot/mywebsite/v41/ and point one of the TEST sites to that version of the site and test it. Once passed, the LIVE (and other TEST) websites are also repointed to the new version (e.g. v41).
Now my problem is this. Each website has its own database (TESTs have a copy of LIVE which can be refreshed via a couple of SQL BACKUP/RESTORE commands) however, the three sites are all "looking" at the same web.config file and therefore the same Database Connection Strings (either a System.Data.SqlClient or System.Data.EntityClient provider).
Is there any way that I can configure web.config to provide different connectionStrings based on the domain name/IIS website of the incoming request?
Maybe a tag or an attribute that qualifies a given tag?
I've looked all over for a solution but not yet found one.
Thanks in advance,
BloodBaz
there are two ways to manage multiple environment Specific Web.config file....
using t4 template,below is the link for that
http://ilearnable.net/2010/08/02/t4-for-complex-configuration/
VS Configuration Manager and create new "LIVE", "test1", "test2" build configurations for your project,check out the link
http://weblogs.asp.net/scottgu/archive/2007/09/21/tip-trick-automating-dev-qa-staging-and-production-web-config-settings-with-vs-2005.aspx
hope this helps..
why not split your config file up so connectionStrings.config is its own file. Then you can deploy everything and not overwrite that connectionString file.
where you normally would put the connectionString do this
<connectionStrings configSource="connectionStrings.config" />
Then create a file named connectionString.config
<?xml version="1.0"?>
<connectionStrings>
</connectionStrings>
Alternatively you can create another build option other than just release/debug. You can have web.config transforms that output a different config file depending on which one is selected.
I had a similar problem; I solved it by setting up all the database connection info in the same web config file, and then writing a handler that decides on the fly, based on the request and context, which environment it's in and uses that database.

Storing connection strings in web.config

Why do we store connection strings in web.config file? What are the benefits of doing so?
The web config file is used so you can reference the connection anywhere in your app. It avoids having to deal with a fairly long connection string within your application.
Here is a good article which may give you detailed information: http://articles.sitepoint.com/article/web-config-file-demystified#
There is even a wiki page for it :) (surprisingly):
http://en.wikipedia.org/wiki/Web.config
If you ever move / change to a different database format the connection string setting inside of the web.config file can be changed in one place rather then digging through your entire application looking for it. This also avoids having to recompile or build an application to get the new connection string setting.
If you are wondering how to access the information from within a web.config file that can be found here:
http://msdn.microsoft.com/en-us/library/4c2kcht0(VS.85).aspx
There is also sample code right in there.
Imagine you have several classes which access your database; you can:
Place your connection string in every class
Create a constant to store that value
Place it inside your configuration file and to refer it
These have following characteristics:
Every time a connection string changes, for instance, migrating from development to production environment, you'll need to change everywhere;
By using a constant, you just need to change a single place. But in this case, when your string needs to be changed, you'll need to recompile it.
Same as above, without that recompile part. You can to develop your application and other people set that database connection to you
So, by storing a connection string into your web.config file you gain more flexibility to change that setting than other alternatives.
Reason #1
As everyone is mentioning, having the connection string in the web.config makes it easy to update/change as needed. It becomes a single source where the arguments can be easily be changed.
Reason #2
Beyond that though, IIS is configured not serve up web.config to users who request the file. If your website is,
www.mydomain.com
someone can't hit http://www.mydomain.com/web.config and scrape all your confidential settings, passwords, and so forth.
(As a side, note, IIS won't serve up files in the App_Code directory either to a user, so the web.config file isn't unique in this respect.)
Reason #3
ASP.NET automatically detects changes to configuration files and will immediately applies new settings.
More info..
MSDN has a discussion of the ASP.NET configuration system at,
http://msdn.microsoft.com/en-us/library/aa719558%28VS.71%29.aspx
What I like most about having the connection string in the web.config is when you have multiple environments that you test on. Say you have a Test server, Staging server and a Production server. Well you don't need to have code that checks which server you're on, or have to recompile different versions for each... just use the web.config to store your connection strings and that way you can have a different web.config on each server but the same web application with no hassles. You may want to Encrypt your Connection String Settings as well so they're not visible to everyone that has access to the folder.
You can reference them using the ConfigurationManager via the ConnectionStrings property.
http://msdn.microsoft.com/en-us/library/system.configuration.configurationmanager.connectionstrings.aspx
It allows the connection string to be configured by someone maintaining the site.
You don't have to re-build the application if the connection string changes. An admin can make the change and the code remains the same.

How to conditionally return web.config entry

How do I return web.config entry conditionally.
I.E: If the web.config folder is 'C:\inetpub\www\myTestSite' then return 'connectionString' TestDB
else return 'ProdDB'
You can try something like this:
string conn;
if (Server.MapPath("~/web.config").StartsWith(#"C:\inetpub\www\myTestSite"))
conn = ConfigurationManager.ConnectionStrings["TestDB"].ConnectionString;
else
conn = ConfigurationManager.ConnectionStrings["ProdDB"].ConnectionString;
Although I would recommend a different approach, either by using separate config files for your sites or by using a more sophisticated method of testing where your code is located. What I do is put a key in appSettings in the machine.config file for my development machine, as well as my test, and production web servers so that my application knows what environment it is running on. I use the value of that appSetting to determine which connection string to use.
It is much better to use different config files for the different wnvironments.
You can have the following in your web.config file:
<connectionStrings configSource="SiteSettings\connectionStrings.config"/>
where the configSource is path relative to the path of the web.config file. Then you can easily create 4 different connectionStrings.config files in your project: connectionStrings.config.dev, connectionStrings.config.test, connectionStrings.config.staging, connectionStrings.config.prod. With that set up you can configure your build process to automatically choose the right connectionStrings.config file for the right environment. This way maintenance will be a lot easier.
ScottGu has an article describing this method. And one more article about Customizing web.config for different environments which you may find helpful.

Read a web.config file in a virtual directory

I have an ASP.NET application (Root Application) that has a virtual directory set up to another ASP.NET application (Virtual Application). How can I make the Virtual Application read values from the Root Application's web.config file? I was looking at the WebConfigurationManager.OpenWebConfiguration() class, but I'm unsure how how to tell it to go up one level from the root. For example, I would tell it to go to ~/web.config to get the the Virtual Application's web.config file, but I need it to go up one more level to the Root Application's file structure. Is this even the correct approach?
Any help would be greatly appreciated!!
You can use the ExeConfigurationFileMap class with ConfigurationManager, like:
string configFile = new FileInfo(Server.MapPath("/Web.config")).Directory.Parent.FullName + "\\Web.config";
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = configFile;
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
Response.Write(config.AppSettings.Settings["Test"].Value);
IIS does have programmatic access to its configuration data (which is documented on MSDN and/or Technet). This will be the only supported route (i.e. will continue to work across IIS versions).
Otherwise you can hack a solution (both of these will require higher than usual rights for the process):
Parse the output from appcmd.exe:
E.g. here:
> C:\Windows\system32\inetsrv\appcmd.exe list vdir
VDIR "Default Web Site/" (physicalPath:E:\Dev\weblocal\XYZ)
VDIR "Default Web Site/DevRoot/TestWebClient" (physicalPath:E:\Dev\Tests\ClientSideWeb)
VDIR "Default Web Site/Home" (physicalPath:E:\Data\Homepages)
Read the configuration directly from C:\Windows\System32\inetsrv\config.
I think you will find that your desired behavior is in fact the default behavior.
web.config settings cascade down. Your app will look up to the next hierarchical web.config if it can't find the value.
This would allow you to just look up via AppSettings etc. for most cases.
I'm not sure what happens if you really need to direct access to the file as opposed to the various config api access methods.
I have a setup like this right now using IIS7 with multiple virtual apps configured.
I tried HectorMac's suggestion again and it still doesn't work for me. As a result I am going to seek an alternative to storing my value in the web.config file.

How to make sure we publish production to the prod server and test to the test server

I have an entry in my Web.Config file that indicates which environment I am in for connection strings and junk:
<add key="AppEnv" value ="2" /> <!--(0 = Dev, 1 = test, 2 = prod)-->
I am looking for a way to alert the developer, at the time of publishing, to make sure they have checked this key/value so that they don't publish the 'test' to the 'prod' server and vice versa.
Thanks
I have come up with my own (probably unconventional) solution to this issue. We develop many different web projects for many different clients and have migrated all of them to this method due to all of the issues we have had with multiple web.config files, or required edits before publishing.
Basically, we let our app tell us which environment its running under based on the incoming URL. We initialize this on the first request and store it in memory for the life of the app. This way we can store each of our environment specific config values in the same config file and just qualify them with Development, Staging, Production, etc. And any settings that dont differ between environments dont need to be qualified.
First a sample web.config:
<appSettings>
<add key="DevelopmentHost" value="dev.trackmyhours.com" />
<add key="StagingHost" value="staging.trackmyhours.com" />
<add key="ProductionHost" value="www.trackmyhours.com" />
</appSettings>
<connectionStrings>
<clear />
<add name="DevelopmentConnectionString" connectionString="your dev conn string" providerName="System.Data.SqlClient" />
<add name="StagingConnectionString" connectionString="your staging conn string (mine is typically same as staging)" providerName="System.Data.SqlClient" />
<add name="ProductionConnectionString" connectionString="your production conn string" providerName="System.Data.SqlClient" />
</connectionStrings>
Next we have an "App" class that gives us access to our "Site" class, but you could architect your classes however you see fit.
Public Class App
Private Shared _Site As New Site
Public Shared ReadOnly Property Site() As Site
Get
Return _Site
End Get
End Property
End Class
Imports System.Configuration
Imports System.Web
Public Class Site
Public Enum EnvironmentType
Development
Staging
Production
End Enum
Friend Sub New()
If HttpContext.Current IsNot Nothing Then
Dim URL = HttpContext.Current.Request.Url.DnsSafeHost
Select Case URL
Case ConfigurationManager.AppSettings("DevelopmentHost"), "localhost"
_Environment = EnvironmentType.Development
Case ConfigurationManager.AppSettings("StagingHost")
_Environment = EnvironmentType.Staging
Case ConfigurationManager.AppSettings("ProductionHost")
_Environment = EnvironmentType.Production
End Select
Else
'probably getting called from a winforms/console app, or unit tests
_Environment = EnvironmentType.Staging
End If
_ConnectionString = ConfigurationManager.ConnectionStrings(_Environment.ToString & "ConnectionString").ConnectionString
End Sub
Private _Environment As EnvironmentType
Public Property Environment() As EnvironmentType
Get
Return _Environment
End Get
Set(ByVal value As EnvironmentType)
_Environment = value
_ConnectionString = ConfigurationManager.ConnectionStrings(_Environment.ToString & "ConnectionString").ConnectionString
End Set
End Property
Private _ConnectionString As String
Public ReadOnly Property ConnectionString() As String
Get
Return _ConnectionString
End Get
End Property
End Class
We put our classes in our Biz Object class library. We just decided that it is not necessary to determine the environment on every single request, since it really cant change during the app's lifetime. Also, this allows us to reference App.Site.Environment from ANYWHERE in code in the library or code behind. This is also helpful if you need to sprinkle some conditional logic in your code - like not sending emails to real people when running in dev/staging.
One last thing - for our Linq2SQL or EF Data/ObjectContexts, we do not store the connection string in the file, and instead overload the constructor so we can supply our correct environment connection string like this:
Partial Class SampleDataContext
Sub New()
MyBase.New(App.Site.ConnectionString)
End Sub
End Class
Here's a solution: store that file as "Web.Config.in" under source-control.
On each server (dev, test, staging, production), copy Web.Config.in to Web.Config, and edit the AppEnv value carefully.
On each subsequent push from one environment to the next, exclude Web.Config files. That is, don't overwrite that particular file. Write deployment scripts that run rsync --exclude WebConfig for example.
I have in bold red letters on the top of the page what mode the site is in if it is not in Production mode.
I also have a diagnostics page which lists what server is is running on and what database it is connected to.
You could also put a lookup table in each database, to make sure you ahve the right flag connected to it. (in the Test DB mark it as 1 and check on Application_Start that the values match)
Whenever there is a process that always requires more than one step, I know I'm going to screw it up at least half the time. For this reason, I love MSBuild.
There are quite a number of useful tasks that come in the box with MSBuild, such as the AspNetCompiler task, which appears to be a one-step compile/publish action.
There are also several projects which aggregate a large number of "custom" MSBuild tasks for various purposes. The MSBuild Community Tasks Project has a XmlMassUpdate task that is useful for making several changes to an xml-formatted file. In other words, perfect for updating web.config files.
I found an example of using the XmlMassUpdate task in concert with a Web Deployment project here.
An eternal question that keeps popping up! I guess just about any serious ASP.NET developer has banged his head against this in some ways.
For ASP.NET 4.0, there's some help on its way - the improved web deployment options will offer a feature called "web.config transformations".
Check out this Channel 9 video on the topic - haven't found any decent written reference just yet...
Marc
I take any keys that will be different in test and production and put them in an entirely new .config file. I put the test settings on test and the production settings on production and never copy this file from test to production. Then, in your regular web.config, you can specify this new .config file like this:
<appSettings file="ExternalWeb.config>
.... common keys go here
</appSettings>
on Test server, "external.config" will contain the values specific to that server and in production, it will have the prod values. This allows you to copy your entire web.config between the 2 servers without ever manually changing the file.
I think its bad practice to store connection strings in web.config, I instead creates a separate config file outside my websites in a common known location like
C:\xxxx\config.xml
Then I store all machine dependent settings there. So that on my live server I have my live settings, on the test server connectionstrings to the test DB and on my Dev machine I got my local setttings. These settings can then also be resued along all websites and .net apps on that server. Only a single place to update when the DB changes.
Perfect!
Set your Web.Config to "Read only", therefore it won't be overwritten when publishing to these servers.
The trade off is that you'll have to manually maintain your web.configs on each server, and your publish will claim to fail as it couldn't overwrite the web.config on the remote server; but IMHO this is preferable to modifying the config each time you do an upload

Resources