views:

313

answers:

9

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

A: 

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.

Bill Karwin
A: 

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)

Glennular
A: 

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.

Daniel Pratt
A: 

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

marc_s
A: 

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.

iZ
A: 

Try WDP.

cottsak
A: 

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!

+1  A: 

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
Kevin Lewis
A: 

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