Category Archives: C#

Microsoft SQL Server, version control and deployment using MSDeploy and Phantom

Managing database schema’s and keeping stored procedures up to date can become quite complex during long term development. We have ways to version control source code but how do we version control the database schema and other stuff like stored procedures?

How can we cover this in our automatic deployment setup?

The goal is to never use SQL Management Studio during development and instead rely on updating version controlled .sql files and let these make updates to the database during deployment.

This post covers how to export Microsoft SQL Server stored procedures, adding them to our solution, managing schema updates and deploying changes using Phantom with MSDeploy

1. Add to version control

First we needed to export all store procedures from our database and add them as .sql script files to our solution project. This way they can be version controlled together with our project and later be used to update any target database when we make a deploy.

  1. Right click the database
  2. Choose Tasks
  3. Generate Scripts
  4. Click Next
  5. Select specific database objects
  6. Select all stored procedures
  7. Click Next
  8. Select “Single file per object”
  9. Advanced > select “Include if NOT EXISTS”
  10. Advanced > select “Script DROP and CREATE”
  11. Advanced > deselect “Script USE DATABASE”
  12. Choose where to dump the .sql files
  13. Click Next
  14. Click Next
  15. Done!


Add the .sql files to your solution. The sync.sql is where we keep all the schema updates.

-- Example sync.sql

-- 2011-05-03 : Added author to Article
--
if not exists (select 1 from INFORMATION_SCHEMA.columns where table_name = 'Articles' and column_name = 'Author')
	alter table [Articles] add [Author] [nvarchar](255) NULL

-- 2011-03-26 : Added Date to Source
--
if not exists (select 1 from INFORMATION_SCHEMA.columns where table_name = 'Sources' and column_name = 'Date')
begin
	alter table [Sources] add [Date] [datetime] NOT NULL
        alter table [Sources] add constraint [DF_Sources_Date]  DEFAULT (getdate()) for [Date]
end

2. Modify deploy procedure

We need to add steps to our deploy process where these script files are executed towards our deploy targets. Since we are using Phantom I added something like the following to our build.boo script. The reason why I chose to combine all .sql files into one big one was because it takes a very long time to execute 50+ such files one by one using MSDeploy.

//file: build.boo

import System.IO

solution_file = "./example.sln"
configuration = "release"
output_path = "./output"
sql_file_path = "c:/tmp/bigfile.sql"

target default, (build, clean):
  pass

target build:
  msbuild(file: solution_file, configuration: configuration, properties: {"OutputPath": output_path})

target deploy, (build, deploy_codebase_to_some_machine, update_sql, clean):
  pass

target deploy_codebase_to_some_machine:
  ..deploy stuff <snip>

desc "combine all .sql files into one big one"
target build_sql_script:
	file = File.Open(sql_file_path,FileMode.Create)
	writer = StreamWriter(file)
	with FileList("./.sql"):
		.Flatten(true)
		.Include("**/*.sql")
		.ForEach def(file):
			writer.Write("\n\n")
			writer.Write(File.ReadAllText(file.FullName))
	writer.Close()

target update_sql, (build_sql_script):
	msdeploy(verb:"sync", source: {"dbFullSql":sql_file_path}, dest: {"dbFullSql":"data source=127.0.0.2;initial catalog=Hermes;password=god!secure;persist security info=True;user id=admin;packet size=4096"})

target clean:
  rm(output_path)
  rm(sql_file_path)

3. Invoke

This script can be used to perform a deploy, complete with schema updates and stored procedures by invoking:

phantom.exe deploy

- or to only update schema and stored procedures:

phantom.exe update_sql

All that is left is to add this to the build server, add some triggers and you are all set. good luck!

Deploy and install .NET Windows services using MSDeploy & Phantom

I recently needed to deploy a .NET Windows service to a remote machine and, if needed, install it.

The first part was to be able to install or uninstall a .NET Windows service using InstallerTool.exe in the following way:

Install   >> InstallerTool.exe /name=MyService /account=user /user=domain\username /password=password ./service.exe
Uninstall >> InstallerTool.exe /u /name=MyService ./service.exe

To do this without needing to manually enter credentials in a popup window I needed to modify my installer class. In order for it to accept credentials and stuff from the command line parameters I added the following to my installer.

protected override void OnBeforeInstall(IDictionary savedState)
{
	base.OnBeforeInstall(savedState);

	this.processInstaller = this.processInstaller ?? new ServiceProcessInstaller();
	this.serviceInstaller = this.serviceInstaller ?? new ServiceInstaller();

	this.serviceInstaller.ServiceName = string.IsNullOrEmpty(GetContextParameter("name")) ? this.serviceInstaller.ServiceName : GetContextParameter("name");
	this.serviceInstaller.Description = string.IsNullOrEmpty(GetContextParameter("desc")) ? this.serviceInstaller.Description : GetContextParameter("desc");

	switch (GetContextParameter("account").ToLower())
	{
		case "user":
			this.processInstaller.Account = ServiceAccount.User;
			this.serviceInstaller.Username = string.IsNullOrEmpty(GetContextParameter("user")) ? this.serviceInstaller.Username : GetContextParameter("user");
			this.serviceInstaller.Password = string.IsNullOrEmpty(GetContextParameter("password")) ? this.serviceInstaller.Password : GetContextParameter("password");
			break;
		case "localservice":
			this.processInstaller.Account = ServiceAccount.LocalService;
			break;
		case "localsystem":
			this.processInstaller.Account = ServiceAccount.LocalSystem;
			break;
		case "networkservice":
			this.processInstaller.Account = ServiceAccount.NetworkService;
			break;
	}
}

protected override void OnBeforeUninstall(IDictionary savedState)
{
	base.OnBeforeUninstall(savedState);
	this.serviceInstaller.ServiceName = string.IsNullOrEmpty(GetContextParameter("name")) ? this.serviceInstaller.ServiceName : GetContextParameter("name");
}

private string GetContextParameter(string key)
{
	return Context.Parameters.ContainsKey(key) ? Context.Parameters[key] : string.Empty;
}

I am using my Phantom fork on Github that adds a wrapper for MSDeploy.exe

In this example InstallUtil.exe is deployed together with my service to the remote machine in the folder “./installer/installUtil.exe”

Example script usage:

deploy             >> pantom.exe build deploy clean
deploy & install   >> pantom.exe build deploy install clean
deploy & uninstall >> pantom.exe build deploy uninstall clean
# file: build.boo
# deploys and install or uninstalls services on a remote machine
solution_file = "./solution.sln"
configuration = "release"
output_path = "./phantom.build"

remote_path="E:/Services/service"
remote_installer = deploy_path+"/installer/installUtil.exe"
remote_server = "127.0.0.1"
remote_username = "admin"
remote_password = "god!secure"
remote_uninstall_cmd = remote_installer_path+" /u /name=MyService "+deploy_path+"/service.exe"
remote_install_cmd = remote_installer_path+" /name=MyService /account=user /user=domain\\username /password=password "+deploy_path+"/service.exe"

target default, (build, clean):
pass

target build:
msbuild(file: solution_file, configuration: configuration, properties: {"OutputPath": output_path })

target deploy:
msdeploy(verb: "sync",source:{"dirPath": output_path}, dest: {"dirPath":remote_path,"wmsvc":remote_server,"authType":"basic","userName":remote_username,"password":remote_password},flags:"-allowUntrusted")

target uninstall:
msdeploy(verb: "sync", source:{"runCommand":remote_uninstall_cmd}, dest: {"auto":"","wmsvc":remote_server,"authType":"basic","userName":deploy_username,"password":deploy_password},flags:"-allowUntrusted")

target install:
msdeploy(verb: "sync", source:{"runCommand":remote_install_cmd}, dest: {"auto":"","wmsvc":remote_server,"authType":"basic","userName":remote_username,"password":remote_password},flags:"-allowUntrusted")

target clean:
rm(output_path)

You may need to add some privileges to the WMSVC service running on the remote machine

c:\> sc privs wmsvc SeChangeNotifyPrivilege/SeImpersonatePrivilege/SeAssignPrimaryTokenPrivilege/SeIncreaseQuotaPrivilege
c:\> sc stop wmsvc
c:\> sc start wmsvc

backup and deploy using MSDeploy and Phantom with configuration version control

Working on a project that spans several environments like development, testing, staging and production can prove difficult when it comes to managing their configuration files.

Because they are basically the same configuration files but with different settings, like pointing on different databases or enable/disable debug stuff.

Using my recent Github fork of Phantom I am now able to do this:

  1. Deploy a website/application using MSDeploy
  2. Backup an existing website/application before deploying, also using MSDeploy
  3. Version control configuration files and deploy the correct set to a given environment

The following build.boo show how to configure  backups,  configuration files and deploy targets for the same solution.

this script assumes that there is a directory named “.configurations” in the solution directory and that it contains two sub folders named “staging” and “production”

solution_file = "solution.sln"
configuration = "release"

output_path = "./.output"
published_web_path = output_path + "/_PublishedWebsites/website"
package_path = output_path+"/deploy_package"

remote_backup_folder = "{path on remote machine}/some_name_["+System.Environment.MachineName +"]_"+ date.Now.ToString("yyyy.MM.dd.hh.mm.ss")

msdeploy_authentication_staging = ",wmsvc=\"127.0.0.1\",authType=\"basic\",userName=\"admin\",password=\"god!secure\""
msdeploy_authentication_production = ",wmsvc=\"127.0.0.2\",authType=\"basic\",userName=\"admin\",password=\"god!more!secure\""

target default, (build, clean):
  pass

target build:
  msbuild(file: solution_file, configuration: configuration, properties: {"OutputPath": output_path })

target package:
  rm(package_path)
  with FileList(published_web_path):
    .Exclude("{*.config, *.txt}")
    .ForEach def(file):
      file.CopyToDirectory(package_path)

target deploy_to_staging, (clean, build, package, backup_staging, clean):

	with FileList("./.configurations/staging/"):
		.Include("**")
		.ForEach def(file):
			file.CopyToDirectory(package_path)

	msdeploy(skipfiles: (".*\\.bak",".*\\.txt"), verb: "sync", source: "iisApp="+package_path, dest: "iisApp=\"Default Website\""+msdeploy_authentication_staging)

target deploy_to_production, (clean, build, package, backup_production, clean):

	with FileList("./.configurations/production/"):
		.Include("**")
		.ForEach def(file):
			file.CopyToDirectory(package_path)

	msdeploy(skipfiles: (".*\\.bak",".*\\.txt"), verb: "sync", source: "iisApp="+package_path, dest: "iisApp=\"Default Website\""+msdeploy_authentication_production )

target backup_staging:
	msdeploy(verb: "sync", source: "iisApp=\"Default Website\""+msdeploy_authentication_staging, dest: "archivedir=\""+remote_backup_folder+"\"+msdeploy_authentication_staging)

target backup_production:
	msdeploy(verb: "sync", source: "iisApp=\"Default Website\""+msdeploy_authentication_production , dest: "archivedir=\""+remote_backup_folder+"\"+msdeploy_authentication_production )

target clean:
  rm(output_path)

Example usage:

  • “Phantom.exe deploy_to_staging” : builds, makes a backup, configures and deploys
  • “Phantom.exe deploy_to_production” : builds, makes a backup, configures and deploys

Continuous Integration using Phantom and MSDeploy

Phantom is a pretty sweet .NET build system by Jeremy Skinner written in C# and Boo.

I recently forked it on github as I wanted to add support for aspnet_compiler.exe and msdeploy.exe for use in one of our bigger projects.

These are just two simple wrappers for msdeploy.exe and aspnet_compiler.exe - basic and crude, but they do work.

With these in my build.boo script I can make a deploy simply by calling “Phantom.exe build publish deploy”

Nothing left but to set it up in Team City with the appropriate triggers ;)

The next step I guess is to throw in the updating of configuration files.

solution_file = "./solution.sln"
configuration = "release"

output_path = "./.output/solution.build"
publish_path = output_path+"/published.solution"

deploy_src = "iisApp=\""+publish_path+"\""
deploy_dest_test = "iisApp=\"website/application\",wmsvc=\"127.0.0.1\",authType=\"basic\",userName=\"admin\",password=\"god\" -allowUntrusted"
deploy_dest_stage = "iisApp=\"website/application\",wmsvc=\"127.0.0.2\",authType=\"basic\",userName=\"admin\",password=\"god!secure\" -allowUntrusted"

target default, (build, clean):
  pass

target build:
  msbuild(file: solution_file, configuration: configuration, properties: {"OutputPath": output_path, "TrackFileAccess": false})

target publish:
  aspnetcompiler(physicalpath: "./", virtualpath: "App", targetpath: publish_path)

target deploy, (deploy_test):
  pass

target deploy_test:
  msdeploy(skipfiles: (".*\\.config",".*\\.txt"),verb: "sync", source: deploy_src, dest: deploy_dest_test)

target deploy_stage:
  msdeploy(skipfiles: (".*\\.config",".*\\.txt"),verb: "sync", source: deploy_src, dest: deploy_dest_stage)  

target clean:
  rm(output_path)

Web Services: Automated feature testing using Specflow and Babelfish

example project at github

I recently started implementing BDD using Specflow for one of our customers.

Basically Specflow is a BDD framework written in .NET inspired by Cucumber and the tests are written in the same language (Gherkin)

One of the things we would like to include in this are the testing of soap service endpoints.

If you are a .NET developer you probably know all about the generation of proxy classes by using the wizard in visual studio or invoking wsdl.exe on the wsdl source file. This could be a problem and result in needless work, so I looked into compiling the proxy classes in memory during runtime.

Authentication

There exist a number of free and open soap web services out there, some that are used in my examples are taken from www.webservicex.net.

But of course we do have services that require some form of authentication. The following snippet illustrates how to perform forms authentication using the System.Net.WebClient and Babelfish.

public string FormsAuth(Uri url, string username, string password)
{
    var response = DownloadData(url);

    INode document = new Babelfish.HTML.HTMLDocument(Encoding.ASCII.GetString(response));

    var postData = string.Empty;

    var formElements = document.Find(n => n.Name.ToLower() == "input" && new[] { "hidden", "button", "submit", "checkbox", "radio", "text", "password" }
    .Contains(n.Attributes["type"]));

    foreach (var formElement in formElements)
    {
        postData += postData.Length > 0 ? "&" : string.Empty;

        var name = formElement.Attributes["name"];
        var value = formElement.Attributes["value"];

        switch (formElement.Attributes["type"])
        {
            case "text":
                postData += string.Format("{0}={1}", name, HttpUtility.UrlEncode(username));
                break;
            case "password":
                postData += string.Format("{0}={1}", name, HttpUtility.UrlEncode(password));
                break;
            default:
                if (!string.IsNullOrEmpty(value))
                    postData += string.Format("{0}={1}", name, HttpUtility.UrlEncode(value));
                break;
        }
    }

    Headers.Add("Content-Type", "application/x-www-form-urlencoded");
    string result;
    try
    {
        result = Encoding.ASCII.GetString(UploadData(url, "POST", Encoding.ASCII.GetBytes(postData)));
    }
    catch (WebException ex)
    {
        result = ex.ToString();
    }
    return result;
}

Web Service instantiation

Now when we have access to the wsdl definition via our modified WebClient we can use that to compile a SoapHttpClientProtocol object.

The following snippet is from the class ArbitraryWebservice and shows how to compile a wsdl stream into such an object given a valid type.

private static SoapHttpClientProtocol GenerateService(Stream stream, string serviceName)
{
    var serviceDescription = ServiceDescription.Read(stream);
    var serviceDescriptionImporter = new ServiceDescriptionImporter
    {
        ProtocolName = "Soap12",
        Style = ServiceDescriptionImportStyle.Client,
        CodeGenerationOptions = CodeGenerationOptions.GenerateProperties
    };

    serviceDescriptionImporter.AddServiceDescription(serviceDescription, null, null);

    var codeNamespace = new CodeNamespace();
    var codeCompileUnit = new CodeCompileUnit();

    codeCompileUnit.Namespaces.Add(codeNamespace);

    var warnings = serviceDescriptionImporter.Import(codeNamespace, codeCompileUnit);

    if (warnings > 0)
        throw new Exception(string.Format("{0}", warnings));

    using (var codeDomProvider = CodeDomProvider.CreateProvider("C#"))
    {
        var compilerParameters = new CompilerParameters(new[] { "System.dll", "System.Web.Services.dll", "System.Web.dll", "System.Xml.dll", "System.Data.dll" }) { GenerateInMemory = true };

        var results = codeDomProvider.CompileAssemblyFromDom(compilerParameters, codeCompileUnit);

        if (results.Errors.Count > 0)
            throw new Exception(string.Join(", ", results.Errors.Cast<CompilerError>().Select(e => e.ToString()).ToArray()));

        var service = results.CompiledAssembly.CreateInstance(serviceName);

        return service as SoapHttpClientProtocol;
    }
}

Method invocation

The following snippet is also from the class ArbitraryWebservice.

Basically it attempts to map the given methodName to a method in the instantated service object. Then it iterates trough the parameters for that method and match each one to one of the elements in the args array.

The last part is to cast/convert complex parameter types to the correct type using a extension method (basically JSON serialization/deserialization).

public object Call(string methodName, MethodParamDescriptor[] args)
{
    var method = _service.GetType().GetMethod(methodName);

    if (method == null)
        throw new MissingMethodException(string.Format("this service contains no method named {0}", methodName));

    var methodParams = new List<object>();

    foreach (var p in method.GetParameters())
    {
        if(!args.Any(a => a.Name == p.Name))
            throw new ArgumentException("expected parameter not supplied", p.Name);

        methodParams.Add(args.Where(a => a.Name == p.Name)
            .Select(a => a.Value.Convert(p.ParameterType))
            .FirstOrDefault());
    }

    return method.Invoke(_service, methodParams.ToArray());
}

Specflow

Finally with the above in place we can write feature descriptions using Specflow and execute them as a normal unit test.

The complex object comparisons are done with the help of a Babelfish extension method

You can compile and run these tests yourself by downloading the solution from here.

Feature: Webservices
	In order to know what is going on
	As a developer
	I want to instantiate any arbitrary web service and invoke availible methods and assert their return values

@mytag
Scenario: Weather
	Given webservice named Weather with the wsdl url http://ws.cdyne.com/WeatherWS/Weather.asmx?wsdl
	When I call the method GetCityForecastByZIP with the parameter ZIP=36003
	Then I expect it to return an object matching content {City: 'Autaugaville', WeatherStationCity: 'Selma'}

@mytag
Scenario: Speed
	Given webservice named ConvertSpeeds with the wsdl url http://www.webservicex.net/ConvertSpeed.asmx?WSDL
	When I call the method ConvertSpeed with the parameters speed=100 and FromUnit=centimetersPersecond and ToUnit=feetPersecond
	Then I expect it to return the number 3.2808398950131235

@mytag
Scenario: Whois
	Given webservice named whois with the wsdl url http://www.webservicex.net/whois.asmx?WSDL
	When I call the method GetWhoIS with the parameters HostName=google.com
	Then I expect it to return a string containing TERMS OF USE: You are not authorized to access or query our Whois

@mytag
Scenario: country
	Given webservice named country with the wsdl url http://www.webservicex.net/country.asmx?WSDL
	When I call the method GetCountries with no parameters
	Then I expect it to return html containing <NewDataSet><Table><Name>Kiribati</Name></Table><Table><Name>Cocos (Keeling) Islands</Name></Table></NewDataSet>

@mytag
Scenario: GeoIPService
	Given webservice named GeoIPService with the wsdl url http://www.webservicex.net/geoipservice.asmx?WSDL
	When I call the method GetGeoIP with the parameters IPAddress='209.85.148.147'
	Then I expect it to return an object matching content {IP:'209.85.148.147', CountryName:'United States', CountryCode:'USA'}

@mytag
Scenario: BarCode
	Given webservice named BarCode with the wsdl url http://www.webservicex.net/genericbarcode.asmx?WSDL
	When I call the method GenerateBarCode with the parameters BarCodeText=123456 and BarCodeParam={Height:1,Width:1,Ratio:5,FontName:'Arial',FontSize:10,BarCodeImageFormat: 'PNG'}
	Then I expect it to return a base64 string matching iVBORw0KGgoAAAANSUhEU...AAAAAAAAAAAAAAAAAAAAAAAAAAAAA==

PyBlocks

Some time ago I had the need to generate order confirmation emails using templates populated with order data such as ordered items, pricing and customer information.

One way is of course to serialize the order to XML and transform it via XSL but I had an urge to try something new.

So I decided to use IronPython and parse templates with {py py} blocks.

You can download the example PyBlocks solution here

Example PyBlocks template

Hello {py Console.Write(customer.Givenname+ ' ' + customer.Surname) py}!

The package will be sent to the following adress

  {py
  Console.WriteLine(customer.Street + ' ' + customer.StreetNumber)
  Console.WriteLine(customer.ZipCode + ' ' + customer.City)
  py}
Regards
//Some online store

Rendering the template

var engine = Python.CreateEngine(AppDomain.CurrentDomain);
var scope = new ScriptScope(engine, new Scope()).Engine.GetClrModule();

scope.SetVariable("customer", new Customer
                                  {
                                      City = "Stockholm",
                                      Givenname = "Steve",
                                      Street = "AppleRow",
                                      StreetNumber = "23",
                                      Surname = "Mobs",
                                      ZipCode = "14565"
                                  });

Console.WriteLine(PyBlocksRenderer.Render(engine, scope, template));

Output

Hello Steve Mobs!

The package will be sent to the following adress

  AppleRow 23
  14565 Stockholm

Regards
//Some online store

PyBlocksRenderer.cs

    public class PyBlocksRenderer
    {

        /// <summary>
        /// Renders the template using the specified engine and scope.
        /// </summary>
        /// <param name="pythonEngine">The python engine.</param>
        /// <param name="scriptScope">The script scope.</param>
        /// <param name="template">The template.</param>
        /// <returns></returns>
        public static string Render(ScriptEngine pythonEngine, ScriptScope scriptScope, string template)
        {
            //Replace tabs with 4 spaces using a string extension method
            template = template.ReplaceTabsWithSpaces();

            //Fetch a MatchCollection using a string extension method
            var pythonBlocksMatchCollection = template.GetPythonBlockMatches();

            //contains the matching {py..py} block as a key and the result as value
            var pyBlockMatchResults = new List<KeyValuePair<string, string>>();

            foreach (Match pythonBlockMatch in pythonBlocksMatchCollection)
            {
                //Fetch matching code block code and normalize indentation using a string extension method
                var pythonCode = pythonBlockMatch.Groups["code"].Value.NormalizeIndentations();

                //Add import to enable writing to the console from within the pythonCode
                pythonCode = string.Format("from System import Console{0}{1}", Environment.NewLine, pythonCode);

                //Execute script
                var pyResult = ExecuteScript(pythonEngine, scriptScope, pythonCode);

                //Fix result indentations
                var line = template.GetLineAt(pythonBlockMatch.Index);
                if (string.IsNullOrEmpty(line.Trim()))
                {
                    var indentation = line.GetIndentation();
                    var pyLines = pyResult.GetLines().ToList();
                    var lines = from l in pyLines select pyLines.IndexOf(l) > 0 ? l.Prepend(" ", indentation) : l;
                    pyResult = string.Join(Environment.NewLine, lines.ToArray());
                }

                //Add the result of this execution to the list
                pyBlockMatchResults.Add(new KeyValuePair<string, string>(pythonBlockMatch.Value, pyResult));
            }

            //find / replace template py blocks with results
            foreach (var replace in pyBlockMatchResults)
                template = template.Replace(replace.Key, replace.Value);

            return template;
        }

        /// <summary>
        /// Executes the script.
        /// </summary>
        /// <param name="engine">The engine.</param>
        /// <param name="scope">The scope.</param>
        /// <param name="code">The code.</param>
        /// <returns>code output</returns>
        public static string ExecuteScript(ScriptEngine engine, ScriptScope scope, string code)
        {
            //save reference to standard Console out
            var stdOut = Console.Out;

            //1: redirect the console to write to a stringwriter
            //2: execute the script
            //3: return the contents of the stringbuilder
            //4: reset Console out to the saved reference above
            //Exception: return the exception message
            try
            {
                var sb = new StringBuilder();
                Console.SetOut(new StringWriter(sb));
                engine.Execute<string>(code, scope);
                return sb.ToString();
            }
            catch (Exception ex)
            {
                return string.Format("#! {0}", ex.Message);
            }
            finally
            {
                Console.SetOut(stdOut);
            }
        }
    }

StringExtensions.cs

    public static class StringExtensions
    {
        /// <summary>
        /// Replaces the tabs with spaces.
        /// </summary>
        /// <param name="str">The STR.</param>
        /// <returns></returns>
        public static string ReplaceTabsWithSpaces(this string str)
        {
            return str.Replace("\t", "    ");
        }

        /// <summary>
        /// Gets the python blocks.
        /// </summary>
        /// <param name="template">The template.</param>
        /// <returns></returns>
        public static MatchCollection GetPythonBlockMatches(this string template)
        {
            return Regex.Matches(template, "{py(?<code>.*?)py}", RegexOptions.IgnoreCase | RegexOptions.Singleline);
        }

        /// <summary>
        /// Normalizes the indentations.
        /// </summary>
        /// <param name="str">The STR.</param>
        /// <returns></returns>
        public static string NormalizeIndentations(this string str)
        {
            str = str.ReplaceTabsWithSpaces();
            var lines = from line in str.GetLines() where !string.IsNullOrEmpty(line.Trim()) select line;
            var minimumIndent = lines.GetMinimumIndentation();
            return string.Join(Environment.NewLine, (from line in lines select line.Substring(minimumIndent)).ToArray());
        }

        /// <summary>
        /// Gets the lines.
        /// </summary>
        /// <param name="str">The STR.</param>
        /// <returns></returns>
        public static string[] GetLines(this string str)
        {
            return Regex.Split(str, "\\r?\\n");
        }

        /// <summary>
        /// Gets the minimum indentation.
        /// </summary>
        /// <param name="lines">The lines.</param>
        /// <returns></returns>
        private static int GetMinimumIndentation(this IEnumerable<string> lines)
        {
            var minimumIndent = int.MinValue;

            foreach (var line in lines)
            {
                var indent = line.GetIndentation();
                minimumIndent = minimumIndent > indent || minimumIndent == int.MinValue ? indent : minimumIndent;
            }

            return minimumIndent > int.MinValue ? minimumIndent : 0;
        }

        /// <summary>
        /// Gets the indentation.
        /// </summary>
        /// <param name="str">The STR.</param>
        /// <returns></returns>
        public static int GetIndentation(this string str)
        {
            return Regex.Match(str, "^\\s+").Value.Length;
        }

        /// <summary>
        /// Gets the line at.
        /// </summary>
        /// <param name="str">The STR.</param>
        /// <param name="index">The index.</param>
        /// <returns></returns>
        public static string GetLineAt(this string str, int index)
        {
            return str.Substring(0, index).GetLines().LastOrDefault() ?? string.Empty;
        }

        /// <summary>
        /// Prepends the specified STR with value n times.
        /// </summary>
        /// <param name="str">The STR.</param>
        /// <param name="value">The value.</param>
        /// <param name="times">The times.</param>
        /// <returns></returns>
        public static string Prepend(this string str, string value, int times)
        {
            for (var i = 0; i < times; i++)
                str = value + str;

            return str;
        }
    }