views:

452

answers:

3

I'm writing a basic little forums web app (for fun and to sharpen the ole' saw), and I'm having a bit of trouble with AppSettings.

My plan is to have these settings in their own file (Settings.config), to which I will grant modify permissions to the web process user account, and store all editable settings in this file (e.g. forum title, description, etc).

This is my code:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(FormCollection collection)
{
    try
    {
        var config = WebConfigurationManager.OpenWebConfiguration("~/Web.config");

        config.AppSettings.Settings["SiteTitle"].Value = collection["SiteTitle"];
        config.AppSettings.Settings["SiteDescription"].Value = collection["SiteDescription"];

        config.Save(ConfigurationSaveMode.Minimal, false);
        ConfigurationManager.RefreshSection("appSettings");

        return RedirectToAction("Index");
    }
    catch (Exception ex)
    {
        ModelState.AddModelError("_FORM", ex.Message);
        return View("Index");
    }
}

...but running it returns the following error:

A configuration file cannot be created for the requested Configuration object.

I've tried granting full permission to all users to the settings file, with no effect (I'm currently just running under Cassini, so the process user is me who has ownership of the file in any case).

Any ideas?

A: 

If you are running this from another project then you'll be using the config from that folder which wont have a config file hence the error that it cannot be created.

You may want to use server.mappath or something like that to get to the web folders config file.

griegs
Could you clarify what do you mean by "running this from another project"?
Keith Williams
If you have your web site and you have your say business layer, and the code is running in the business layer project then i suspect the config file it's looking for will be in the business layer project. you need to get the path to the web config file
griegs
Ah, I see... actually no, whilst I normally set things up like that, in this case I'm being lazy and have the business layer in the "Models" folder of my MVC app.
Keith Williams
@griegs:This is not true. System.Configuration reads configuration from the actual AppDomain's configuration file. So even if the business layer is in a different project or a different DLL, if you call it from your application, it will still use your application's configuration.
Venemo
A: 

Try this:

var configFile = HttpContext.Current.Server.MapPath("~/Web.config");
var config = WebConfigurationManager.OpenWebConfiguration(configFile);

However, I think it is a bad idea to store this kind of information in the Web.Config, especially if it is intended to be changed dynamically.

Even if you plan to use a separate config file, I would rather store this in another way, and I wouldn't bother to get my own configuration through System.Configuration's classes. They are mostly meant to read ASP.NET apps' Web.Config and Windows apps' App.Config, and making them work with something else is really pointless.

I recommend the following:

Add a separate XML file to your project. (preferably to the App_Data folder, where it can't be accessed from the web, and where your application already has read and write permissions.)

You can then store this sort of settings in that XML, and easily read and write it using System.Xml or LINQ to XML.

Venemo
Yeah, I know it may not be the best way to do this - I really just wanted to see if it worked, and try out something different (and was too lazy to set up a settings table in my database, if I'm honest ;) )
Keith Williams
+6  A: 

Change your first line to this:

var config = WebConfigurationManager.OpenWebConfiguration("~");

Confusing as it may be, OpenWebConfiguration expects the virtual path where the Web.config resides, excluding the file name. I guess the logic is that there will only be one Web.config in any given directory so including the name is unnecessary.

The MSDN documentation ultimately clues us in here - if you look at the examples they all use explicit relative paths, and when hosting under IIS allow you to specify config files from other locations, for example:

OpenWebConfiguration("/siteName", "Default Web Site", null, "myServer");

Addendum:

So why does OpenWebConfiguration("~/Web.config") work at all? I'm not sure I can definitively explain it, but try this for kicks: change it to ("~/Foo.bar"). The same result! You can read - your same Web.config file - but cannot write! (now try adding the foo.bar directory to your site, then put a Web.config inside it...)

Since OpenWebConfiguration expects a directory (and apparently allows non-existent ones, as long as it finds a Web.config within the parent), I think this is why mistakenly specifying ~/Web.config as a "path" allows us to load the root config, but not save it.

Kurt Schindler
That's actually a very interesting behaviour I haven't heard about.Thanks very much for explaining it as I was also a little clueless in this.
Venemo
Thank you, that's very interesting - I'll give this a go tonight!
Keith Williams
Worked like a charm - thanks!
Keith Williams
This was very helpful. Thanks! :)
aprescott