Multiple AppSettings.config files in a hierarchy - asp.net-mvc

I have a question that has struck me a couple of times when creating ASP.Net MVC applications: Say you have one application that you want to deploy to multiple customers. The application code is identical, but you want to be able to have one appSettings.config file for each customer, so that you are able to deploy to different customers by just changing the configSource of the appSettings tag in web.config ( a bit simplified, but still).
Then you realize that 50% of the content in appSettings.config is common for all customers, and only 50% is customer dependent. What you may end up doing is having duplicated entries in all the appSettings files, which is a major pitfall as you then need to remember to update all of them if you want to do an application-wide change to the config.
In a case like this I would really like to have some sort of hierarchical system where you are able to have a "base config" and a "customer config" in separate files. Then I would like the ConfigurationManager to first check for a value in the customer config, and if it is not defined there it will go to the base config instead.
I haven't found a straight-forward way of solving this with the out-of-the-box functionality in ASP.Net MVC4. Does it exits, or do I need to work my way around the default ConfigurationManager class somehow? I could potentially create my own class and replace all calls to ConfigurationManager.AppSettings[key] with a call to my own implementation, but I'd rather avoid that if I could. I want to be able to take use of some of the basic functionality that the built-in ConfigurationManager takes care of, like caching, etc.
Anyone who has solved a similar problem like this before? I keep thinking that it seems like a common scenario..

It is a common scenario, and there are different ways to solve it. One way would be to use config transforms. You could have a Web.Customer1.config, Web.Customer2.config, etc, just like you have Web.Debug.config and Web.Release.config. In the customer-specific transform files, you could "override" only the appSettings that your customer wants to customize.
To create the different transforms, first create different project platforms. Go to the Visual Studio Configuration Manager, and in the Configuration column for your web project (or any project that needs customized configuration settings), click the dropdown and then click <New...>. Name the New Project Configuration Customer1 or whatever you want, check the box for Copy settings from, and pick Release from that dropdown. Also check the Create new solution configurations checkbox.
Finally, right click your web.config file and click Add config transform. This will generate a template Web.Customer1.config file for you. Edit it to override the appSettings it needs to, using the xdt: config transform attributes. Then you can publish the project using the Customer1 solution build configuration. As part of the build, the web.config will be transformed and you will end up with a different web.config file for each customer. You can also use this to customize projects for different deployments, i.e. changing db connection strings, smtp servers, literally anything in the XML configuration file.
As a last thought, make sure you right click each Web.Xyx.config file, choose properties, and set its Build Action to None.
Example:
base web.config
<appSettings>
<add key="CommonProperty1" value="[for all customers]" />
<add key="CommonProperty2" value="[for all customers]" />
<add key="CommonProperty3" value="[for all customers]" />
<add key="CustomProperty1" value="[for one customer]" />
<add key="CustomProperty2" value="[for one customer]" />
<add key="CustomProperty3" value="[for one customer]" />
<appSettings>
web.Customer1.config
<appSettings>
<add key="CustomProperty1" value="The Ohio State University" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
<add key="CustomProperty2" value="Scarlet" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
<add key="CustomProperty3" value="Gray" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
<appSettings>
web.Customer2.config
<appSettings>
<add key="CustomProperty1" value="Michigan University" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
<add key="CustomProperty2" value="Blue" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
<add key="CustomProperty3" value="Maize" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
<appSettings>

Related

ASP.Net MVC4 Routing not working - reserved word?

In short, I have an MVC web app that has a proxy class to marshall requests to another web app under the context of the logged in user.
This all works fine except that some of the outgoing links (i.e. inbound links to my MVC app) from the other web app contain the url "/views".
These requests should be mapped according to this route:
routes.MapRoute(
name: "TableauViews",
url: "views",
defaults: new { controller = "Tableau", action = "Views" }
);
But it never happens. If I change the name of the controller action to something else and enter the corresponding url in a browser, it works.
This leads me to suspect that there is some problem mapping a url containing the word "views" as part of its path. Can anyone confirm this?
The issue is the order of operations. Views is a physical folder and a route. The ASP.NET HttpHandler will read the web.config and block anything going to views before the route handler picks up the URL. If you look at the web.config file in your views folder (where your views are actually stored) you will likely see something like this:
<httpHandlers>
<add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
</httpHandlers>
Also, later in the config there may also be this:
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<remove name="BlockViewHandler"/>
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
</system.webServer>
This is your culprit. I would suggest either naming your route something more like "externalViews" or simply "external" might help. The other alternative is to remove the line above from your views web.config, but this could result in some undesirable behavior.
This article deals with restricting only certain types of files from being delivered instead of blocking all that may be helpful for you.
http://blog.falafel.com/Blogs/j-tower/2014/03/28/loading-javascript-files-from-the-views-folder-in-asp-net-mvc
I couldn't find anything specifically saying "views" is a reserved word, but the article http://haacked.com/archive/2010/04/29/allowing-reserved-filenames-in-URLs.aspx describes how to relax the rules on what words can be used.

Regarding "UnobtrusiveJavaScriptEnabled", what is the default value in MVC 4?

<appSettings>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
Currently, we have UnobtrusiveJavaScriptEnabled set to true in the appSettings
(This can be also be defined in global.asax)
We are looking at cleaning up the appSettings and removing configuration that we do not need to explicitly define (Especially framework stuff), but not sure what it defaults to.
The default value is true, so unobtrusive javascript is enabled by default. You can remove the setting if you wish. And by the way it is the same in ASP.NET MVC 3.

How to disable language embedding in URLs in Sitecore?

After setting up a simple Sitecore site, links to pages sometimes contain the current language:
http://example.com/item.aspx
http://example.com/en/item.aspx
I would like to disable this behavior so that all links point to http://example.com/item.aspx without the language as part of the path. How can this be done?
The web.config has a configuration setting for <linkManager> where the languageEmbedding option can be configured:
<linkManager defaultProvider="sitecore">
<providers>
<clear/>
<add name="sitecore" ... languageEmbedding="asNeeded" ... />
</providers>
</linkManager>
The languageEmbedding options are:
asNeeded
always
never
Changing the config to use languageEmbedding="never" will stop the language from showing up in the URL, and likewise languageEmbedding="always" will force the language to always show up in the URL.
You should not use the "asNeeded" option as it might cause multiple urls for the same pages. Use either "never" or "always" which is much more SEO-friendly

Invoke ASP.NET MVC Controller When Requesting .html file

I need to add some new life to a legacy application :)
I'd like to call an MVC controller when a "static" HTML page is requested in order to add some markup to the page before returning it to the client.
I tried to follow the approach found in this thread: How to read web.config settings in .html page?
...but even though I have this route defined:
routes.MapRoute(
name: "Topic",
url: "html/{fileName}.html",
defaults: new { controller = "Topic", action = "Index" });
the controller is not being called. I have my web.config defined with:
<remove name="WebServiceHandlerFactory-Integrated" />
<add name="HTML" path="*.html" verb="*"
type="System.Web.UI.PageHandlerFactory"
resourceType="File" preCondition="integratedMode" />
I suspect that I need to call something else besides the PageHandlerFactory or perhaps the issue is something entirely different.
UPDATE: My dev environment is working with integrated pipeline mode, but I need to check if my production environment will support it.
If you do this:
routes.RouteExistingFiles = true;
You should find this works - even without the handler addition. In the controller you can load the HTML directly using the HostingEnvironment.VirtualPathProvider's GetFile method and do something with it - or better still just use a normal MVC view that renders the same content as the static file, just with your additions.
Although be aware that this means any files that are potentially caught by any routes will be pushed into the MVC pipeline. This isn't generally a concern, however, if decent separation of routes and physical paths is used.
I setup the same situation as you and it worked well for me, so you have the key components in place. Some things to keep in mind for the testing and troubleshooting:
Your web.config does need the build provider for the html extension:
<system.web>
<compilation>
<buildProviders>
<add extension=".html"
type="System.Web.Compilation.PageBuildProvider" />
</buildProviders>
</compilation>
</system.web>
A copy and paste of your handlers works for me, so that looks good.
And a copy and paste of your MapRoute works for me too, although I used the default Home controller in a clean project. So as a double check just confirm that you have a controller called Topic with an ActionResult method called Index().
And make sure that your url is localhost.com:{port}/html/test.html with the /html/ in the path since your rule asks for that.
Another good test is to change your MapRoute to use aspx instead and test an aspx page and see if that works. That will confirm whether or not it's the IIS mappings or if it's the MVC rules. If it works with aspx then the issue is related to the handler, but if it fails with aspx too then it's something with MVC.
Also confirm that you're using IIS Express and not Cassini. Cassini will not handle that correctly, but IIS Express will. You can confirm by right-clicking on your project and you should see a menu option called "Use Visual Studio Development Studio...". That will only exist if you are currently using IIS Express.

Creating a strongly-typed view in MVC RC1

I'm trying to add a strongly-typed view to the sample app that's created when you create a new MVC project, but I'm not getting something. I added a "Warehouse.dbml" LINQ to SQL file in the Models folder, which contains one table. When I go to the Views folder and right-click and select Add | View, all I see under "Create a strongly-typed view" are the types that come with the sample app:
MyProject.AccountMembershipService
MyProject.FormsAuthenticationService
What step(s) am I missing for my table to be an available type?
Have you tried looking in your web.config and including those namespaces?
<namespaces>
<add namespace="System.Web.Mvc"/>
<add namespace="System.Web.Mvc.Ajax"/>
You would add another element with something like
<add namespace="MyProject.Data" />
That should fix it.
Ran into the same thing today, and my own classes didn't show up in the dropdown box. Weird enough later on they did. Maybe you need to compile your project first ?
Also saw some other people on forums / blogs suffering from the same issue. If you just type the name of your model in the dropdown / textbox, does that work ?

Resources