Automate update of web.config with Microsoft Powershell

1 Comment

I have an ASP.NET web site project that I use for 5-10 web sites. Each has its own directory, setup in IIS and MySQL database, ie they work as independent web sites, only the code base is the same. For updates I have a setup with one Windows command file to copy out any file changes to the web site directories and another command file that updates the database with any database structure changes.

So far I have made any updates to the web.config files manually but today I finally figured out how to automate this too.

The Windows command tool Powershell has been around for a couple of years but I have not used it before. It was installed with Windows Update some time ago so it should be installed on your machine. To use it simply run powershell.exe from a command prompt. To exit, close the window or type exit.
Powershell can among other things run Powershell script files (file extension .ps1)

This script will show you how to add to and edit multiple web.config files (or any xml file)

# Source: http://blog.tjitjing.com

# Array of files to make changes to, add as many as you like
$filesarray = @("c:\temp\web.config","c:\anotherfile.xml") 

# Go thru all files
foreach ($filename in $filesarray)
{
	# Get file + cast as xml
	$xml = [xml](get-content $filename)
	
	# Backup file before making changes
	$backup = $filename + "-" + (get-date).tostring("yyyy-MM-dd-hh_mm_s")
	$xml.Save($backup)
	
	$root = $xml.get_DocumentElement();
	
	# Add a new node
	$node = $xml.createElement("mynewnode")
	$root."system.web".appendChild($node)
	$subnode = $xml.createElement("add")
	$node.AppendChild($subnode)
	$attribute = $xml.CreateAttribute("url")
	$attribute.set_Value("http://blog.tjitjing.com")
	$subnode.SetAttributeNode($attribute )
	
	# Change existing node
	$root.connectionStrings.add.connectionString = "My new connection string"
	
	# Save
	$xml.Save($filename)	 
}

There are two caveats I ran in to when running my script:

1) You need to always give the path of the file, even if it is in your current directory. To run a file from current directory you need to wite ./

C:\Temp>powershell.exe ./test.ps1

Or from within Powershell

PS C:\Temp>./test.ps1

2) Powershells execution policy is by default set to Restricted. To avoid getting a message similar to “File C:\temp\test.ps1 cannot be loaded because the execution of scripts is disabled on this system.”, you need to run the command Set-ExecutionPolicy RemoteSigned from the powershell prompt.

Some useful links that helped me understand this and get it working:
Powershell on Microsoft Technet
Andy Schneider’s Blog about Windows PowerShell
Blog post by Dave Donaldson

Get root path in ASP.NET without using Tilde ~

No Comments

As you probably know you can use the tilde sign (~) in ASP.NET to get the relative path of the root of your Web Site or Web Application. But unless using the Link Server Control or similar you may need some code to convert it to a correct url or get the absolute path.

Using a Server Control – example using the HyperLink Web Server Control

MyHyperLink.NavigateUrl = "~/Products/XYZ"

Using HttpRuntime.AppDomainAppVirtualPath

strAbsoluteUrl = HttpRuntime.AppDomainAppVirtualPath & "/Products/XYZ"

Using VirtualPathUtility.ToAbsolute

strAbsoluteUrl = VirtualPathUtility.ToAbsolute("~/Products/XYZ")

Thanks to Yousef

Get URL without QueryString in .NET

No Comments

To get the current Url without the query in VB.NET

Request.Url.AbsoluteUri.Split("?")(0)

Split text in two columns in VB.NET

1 Comment

This is a function code snippet to split a text in to two or more columns in VB.NET.

It is converted from my previous posted: Split text into multiple columns in PHP

Parameters:
InputString – String to be split into columns
Columns – Number of columns
SplitString – String to look for to not split midtext etc

Example of how to use:

        Dim strColumns As String() = SplitIntoColumns(strText, 2, "<h2>")
        strText = "<table><tr><td>" & strColumns(0) & "</td><td>" & strColumns(1) & "</td></tr></table>"

Code snippet:

Function SplitIntoColumns(ByVal InputString As String, ByVal Columns As Integer, ByVal SplitString As String) As String()

        ' Source: http://blog.tjitjing.com
        '
        ' Splits a string into x number of columns and returns result as an array of columns
        '
        ' Parameters:
        ' $InputString String to be split into columns
        ' $Columns     Number of columns
        ' $SplitString String to look for to not split midtext etc
        '
        ' Change history:
        ' 2011-02-04/Max    Version 1
        ' 2011-05-16/Max    Version 1.2
        '

        Dim Output() As String
        ReDim Output(0 To Columns - 1)

        ' Find middle of text
        Dim ColLength As Integer = Len(InputString) / Columns

        ' Split into columns
        Dim ColCount As Integer
        Dim Pos As Integer, LastPos As Integer = 0
        For ColCount = 1 To Columns

           ' Find $SplitString, position to cut
            Pos = -1
            If (LastPos + ColLength) < InputString.Length Then
                Pos = InputString.IndexOf(SplitString, LastPos + ColLength)
            End If
            If Pos = -1 Then Pos = InputString.Length

            ' Cut out column
            Output(ColCount - 1) = Mid(InputString, LastPos + 1, Pos - LastPos)

            LastPos = Pos

        Next ColCount

        Return Output

    End Function

[Edit: Updated function]

.NET Client Side Field Validation not working

4 Comments

This was driving me nuts earlier today, so this post is a kind of reminder to myself not to forget this again:

When using Routing in .NET 4 always remember to add exceptions for axd files!
Example:

routes.Ignore("{resource}.axd/{*pathInfo}")

In my case I could not get standard .Net Field Validation to work client side. It was simply failing, not firing up, kaputt!

This was an old Web Site project that I had not touched in over a year. The last few days I have upgraded it from .NET 2.0 to 4.0, upgraded the Visual Studio solution from VS2008 to VS2010, made a lot of functionality and programming changes and only today discovered that Field Validation was not working anymore 🙁
I tried to trace my steps but I had just made too many changes to figure out just what was causing this.

I spent hours testing and googling. I made the simplest pages with a single RequiredFieldValidator. I played around with ValidationGroup, EnableClientScript and all the properties you can think of, changed just about everything in web.config etc etc. But I could not get it to work in this particular Web Site project. Finally I used the Chrome built-in Developer Tool and saw a few lines like this:

/WebResource.axd?d=znZdQDlEpA5JQFAVOxKRSaHvdB72R3w2rXWu-FUxJtGWwzycXLR0odYnyM26zZuWuR1yv0u-FYgtZehPCh1HbP-J7H7XaSgJhjJvniiZE7c1&t=634214740702488522
Failed to load resource: the server responded with a status of 404 (Not Found)

WebResource.axd is basically Javascript libraries that contains .NET functions for things like Field Navigation, Tree View and Ajax.
Anyway, once I found this I spent another hour or so googling and testing before I finally remembered I had just implemented Routing the other day, added the ignore exception mentioned above and – bingo it was working again. 🙂

In a way something good came out of this because, when client side validation fails it will rely only on server side validation and that means another thing you should never forget – to always add at the top of your button click event:

If Not Page.IsValid Then Exit Sub

I had forgot this, which meant that when my client side field validation – which I had relied on in the past – did not work, my script just kept going even though the fields did not validate. Of course, forgetting this opens up for all kinds of attacks from users who simply block javascript.

(I’m sure all this is already in some best practice document somewhere)

How to remove characters to avoid .NET Request Validation Error (A potentially dangerous Request.Path value was detected from the client)

2 Comments

In .NET Framework 4 url checking – request validation – is different from .Net 2.0. This is the error you will see when you hit this:

Server Error in ‘/’ Application.
A potentially dangerous Request.Path value was detected from the client

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Web.HttpException: A potentially dangerous Request.Path value was detected from the client

If you have control over the url, you can avoid using disallowed characters. The default characters that are being checked for are:
< > * % & : \\

If you set your url’s programtically, e.g. from info in a database, simply do replace on these characters into something allowed.
For example (VB.NET):

strUrl = strUrl.Replace("&", "-")

Another method is to revert back to .NET 2.0 request validation and/or change the characters that are being validated. This can be done by changing the following in the web.config file:

<httpRuntime requestValidationMode="2.0" requestPathInvalidCharacters="*,%,:" />

I however, prefer the first method as request validation is a good thing: the purpose is to secure your site from injection attacks.

More info:
HttpRuntimeSection.RequestValidationMode Property
HttpRuntimeSection.RequestPathInvalidCharacters Property

ASP.NET Routing gives 404 error

1 Comment

I really like the new .NET 4 (3.5) routing, it is very simple and straight forward to implement – just add some MapPageRoute to your global.asax and then some RouteData.Values to your aspx files 🙂

Anyway it was simple until last night when I was adding routing to an old VB.NET WebSite project. I just could not get it to work, I kept getting HTTP 404 errors:

Error Summary
HTTP Error 404.0 – Not Found
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.

Detailed Error Information
Module IIS Web Core
Notification MapRequestHandler
Handler StaticFile
Error Code 0x80070002

Finally I figured out that I had some stuff missing in my web.config to get this to work, ie making sure the routing module runs.

<system.webServer> 
  <modules> 
    <remove name="UrlRoutingModule-4.0" /> 
    <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="" /> 
  </modules> 
</system.webServer>

or use this:

	<system.webServer> 
		<modules runAllManagedModulesForAllRequests="true"/>
	</system.webServer>

This is probably not a problem for new projects created in Visual Studio 2010 but obviously my old web.config needed some fixing.

Thanks to this blog post by Ashic Mahtab.

ASP.NET 4.0 Chart Control Problems on IIS7

11 Comments

I have been playing around with the ASP.NET 4.0 Chart Controls for a new web site (Bilvärdet.se – used car price statistics)

When deploying it to IIS7 on my Windows Server 2008 I ran in to a few problems:

1) I did not have .NET 4.0 Framework installed on the server

🙂 It turns out .NET 4.0 Framework is an optional update in Windows Update and for some reason that I could not figure out failed to install (error code: 80200053) Solution that worked: Manually installed it. I had to use a tool that was new to me, the Microsoft Web Platform Installer (Web PI). Seems to be a cool tool to download, install and configure all sorts of software (incl WordPress, Dupral, Joomla) on Windows, will check this out in more detail in the future.
Don’t forget to change the Application Pool for your Web Site/Web Application in IIS Manager to use .NET Framework 4.0

2) Next problem, error message: An ASP.NET setting has been detected that does not apply in Integrated managed pipeline mode

This turned out to be a few lines of code in web.config that seems to be needed on my development machine (Windows7 and Visual Studio 2010) but causes problems on IIS7.

Just removed this section from production web.config on server and it worked:

<httpHandlers>
     <add path="ChartImg.axd" verb="GET,HEAD,POST"
     type="System.Web.UI.DataVisualization.Charting.ChartHttpHandler,
     System.Web.DataVisualization, Version=4.0.0.0, Culture=neutral,
     PublicKeyToken=31bf3856ad364e35" validate="false"/>
</httpHandlers> 

Thanks to Michael Narinsky for pointing this out.

3) Image file directory problems

First error message: Invalid temp directory in chart handler configuration c:\TempImageFiles\

Solution: Changed dir in ChartImageHandler app settings in web.config

<appSettings>
     <add key="ChartImageHandler" value="storage=file;timeout=20;dir=c:\TempImageFiles\;"/>
</appSettings> 

Then this error message: The temp directory in chart handler configuration is not accessible c:\xxx\images\

Followed error message suggestion and added access rights to directory (Properties – Security tab) for user NETWORK SERVICE

[Update: Other error message that can appear:
a) “Access to the path ‘C:\xxx\images\charts\chart.png’ is denied.” – this is the same as above, add rights for NETWORK SERVICE
b) “Could not find a part of the path ‘C:\xxx\images\charts\chart.png’.” – make sure directory exists or create it]

4) Charts not rendering with ASP.NET Routing

I’m using ASP.NET 4.0 Routing but this seems to be something Charts are not working well with. Charts are showing up as broken links. If you are using routing, you need to make sure ChartImg.axd is ignored. Add Ignore (or IgnoreRoute) and – this took me quite some time to figure out – make sure you add these before any MapPageRoute or it will not work!

This is what I added in VB.NET, add one line per level in virtual/route folder structure – first line is for root, second line for one subdirectory level, third line for two subdirectory levels.

routes.Ignore("ChartImg.axd/{*pathInfo}")
routes.Ignore("{controller}/ChartImg.axd/{*pathInfo}")
routes.Ignore("{controller}/{action}/ChartImg.axd/{*pathInfo}")

Thanks to Simon Steele.

BTW – Another nice link about ASP.NET Chart Control

MySQL and Visual Studio 2010 Membership, Roles and User Profile Provider

9 Comments

In Visual Studio 2010 (VS2010) when you create a new Web Application Project using the default Web Template “ASP.NET Web Application” it creates an Account folder with the files Login.aspx, Register.aspx and ChangePassword.aspx.
These are files/pages for ASP.NET Membership which is ASP.NET’s built-in user handling. I have never used Membership before but have been curious to, so I saw this as my cue to do so. However since I’m running MySQL as my database server I knew I had some extra installing and configuring to do.

Now several hours later I have finally figured out what I needed to do…

1) Install the latest version of MySQL Connector/NET. Visual Studio support is from version 6.3 which is currently only available in beta, current version when writing this is 6.3.2 beta.

2) Use the MySQL Website Configuration Tool, follow instructions found by clicking this link. This tool will modify necessary configuration files (have not checked which, maybe only web.config) to use MySQL Membership, Roles, User Profile and/or Session State.

Important: (this took me quite some time to figure out) the MySQL Website Configuration Tool icon only shows up in the Solution Explorer if you create a Web Site Project in VS 2010. It does not show if you create a Web Application Project. I have reported this as a bug to MySQL.

The only workaround I found to use a Web Application instead of using a Web Site was to create a temp Web Site project, run the MySQL Web Configuration Tool and then use copy and paste to move changes from web.confg to my Web Application Project. I have provided these changes at the end of this post.

3) Done!

I am running Windows 7 64-bit, Visual Studio 2010 and MySQL Connector/NET 6.3.2 beta, but according to this thread other people with other versions of VS seems to be having the same problem.

These are the changes that are made to web.config:

  <connectionStrings>
    <remove name="LocalMySqlServer" />
    <add name="LocalMySqlServer" connectionString="server=localhost;User Id=root;database=users;password=xxx" providerName="MySql.Data.MySqlClient" />
  </connectionStrings>
<system.web>
    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login.aspx" timeout="2880" />
    </authentication>
    <membership defaultProvider="MySQLMembershipProvider">
      <providers>
      <clear />  
      <remove name="MySQLMembershipProvider" />
        <add name="MySQLMembershipProvider" type="MySql.Web.Security.MySQLMembershipProvider, MySql.Web, Version=6.3.2.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" applicationName="/" description="MySQL default application" connectionStringName="LocalMySqlServer" writeExceptionsToEventLog="True" autogenerateschema="True" enablePasswordRetrieval="True" enablePasswordReset="True" requiresQuestionAndAnswer="False" requiresUniqueEmail="False" passwordFormat="Clear" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="1" passwordAttemptWindow="10" passwordStrengthRegularExpression="" />
      </providers>
    </membership>
    <profile defaultProvider="MySQLProfileProvider">
      <providers>
        <clear />
        <remove name="MySQLProfileProvider" />
        <add name="MySQLProfileProvider" type="MySql.Web.Profile.MySQLProfileProvider, MySql.Web, Version=6.3.2.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" applicationName="/" description="" connectionStringName="LocalMySqlServer" writeExceptionsToEventLog="False" autogenerateschema="True" />
      </providers>
    </profile>
    <roleManager enabled="false" defaultProvider="MySQLRoleProvider">
      <providers>
        <clear />
        <remove name="MySQLRoleProvider" />
        <add name="MySQLRoleProvider" type="MySql.Web.Security.MySQLRoleProvider, MySql.Web, Version=6.3.2.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" applicationName="/" description="" connectionStringName="LocalMySqlServer" writeExceptionsToEventLog="True" autogenerateschema="True" />
      </providers>
    </roleManager>
  </system.web>

  

MySql.Data.MySqlClient.MySqlException: Parameter ‘@id’ must be defined

57 Comments

I found out a funny thing today. Or maybe not so funny when I think about it…

The order page on one of my sites have not been working since early November, almost 6 months!! As I’m sure you understand it’s not a very busy site, but usually I get a few orders per month so I should have noticed this a long time ago…

The site is running on IIS/ASP.NET and MySQL server 5.0.45 using the MySQL .NET Connector and I’m using a SQL statement like this on the page:

START TRANSACTION;
INSERT INTO table1 (companyname,street,city,zip) VALUES (‘XS Tech’,’test’,’test’,’12345′);
SELECT @id:=id FROM table1 where id = last_insert_id();
INSERT INTO table2 (id,contactname) VALUES (@id,’Max”);
COMMIT;

This statement was throwing an exception error: “MySql.Data.MySqlClient.MySqlException: Parameter ‘@id’ must be defined”

Since I could not really remember – ie had not properly documented – what changes I may have done to that page six months ago, it made trouble shooting a bit harder. But to make a long story short(er) – It turns out I had upgraded the MySQL .NET Connector and this was causing the error. The site was using MySql.Data.dll version 5.2.1.0. I tried to upgrade to 6.0.3.0 to no avail. But when downgrading to 5.1.4.0 it started to work just fine again.

Finally, I  found that this was indeed a change in the .Net Connector. (I’m not sure what version number but starting from 5.2.0 would be my guess)
Starting from version 5.2.2 of the Connector you should add the Allow User Variables=True Connection String Setting in order to use User Defined Variables in your SQL statements.

Example of Connection String:

Database=testdb;Data Source=localhost;User Id=root;Password=hello;Allow User Variables=True

Older Entries