Transform config file without key or name attributes - asp.net

I want to transform some attributes but I can't add a name or key attribute to use xdt:Locator="Match(name)" or xdt:Locator="Match(key)".
For example, I could have:
<parentElement>
<children>
<add key="ExampleKey">
<thing attribute="blablabla"></thing>
</add>
</children>
</parentElement>
How could I replace the thing attribute "blablabla" by another value?
UPDATE 1: in addition to that, I can't insert other attributes in the "thing" markup because it throws some errors via the dll that uses the attribute.
UPDATE 2: it appears that I had to use SlowCheetah, and then it worked well. Thanks to the preview, I found out I can add xdt:Transform="Replace" to the parent element, just like this:
<parentElement xdt:Transform="Replace">
<children>
<add key="ExampleKey">
<thing attribute="blablabla"></thing>
</add>
</children>
</parentElement>
And now it works perfectly! :)

You can find the element using an XPath expression with a Condition locator instead of Match, as described in the documentation. i.e.
<?xml version="1.0" encoding="utf-8"?>
<parentElement xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<children>
<add>
<thing attribute="falala" xdt:Transform="SetAttributes" xdt:Locator="Condition(#attribute = 'blablabla')"></thing>
</add>
</children>
</parentElement>

Related

How do I use configSource with configBuilders in ASP.NET?

I'm following https://jeffreyfritz.com/2017/11/modern-configuration-for-asp-net-4-7-1-with-configurationbuilders/ for my ASP.NET 4.7.2 application but we have this in the Web.config:
<connectionStrings configSource="myconfig.config"/>
I was hoping I could transiently set the values in myconfig.config using environment variables by changing this block to:
<connectionStrings configBuilders="Env" configSource="myconfig.config"/>
But this gives me a compiler error:
A section using 'configSource' may contain no other attributes or elements.
Here's what myconfig.config looks like:
<?xml version="1.0" encoding="utf-8"?>
<connectionStrings>
<add name="db1" connectionString="conn1" providerName="prov1" />
<add name="db2" connectionString="conn2" providerName="prov2" />
<add name="db3" connectionString="conn3" providerName="prov3" />
</connectionStrings>
Any ideas? I've been trying to search up how to make the two work in conjuction to no avail!
The solution, for anyone facing this, is to put the "configBuilders" attribute on the target file as so:
<?xml version="1.0" encoding="utf-8"?>
<connectionStrings configBuilders="Env">
<add name="db1" connectionString="conn1" providerName="prov1" />
<add name="db2" connectionString="conn2" providerName="prov2" />
<add name="db3" connectionString="conn3" providerName="prov3" />
</connectionStrings>
Also, because another issue popped up after.. make sure you are using "configSource" as an attribute and not "file".

Transforms for CSS and Views in MVC

I have an MVC application which I am deploying out to a staging server using Team City. I have created a Web.Staging.config transform to handle the different database connection, certificates and service calls.
On this staging server I have had a request to have the title of the Index view to read "TEST SYSTEM" and to have a different colour scheme to signify, at a glance that the user is on the test system.
So far I have handled this by changing the view on the file system in notepad, and swapping the bootstrap.css file to one with a different theme however every time I do a new commit/deploy these changes are wiped and its becoming tiresome.
Is there anyway to handle CSS/view changes per server by using a similar transform system as employed to handle the web.config
There's not really an easy way to transform CSS files like that, but what you could do is have your master CSS file link in your config file, so your view would be something like:
<link href='#ConfigurationManager.AppSettings["MainCSS"]'>
Then your web.Debug (and other transformations) could point to the correct path:
<add key="MainCSS" value="/Content/Site.css" />
And your web.Staging config could point to another:
<add key="MainCSS" value="/Content/Staging/Site.css" />
You can also apply the above logic for your Index view:
public ActionResult Index()
{
string viewName = ConfigurationManager.AppSettings["MainView"];
return View(viewName);
}
Then have in your web.Debug (and others):
<add key="MainView" value="Index" />
And in your web.Staging:
<add key="MainView" value="IndexStaging" />
One solution could be to add a key to the web.config and read it using the WebConfigurationManager.AppSettings. Those settings can be transformed. In your view you can check this setting if you need to change the title and if you need to load an additional css file.
In your web.config:
<appSettings>
<add key="IsTestSystem" value="False" />
</appSettings>
and in as transform:
<appSettings>
<add key="IsTestSystem" value="True" xdt:Locator="Match(key)" xdt:Transform="SetAttributes"/>
</appSettings>
In your view / layout:
if (Boolean.TryParse(WebConfigurationManager.AppSettings["IsTestSystem"] ?? "False", out run)) {
// include css file for test system or modify the title
}
And, if you want, you can just switch between the two bootstrap.css files.

In MVC3, is it possible to create reuseable functions in view?

I'm not talking about javascript functions, but server side functions written in c#.
For the html table in my view, I'm creating table headers that act like sortable columns. But the sortability depends on a complex logic so I want to put the logic into a function instead of writring it for each column.
#Amr ElGarhy
Thank you for suggesting customer helper, but the helper class I create is not being regonizied.
Helper Code:
namespace MyHtmlHelpers
{
public static class CustomHelpers
{
public static string MySortColumn(this HtmlHelper helper, string label, string col, string dir, UrlHelper url)
{
return string.Empty;
}
}
}
web.config:
<pages>
<namespaces>
<add namespace="System.Web.Helpers" />
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="System.Web.WebPages"/>
<add namespace="MyHtmlHelpers"/>
</namespaces>
</pages>
But in View, neither #HtmlHelper.MySortColumn nor #Html.MySortColumn is regonized. Am I missing something here? I even restarted my pc.
UPDATE
adding namespace in web.config didn't work for me. adding in View page works.
I think you need to take a look at HTML Helpers and how to create a custom one:
http://www.asp.net/mvc/tutorials/creating-custom-html-helpers-cs
http://www.asp.net/mvc/videos/how-do-i-create-a-custom-html-helper-for-an-mvc-application
Try this
helper syntax
It's better to create base class with the logic and derive your views from this class. Normally you shouldn't reveal/share any methods to reuse between views.
Are you using areas? Areas have their own view folder and usually have their own web.config. If that's the case, you can try to add namespace to that web.config.
Also, web.config files are resolved hierarchically through folders. It means you can have web.config in your base Area directory which affects all area specific views.
In my case, I have two web.config files where I add my namespaces - one is in View directory and other one in Area directory.

Problem with using the MvcReCaptcha library in a view

The MvcReCaptcha library looks very solid, and appears to have some good developer adoption, but when I tried it for the first time today, using the supplied HTML helper, my app won't run. I have the helper in a partial view as follows:
<fieldset>
<legend>3. Verify that you are a human.</legend>
#Html.GenerateCaptcha()
</fieldset>
I have the namespace included in web.config as instructed:
<pages>
<namespaces>
<add namespace="System.Web.Helpers" />
<add namespace="MvcReCaptcha.Helpers"/>
</namespaces>
</pages>
(Other namespaces removed for brevity)
And I have my private and public keys defined in appSettings. I can see no way that I deviate from the examples on the CodePlex page except that I am using Razor. Can anyone offer some insight into what I may be doing wrong?
If you are using Razor you need to add the MvcReCaptcha.Helpers namespace to the <namespaces> section of ~/Views/web.config file and not the one in ~/web.config.

SiteMap change SiteMapProvider?

I've got a custom menu navigation built from a web.sitemap file, the first line of this would be something like:
SiteMapNodeCollection topLevelNodes = SiteMap.RootNode.ChildNodes;
This works - it gets all the top level nodes from the web.sitemap file, and allows me to look through each SiteMapNode and do stuff.
However, now I want to be able to create multiple web.sitemap files, and then programmatically determine which web.sitemap file to use, but I can't seem to find out how to do this. I'm assuming I could either create one custom SiteMapProvider that can perform the logic to determine which web.sitemap file to load, or I have multiple providers, each one with the SiteMapFile property set to a specific *.sitemap file, and then switch providers programmatically before I access SiteMap.RootNode.
I think it's probably easier to have one custom provider, and then override the part where it looks for the actual physical sitemap file location, but I'm unclear how I would do this
I've googled a lot, but most answers seem to be regarding the standard sitemappath controls and so on, and how to set a SiteMapDataSource, which I don't think is relevant to my approach.
First you need to specify all of your sitemap files in your web.config as such:
<siteMap defaultProvider="FNDSiteMap" enabled="true">
<providers>
<add name="FNDSiteMap" type="System.Web.XmlSiteMapProvider" siteMapFile="FND.sitemap" securityTrimmingEnabled="true"/>
<add name="STASiteMap" type="System.Web.XmlSiteMapProvider" siteMapFile="STA.sitemap" securityTrimmingEnabled="true"/>
<add name="TASiteMap" type="System.Web.XmlSiteMapProvider" siteMapFile="TA.sitemap" securityTrimmingEnabled="true"/>
</providers>
</siteMap>
Then in your code-behind you can dynamically assign your SiteMapDataSource (which is bound to your menu) to one of the providers you specified in your web.config:
.aspx
<asp:Menu ID="MenuLevel1" runat="server" Orientation="Horizontal" DataSourceID="SiteMapLevel1"
MaximumDynamicDisplayLevels="0" IncludeStyleBlock="false">
</asp:Menu>
<asp:SiteMapDataSource ID="SiteMapLevel1" runat="server" />
.cs
SiteMapLevel1.SiteMapProvider = "TASiteMap";
Pauli's comment was the answer to my particular requirement:
"You shouldn't switch/change anything... instead you need to access
the RootNode like this all the time
SiteMap.Providers[someProvider].RootNode and the someProvider should
then be resolved at runtime."
I hadn't realised this was possible, but was the correct solution for me.

Resources