FileConfigurationSource::GetRootedCurrentConfigurationFile

Topics: Enterprise Library Core
Mar 9, 2010 at 7:12 AM

It seems impossible to properly resolve configuration files under asp.net development web server via' FileConfigurationSource'.

Here is a version of the code that would seem more appropriate, thoughts? It re-roots the path if it is not rooted (before checking that the path is valid and throwing), and also uses the executing assembly's Location in lieu of the appdomain host's BaseDirectory. I haven't built yet, I'm hoping there is a workaround that doesn't require me to rebuild the source and reference unsigned assemblies.

 

        private static string GetRootedCurrentConfigurationFile(string configurationFile)
        {
            if (string.IsNullOrEmpty(configurationFile))
                throw new ArgumentException(Resources.ExceptionStringNullOrEmpty, "configurationFile");
            if (!Path.IsPathRooted(configurationFile))
            {
                configurationFile = System.IO.Path.Combine(System.Reflection.Assembly.GetExecutingAssembly().Location, configurationFile);
            }
            if (!File.Exists(configurationFile))
            {
                throw new FileNotFoundException(
                    string.Format(
                        CultureInfo.CurrentCulture,
                        Resources.ExceptionConfigurationLoadFileNotFound,
                        configurationFile));
            }
            return configurationFile;
        }

 

 

        private static string GetRootedCurrentConfigurationFile(string configurationFile)
        {
            if (string.IsNullOrEmpty(configurationFile))
                throw new ArgumentException(Resources.ExceptionStringNullOrEmpty, "configurationFile");

            if (!Path.IsPathRooted(configurationFile))
            {
                configurationFile = System.IO.Path.Combine(System.Reflection.Assembly.GetExecutingAssembly().Location, configurationFile);
            }

            if (!File.Exists(configurationFile))
            {
                throw new FileNotFoundException(
                    string.Format(
                        CultureInfo.CurrentCulture,
                        Resources.ExceptionConfigurationLoadFileNotFound,
                        configurationFile));
            }

            return configurationFile;
        }

 

 

 

Mar 9, 2010 at 8:03 AM

Hi,

what is your specific scenario that leads you to come up with this statement? "It seems impossible to properly resolve configuration files under asp.net development web server via' FileConfigurationSource".

Valiant Dudan
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com

 

Mar 10, 2010 at 11:13 AM

1) Create a Web Site project

2) Create a Web.config, and add a 'configuration' configuration section, so that you can specify configuration sources.

3) Add a FileConfigurationSource into the sources collection, and also set it as the default source. The filePath attribute should be relative to the deployment, in my scenario it is "bin\EntLib.Development.config".

4) Add a link to an existing .config (e.g "Add Existing->As Link") to the root of the Web Site project created in Step 1, in my scenario it is "EntLib.Development.config". Under Project Item Properties mark it as "Content & Copy Always".

5) In the Default.aspx code-behind add a call into the logging facade to write "test" during the init or load process. It doesn't matter.

6) Set your Web Site project as the start project, select Default.aspx in Solution Explorer. Press F5 to launch the ASP.NET Development Web Server.

Upon page load, wherever you access the Logger, it fails. The reason is that the path resolution FileConfigurationSource relies on is process-relative (e.g. it's performing verification/validation against Current Working Directory of the process via File::Exists)). It then attempts to re-root. Unfortunately Visual Studio doesn't launch the ASP.NET Development Web Server with a working folder relative to your bin\Debug\ folder (e.g. where it's serving all your content from) and so path resolution fails. Although this happens with the Web Development server, it could be reproduced with any 'sand box solution' that spawns appdomains and loads assemblies and config from shadow folders, unfortunately the current implementation wouldn't work as expected. Perhaps a heavy-handed unit test could perform a verification.

The code provided expresses the correct order of (i) path resolution relative to the executing assembly rather than 'CWD' prior to  (ii) verification of the files existence, however it is bugged. I can provided updated code which works correctly. It has bugs, for example .Location includes the Assembly Name and the call to Path::Combine() requires only the folder spec to the file.

 

Mar 10, 2010 at 11:15 AM

I assume the version of "EntLib.Development.config" you provide for the scenario has a proper configuration for the logger so that the logger normally succeeds if path resolution doesn't fail.

Mar 10, 2010 at 11:30 AM

Also, I use the term "seems" because perhaps I overlooked something obvious to someone else wrt the asp.net development web server and relative path resolution in entlib.

Mar 11, 2010 at 8:13 AM

Hi,

I tried the steps you have provided. First thing, Is it really a Web Site type project or a ASP.NET Web Application project? I tried doing a Web Site type project, say New -> Web Site ... But in the 4th step its not giving me an option to add as link. Also, in a web site type project, there is no bin folder unless you add it explicitly. Then I tried doing it using a ASP.NET Web Application, everything went fine. The logger doesn't fail. I can send you that solution so you could check if i missed something.

Valiant Dudan
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com

Mar 12, 2010 at 4:49 AM
Edited Mar 12, 2010 at 5:30 AM
AvanadeSupport wrote:

Hi,

I tried the steps you have provided. First thing, Is it really a Web Site type project or a ASP.NET Web Application project? I tried doing a Web Site type project, say New -> Web Site ... But in the 4th step its not giving me an option to add as link. Also, in a web site type project, there is no bin folder unless you add it explicitly. Then I tried doing it using a ASP.NET Web Application, everything went fine. The logger doesn't fail. I can send you that solution so you could check if i missed something.

Valiant Dudan
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com

I apologize, you are correct I meant Web Application Project. I re-read my post and I've been ambiguous as well. Also, the config file needs to be in a project of its own, and that project should be referenced from the Web Application project. The config link is not necessary. The build process will copy this as a content item alongside the rest of the assemblies, thus arriving in the bin/ folder of your web app once built. 

I have created a test solution which reproduces the problem @ http://mrshaunwilson.com/other/EntLibConfigLoadFailureWeb.zip 

Simply Press F5 and FileConfigurationSource will throw a FileNotFoundException when the Logger.Write() call is made in Page_Load. I assume in a perfect world this would work without throwing an exception. The path is correct, and the build shows the proper content, but the path being resolved by File.Exists() is the path of the asp.net development web server (or visual studio, depending on how you look at it) and not from where the content is being served (e.g. your web app (or project) root.

I believe a fair method of resolution that would work for more people than it does right now would be:

1. If File.Exists(filePath) then return 'FilePath' unchanged.

2. If Exists() failed, and IsPathRooted() == true, then throw FileNotFoundException.

3. if !IsPathRooted() then concatenate the Executing or Calling Assembly Location (w/o the assembly name) with the content of 'filePath' to produce a 'new filePath', perform a File.Exists() on this 'new filePath', if it exists return this 'full path' to the caller.

4. If 'new filePath' from step 3 did not exist, you can optionally attempt to re-root it using the AppDomain Current Domain BaseDirectory, and if that fails, the GetExecutingAssembly for the primary app domain.

5. Lastly, no value should be returned from the function unless a call to File.Exists() succeeds on the value.

This may seem a but much, but without some modification it's broken for me. Currently I believe that if File.Exists() can't resolve the location you throw FileNotFound, and then if it's not a 'full path' (e.g. not rooted to a drive) you provide a rewritten version that isn't always correct. A nice alternative would be if we could provide our own path resolver, but this is the equivalent of bloat given that .config files generally only find themselves in specific locations depending on your project/solution config and build process, it would make sense if all standard locations were probed for a configuration file before FileNotFoundException was thrown.

Let me know if I can provide more info. My current work-around is to include my config directly within my Web.config, I have multiple environments to deploy to and wanted a different configuration source for each. Thus, rather than duplicating our Web.config I wanted to duplicate our EntLib (and similar) configs and reference the environment by name (current it is a function of the build process which configs get referenced, which is problematic over time as the size of solutions get larger. EntLib seems like it may offer an alternative to build-time management of run-time config, so that's my ultimate goal.

Thanks!

 

 

 

Mar 12, 2010 at 9:49 AM

Hi,

Yes I have saw the error that you are mentioning. The first thing that I've noticed is you are using ent lib 5. So what I did is to compare between the code of the 4.1 version to the 5.0 version. It seems that the method GetRootedCurrentConfigurationFile is new to the just released beta version of the 5.0. I also tried changing the references of your sample app to 4.1 and everything works fine. We'll be investigating further about that problem. In your last line of post, "EntLib seems like it may offer an alternative to build-time management of run-time config, so that's my ultimate goal", do you mean you are looking for an alternative of using the config? If it is, there is a new feature of ent lib which is called the fluent configuration interface, where in you can create the whole configuration thru code. Please see the documentation for details.

Valiant Dudan
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com

 

May 11, 2010 at 11:11 PM

Here is, what I think, is corrected code.  It doesn't make sense to use Path.Combine() in the return and not in the File.Exists() check.

 

        private static string GetRootedCurrentConfigurationFile(string configurationFile)
        {
            if (string.IsNullOrEmpty(configurationFile))
                throw new ArgumentException(Resources.ExceptionStringNullOrEmpty, "configurationFile");

            // corrected config file existence check, does Path.Combine() if not absolute path
            if (!File.Exists(Path.IsPathRooted(configurationFile) ? configurationFile : Path.Combine(AppDomain.CurrentDomain.BaseDirectory, configurationFile)))
            {
                throw new FileNotFoundException(
                    string.Format(
                        CultureInfo.CurrentCulture,
                        Resources.ExceptionConfigurationLoadFileNotFound,
                        configurationFile));
            }

/*
            if (!File.Exists(configurationFile))
            {
                throw new FileNotFoundException(
                    string.Format(
                        CultureInfo.CurrentCulture,
                        Resources.ExceptionConfigurationLoadFileNotFound,
                        configurationFile));
            }
*/
            return
                Path.IsPathRooted(configurationFile)
                    ? configurationFile
                    : Path.Combine(AppDomain.CurrentDomain.BaseDirectory, configurationFile);
        }

Dec 6, 2010 at 11:49 AM

Any news on this issue? The final bits of Entlib 5 still have that (wrong) behavior...

Dec 6, 2010 at 11:34 PM

It'll probably be fixed in the next version.  Another workaround for this would be to create a custom file configuration source where the sample code is posted in this thread.

 

Sarah Urmeneta
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com