Archive for the 'Solutions Factory Features' Category

Managing Environment-Specific web.config, app.config and EntLib Config Files

Thursday, November 26th, 2009

When you need to deploy a solution to multiple environments (e.g. separate Development, Test, Acceptation and Production or DTAP environments), you often have a set of environment-specific settings that you need to manage.

Since version 2.1 the Macaw Solutions Factory supports environment-specific web.config, app.config and EntLib config files.

Recommendations

Managing environment specific settings is risky, because by definition you cannot test these settings anywhere else.

For this reason, the Microsoft .NET Architecture Style (MAST) recommends to minimize the number of environment specific settings that your solution needs by design.

Secondly, MAST recommends that you further reduce risk by avoiding duplication of environment specific settings in multiple config files within the same environment. Instead, try to place these settings as much as possible in one central repository for each environment, e.g. in a service, a database or a SharePoint list (the last one has the added benefit of a free management UI).

Still, some technologies require settings in configuration files. To support this, the Factory allows you to manage environment-specific web.config, app.config and MS Enterprise Library (EntLib) settings.

Note that each MAST product has separate app.config, web.config and entlib config files. However to further reduce duplication of config files when maintaining multiple Factory products in the same environment, it is possible to share config files from a single location in source control across multiple MAST products, by using e.g. Externals in Subversion source control or NTFS Junctions when using TFS source control.

To add an environment-specific app.config or web.config file:

  1. Browse to the folder where your shared app.config or web.config file is located.

    It should be located in a subfolder under the _ConfigFiles folder for the product dependency folder that contains your Visual Studio solution.
  2. Create the new app / web config file in this folder and prefix the file name with the environment name (this can be anything, e.g. ‘production’ or a specific machine name)
  3. For each machine that should use this configuration, specify the new environment-specific web / app config file name in the machine configuration file.

    Note that a sample.production.machine.config file is supplied with the Factory.

Now when you deploy a deployment package on any of these machines, or when you build a solution in Visual Studio on any of these machines, the environment-specific version of the config file will be deployed to the runtime locations as the web.config or app.config file.

To add a new environment to MS Enterprise Library settings:

We leverage the built-in EntLib feature ‘Environmental Overrides’.

  1. Select ‘Edit Enterprise Library product configuration’ in the Factory Guide:
  2. Create a new Environment, e.g. ‘Test’:

    Be sure to follow the naming convention for the environment-specific files as indicated below:

    When you click ‘Save’, the environment delta file will be created:

  3. Now you can specify an override for any EntLib setting, for the Test environment. First select the setting, then select the value ‘Override Properties’ for ‘Override on Test’, and finally supply the override value for the setting.

    E.g. to override a database connection string:

  4. Now add an override value for the Test environment for the Application Setting ‘MastEnvironmentMachineNames’:

    The MastEnvironmentMachineNames value is a comma-separated list of names of the machines that make up this environment. Wildcards * and ? are supported. These are the same machine names that are used in the filename of the machine configuration files.

    Note that when a machine name does not match any override value of MastEnvironmentMachineNames, the base EntLib configuration without any overrides will be used.

  5. Select ‘Save’ to save all override settings in the Test.dconfig file. Also right-Click the (Test) environment and select ‘Save Merged Configuration’ to create the environment-specific EntLib configuration file :


    Don’t forget to add both the <Environment>.dconfig and the Entlib-<Environment>-Product.exe.config to source control.

Now the correct environment-specific version of the EntLib configuration file will be deployed when you deploy a MAST package on a machine, or when you select the ‘Deploy Enterprise Library product configuration’ action in the Factory Guide:

To edit or add values in an existing environment-specific EntLib file:

  1. First open the base EntLib configuration via the ‘Edit Enterprise Library product configuration ‘ action in the Factory Guide. It will open without any environment overrides loaded.
  2. Then right-click the Environments node, select ‘Open Environment Delta’ and open the appropriate <Environment>.dconfig file:
  3. Once you are done editing and adding new override values, save both the Entlib .dconfig file and the merged configuration file again.

Detect 32 or 64 bits Windows – regardless of WoW64 – with the PowerShell OSArchitecture function

Monday, November 2nd, 2009

In 64-bits versions of Windows Operating Systems, a subsystem called Windows on Windows 64 (WoW64) enables you to run 32 bits applications. Even to date, many Windows applications only exist in a 32 bits version – not the least of which is Microsoft’s own Visual Studio(!)

The purpose of WoW64 is to hide many of the structural differences between 32 bits and 64 bits Windows for 32 bits applications. This is a good thing. Most of the time, anyway…

However, when your PowerShell script runs in a 32 bits process on a 64 bits OS, and you want to integrate with 64-bits applications (e.g. you want to integrate SharePoint or SQL Server within Visual Studio), WoW64 can get in the way.

E.g., WoW64 hides the 64 bit registry and changes the environment variables for processor architecture and for the program files folder – in short, WoW64 hides that you are running on a 64 bits OS.

To detect a 64 bits OS from PowerShell on Vista, W2008 and later Windows versions, we could use WMI:

(Get-WMIObject win32_operatingsystem).OSArchitecture

However, this is not supported on W2003.

Fortunately, Microsoft provided an alternative, which is supported on all Windows versions that have Wow64: the IsWow64Process function. Since this is an unmanaged API call to Kernel32.dll, we need to do some extra work to call this in PowerShell.

We can write a simple managed C# wrapper around IsWow64Process:

using System;
using System.Runtime.InteropServices;
 
public class Kernel32
{
    [DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool IsWow64Process([In] IntPtr hProcess, [Out] out bool lpSystemInfo);
 
    // This overload returns True if the current process is running on Wow, False otherwise
    public bool CurrentProcessIsWow64()
    {
        bool retVal;
        if (!(IsWow64Process(System.Diagnostics.Process.GetCurrentProcess().Handle, out retVal))) { throw new Exception("IsWow64Process() failed"); }
        return retVal;
    }
}

When we use the PowerShell InvokeCSharp function, we can put this C# inline in our PowerShell script:

Function global:CurrentProcessIsWOW64
{
    # Use some inline C# code to call the unmanaged IsWow64Process() function in Kernel32.dll and return its result:
    $code = @"
        using System;
        using System.Runtime.InteropServices;
 
        public class Kernel32
        {
            [DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool IsWow64Process([In] IntPtr hProcess, [Out] out bool lpSystemInfo);
 
            // This overload returns True if the current process is running on Wow, False otherwise
            public bool CurrentProcessIsWow64()
            {
                bool retVal;
                if (!(IsWow64Process(System.Diagnostics.Process.GetCurrentProcess().Handle, out retVal))) { throw new Exception("IsWow64Process() failed"); }
                return retVal;
            }
        }
"@
    InvokeCSharp -code $code -class 'Kernel32' -method 'CurrentProcessIsWow64'
}
 
Function global:ProcessArchitecture
{
    switch ([System.IntPtr]::Size)
    {
        4 { 32 }
        8 { 64 }
        default { throw "Unknown Process Architecture: $([System.IntPtr]::Size * 8) bits" }
    }
}
 
Function global:OSArchitecture
{
    if (((ProcessArchitecture) -eq 32) -and (CurrentProcessIsWOW64)) { 64 } else { ProcessArchitecture }
    # Note that on Vista, W2008 and later Windows versions, we could use (Get-WMIObject win32_operatingsystem).OSArchitecture.
    # We don't use that because we also support W2003
}

So there you have it, some simple PowerShell functions to detect the process architecture and the OS architecture, regardless of whether or not you are running under WoW64.

P.S. I also added these functions to the global functions in the Macaw Solutions Factory, so they can be leveraged by anyone who wants to extend the Factory.

Call inline C# from PowerShell with InvokeCSharp

Tuesday, October 27th, 2009

Out of the box PowerShell 1.0 makes it very easy to call any managed code in an assembly. However, one of the strong points of a scripted language such as PowerShell is that you can debug and extend it in any environment without the need for development tools or additional source code. This is especially valuable in scripts that integrate heavily with production environments, e.g. with automated deployment scripts such as we use in the Macaw Solutions Factory.

Ideally you would want the ability to call any C# source code from PowerShell, either inline or as a separate source file, effectively adding the maintainability benefits of a scripted language to C#. Based on a library created by Lee Holmes I created an improved PowerShell 1.0 function called InvokeCSharp that does just this.

Note that in PowerShell V2, this functionality is available through the built-in Add-Type cmdlet.

InvokeCSharp is completely self-contained and can be used in any PowerShell script.

Usage

Here are some simple examples on how to use InvokeCSharp:

Let’s say we have a file MyClass.cs:

using System;
 
public class MyClass
{
    public string AString;
 
    public MyClass(string aString)
    {
        AString = aString;
    }
}

Then we can invoke it like this in PowerShell:

# Create a class instance from a .cs file, passing a parameter to the class constructor:
# Note that the class name can be omitted here, since it is implied from the .cs file name
$myClassInstance = InvokeCSharp -file '.\MyClass.cs' -parameters 'A text'

Or we can put some C# code inline in the PowerShell script itself, and invoke it:

# Call some simple inline code. Note that you could also wrap the code in a namespace,
# add using statements and reference other assemblies.
$code = @"
	public class MyClass2
	{
		public static int MyStaticMethod(int a, int b)
		{
			return a * b;
		}
	}
"@
$result = InvokeCSharp -code $code -class 'MyClass2' -method 'MyStaticMethod' -parameters 3, 7 # Will return 21

Although these are very simple examples, all the rich goodness of PowerShell – .NET integration is available. Passing any type of parameters back and forth is simple and completely transparent. All C# features can be used; e.g. you can use the C# DllImport attribute to call unmanaged API’s when COM interop is not available or not preferred.

After initial compilation the performance is equivalent to calling compiled assemblies from PowerShell.

Extending the Macaw Solutions Factory PowerShell scripts with inline C#

I added InvokeCSharp to the global functions in the Macaw Solutions Factory. This makes it possible to simply extend or modify any Factory script – including the scripts that deploy a MAST Product from a package to (production) machines.

For anyone who is more adept with C# than with PowerShell this will make it easier to extend the Factory scripts. This also enables leveraging existing C# libraries from PowerShell while keeping the entire solution maintainable as a script.

Improvements

The improvements I added to InvokeCSharp when compared to the original version are:

  • The C# code you specify is completely self-contained; no more C# wrapper code where your code is pasted into. This improves readability and maintainability of inline C# code.
  • Simpler parameter passing; no more wrapper code needed for parameters and return values.
  • Automatically un-wraps parameters that PowerShell wraps in a PSObject. This eliminates the need to explicitly cast parameters to make them strongly typed on each call to InvokeCSharp, and to always wrap them in @().
    Figuring this one out was interesting; it required diving in the innards of the PowerShell Extended Type System (ETS) which gets in the way when you work with .NET reflection classes from PowerShell. PowerShell tries to make the ETS transparent to the PowerShell user, which in general is a good thing but in this case meant it was hard to see what was happening.
  • Adds support for calling static methods, including overloaded methods, without creating a class instance.
  • Adds support for creating a class instance and providing constructor parameters.
  • Caches compiled assemblies instead of class instances. This improves performance because it eliminates duplicate compilation when the same C# code is called more than once. In addition to assembly caching you can of course also cache the object instances using any of the standard PowerShell mechanisms; however this is better handled outside the InvokeCSharp function.
  • Support for inline C# and separate C# source files; the latter allows you to easily edit the C# in Visual Studio, e.g. to use IntelliSense and compile the source to catch compile time errors.

PowerShell Source

So here are the goods – the complete PowerShell source for InvokeCSharp:

#&lt; # #.Synopsis #   Create a class instance and/or invoke a method on C# source code. #   For details, see: #   http://vincenth.net/blog/archive/2009/10/27/call-inline-c-from-powershell-with-invokecsharp.aspx  #.Description #   This function compiles the provided C# source code (only if necessary, compiled #   assemblies are cached). The code can be supplied either inline in a string or #   in a source file. Relative source filename paths are interpreted #   relative to the parent folder of the calling script. #   This enables simple organization and reference of related .ps1 and .cs files. # #   If a method name is specified, that method is invoked with any supplied #   parameters and the function then returns the result of the method. #   Both static and nonstatic methods are supported, including overloaded #   methods. # #   If no method name is specified, an instance of the specified class is #   created, passing any supplied parameters to the class constructor, and the #   function then returns the class instance. You can then program against the #   class instance in standard PowerShell fashion. # #   If necessary for compilation, you can specify names of any referenced #   assemblies. #.Parameter code #   The complete C# source code - effectively this is an inline C# source file. #   Note that it is possible but not necessary to declare classes within a #   namespace. #   Either the code parameter or the file parameter must be specified. #.Parameter file #   Absolute or relative path to a C# source file. Relative paths (beginning #   with '.\' or '..\') are interpreted relative to the folder that contains #   the calling script (i.e. the script that calls this function). #   Either the code parameter or the file parameter must be specified. #.Parameter class #   The name of the class to be instantiated / that contains the method to #   be invoked. Include the namespace in which the class is declared, if any. #   If you specify the -file parameter and the class name is identical to the #   file name, you can omit the -class parameter. #.Parameter method #   Either the name of the method to be invoked, '()' to invoke a  #   constructor to create and resturn a class instance, or '' to return #   the cached assembly. The last option allows you to precompile assemblies #   without calling a method or creating a class instance. #     #   If the specified method is not static, an instance of the class will #   be created on the fly. #.Parameter parameters #   To pass parameters to the constructor or method, specify them as an #   array of objects (e.g. with the comma operator or @() ). #.Parameter reference #   If necessary, specify an array of assembly filenames to be added as #   assembly references when the source code is compiled. #.Parameter forceCompile #   Specify this switch to force recompilation. This is useful if you specify #   the file parameter and the file contents has changed (when specifying a #   source file name, the cache key is the file name only - changes to the file #   do not automatically cause cache invalidation). #.Returns #   Either the cached assembly, a class instance or a method result (depending  #   on the value specified for the method parameter). #.Example #   # Create a class instance from a .cs file, passing a parameter to the class constructor: #   # Note that the class name can be omitted here, since it is implied from the .cs file name #   $myClassInstance = InvokeCSharp -file '.\MyClass.cs' -parameters 'A text' #     #   # Call some simple inline code. Note that you could also wrap the code in a namespace,  #   # add using statements and reference other assemblies. #   $code = @" #       public class MyClass2 #       { #           public static int MyStaticMethod(int a, int b) #           { #               return a * b; #           } #       } #   "@ #   $result = InvokeCSharp -code $code -class 'MyClass2' -method 'MyStaticMethod' -parameters 3, 7 # Will return 21 ##&gt;
function global:InvokeCSharp
{
    param(
        [string] $code = '',
        [string] $file = '',
        [string] $class = '',
        [string] $method = '()',
        [Object[]] $parameters = $null,
        [string[]] $reference = @(),
        [switch] $forceCompile
    )
 
    # Stores a cache of generated assemblies. If this library is dot-sourced
    # from a script, these objects go away when the script exits.
    if(-not (Test-Path Variable:\macaw.solutionsfactory.assemblycache))
    {
        ${GLOBAL:macaw.solutionsfactory.assemblycache} = @{}
    }
 
    if (($code -eq '') -and ($file -eq '')) { throw 'Neither code nor file are specified. Specify either one or the other.' }
 
    # If a source file was specified, see if it was already loaded, compiled and cached:
    if ($file -ne '')
    {
        if ($code -ne '') { throw 'Both code and file are specified. Specify either one or the other.' }
 
        # We interpret the current directory as the directory containing the calling script, instead of the currect directory of the current process.
        if ($file.StartsWith('.'))
        {
            $callingScriptFolder = Split-Path -path ((Get-Variable MyInvocation -Scope 1).Value).MyCommand.Path -Parent
            $file = Join-Path -Path $callingScriptFolder -ChildPath $file
        }
 
        # If no class name is  specified, we assume by convention that the file name is equal to the class name.
        if ($class -eq '') { $class = [System.IO.Path]::GetFileNameWithoutExtension($file) }
 
        # Use the real full path as the cache key:
        $file = [System.IO.Path]::GetFullPath((Convert-Path -path $file))
        $cacheKey = $file
    }
    else
    {
        # See if the code has already been compiled and cached
        $cacheKey = $code
    }
    if ($class -eq '') { throw 'Required parameter missing: class' }
 
    # See if the code must be (re)compiled:
    $cachedAssembly = ${macaw.solutionsfactory.assemblycache}[$cacheKey]
    if(($cachedAssembly -eq $null) -or $forceCompile)
    {
        if ($code -eq '') { $code = [System.IO.File]::ReadAllText($file) }
        Write-Verbose "Compiling C# code:`r`n$code`r`n"
 
        # Obtains an ICodeCompiler from a CodeDomProvider class.
        $provider = New-Object Microsoft.CSharp.CSharpCodeProvider 
 
        # Get the location for System.Management.Automation DLL
        $dllName = [PsObject].Assembly.Location
 
        # Configure the compiler parameters
        $compilerParameters = New-Object System.CodeDom.Compiler.CompilerParameters 
 
        $assemblies = @("System.dll", $dllName)
        $compilerParameters.ReferencedAssemblies.AddRange($assemblies)
        $compilerParameters.ReferencedAssemblies.AddRange($reference)
        $compilerParameters.IncludeDebugInformation = $true
        $compilerParameters.GenerateInMemory = $true 
 
        # Invokes compilation.
        $compilerResults = $provider.CompileAssemblyFromSource($compilerParameters, $code)
 
        # Write any errors if generated.
        if($compilerResults.Errors.Count -gt 0)
        {
            $errorLines = ""
            foreach($error in $compilerResults.Errors)
            {
                $errorLines += "`n`t" + $error.Line + ":`t" + $error.ErrorText
            }
            Write-Error $errorLines
        }
        # There were no errors.  Store the resulting assembly in the cache.
        else
        {
            ${macaw.solutionsfactory.assemblycache}[$cacheKey] = $compilerResults.CompiledAssembly
        }
 
        $cachedAssembly = ${macaw.solutionsfactory.assemblycache}[$cacheKey]
    }
 
    # Prevent type mismatch issues caused by PowerShell wrapping of managed objects in PSObject.
    # We need to explicitly unwrap those objects because otherwise the .NET reflection classes will
    # not find the constructor or method whose signature matches the specified parameters.
    # This unwrapping eliminates the need to always wrap all your parameters in @() and to explicitly
    # cast each parameter to the correct type in each call to InvokeCSharp.
    if ($parameters -ne $null)
    {
        for($i = 0; $i -lt $parameters.Length; $i++)
        {
            $parameters[$i] = [System.Management.Automation.LanguagePrimitives]::ConvertTo( `
                $parameters[$i], `
                [System.Type]::GetType($parameters[$i].GetType().FullName) `
            )
        }
    }
 
    if ($method -eq '') # We return the assembly
    {
        $result = $cachedAssembly
    }
    elseif ($method -eq '()') # We create and return a class instance
    {
        $result = $cachedAssembly.CreateInstance($class, $false, [System.Reflection.BindingFlags]::CreateInstance, $null, $parameters, $null, @())
    }
    else # We invoke the method and return the method result
    {
        $classType = $cachedAssembly.GetType($class)
 
        $parameterTypes = @()
        if ($parameters -ne $null) { foreach($p in $parameters) { $parameterTypes += $p.GetType() } }
 
        $methodInfo = $classType.GetMethod($method, [System.Type[]]$parameterTypes)
        if ($methodInfo.IsStatic)
        {
            $instance = $null
        }
        else
        {
            $instance = $cachedAssembly.CreateInstance($class, $false, [System.Reflection.BindingFlags]::CreateInstance, $null, $null, $null, @())
        }
        $result = $methodInfo.Invoke($instance, $parameters);
    }
 
   return $result
}

Factory Feature – Create Component

Tuesday, September 15th, 2009

With Create Component you can quickly add instances of the sample template components to your existing solution. You simply select the component type and specify the name of the new component; all other actions are automated. The component is created automatically in the correct project and folder, its namespace is set accordingly, and if the correct project is not present in the solution it is added on the fly with all correct namespaces, project settings etc.

Create Component is a feature that was added to the Macaw Solutions Factory in version 2.1.

Note: there are several series of Macaw Solutions Factory posts, on multiple blogs.
You can read all posts on the Macaw Solutions Factory site, or use this feed.

Basically, the Create Components feature allows you to think and work quickly at the logical level when creating components. You do not need to think about which project, folder, project items or settings are needed to implement the component. Instead, all that stuff is generated for you; you can start directly with implementing the component’s internals.

The Create Component feature only works on MSBuild-based projects, so this feature is not available in the Bts2006 and the Sql2008 or Sql2005 SS*S solutions.

This is how Create Component works in detail:

  1. You start by creating a baseline solution with the MAST Solution Builder, by selecting an action in the Factory Guide under ‘Create Functional Area’.

    All you need to do now is type the name of the new Functional Area (VS solution), and select which components types (VS projects) the Functional Area should contain initially.
    Note that you only need to select the component types (projects) of which you know for sure that you will need them; you can add component types to an existing solution any time you want.
    When you click Create, the new solution is built and all namespaces, project settings etc are set to the correct values.After you close the solution builder window, you will be asked whether you want to remove the sample components and sample code from the new solution:

    And whether you want to open the new solution:

    After you make these choices, the Product.config file will be opened in VS instance where you selected the ‘Create Functional Area’ action, and the new Functional Area will be added automatically to the first non-Templates machine role:

    If you have more than one machine role – other than Templates – in your product, you may want to move the Functional Area to a different machine role now.

  2. When the new solution is opened in VS you will see a node ‘Create Component’ in the Factory Guide, with a lot of actions in it:
    In any non-Templates solution these Create Component actions are generated on the fly, based on the Sample components in the Templates Functional Area for the current Product Dependency.
    E.g. in the DotNet3Templates.sln you can easily recognize the sample components that correspond to the actions in above screenshot:

    For each project item in a template solution whose name starts with ‘Sample’, an action is generated under Create Component. The name of the action specifies both the project name and the component name (without ‘Sample’).
    If a template project does not contain any sample components, an action is generated to only add the project to your solution (e.g. ‘New OperationalManagement’ in the above screenshot).

    You can easily modify the template solutions to change, add or remove sample components. You need to select the “Refresh ‘Create Component’ actions” action to regenerate the actions after you make this type of changes.

  3. When you execute a Create Component action, you are prompted for the component name and everything else is done automatically:

    When you select OK, the component is added and it is opened in the editor:
    If necessary, the proper project for the new component will be created on the fly. You can see whether this will happen beforehand in the name prompt dialog:

    When you select OK, the MAST solution builder window will open and the new project will be created:

    As you can see in the log window the solution builder performs a lot of actions automatically, e.g. setting namespaces and project properties, and adding the project target to the FxCop project file for this solution.

    When you close the MAST solution builder window, the new component will be added and it is opened in the editor:

     

  4. The new component that you added contains example code, to show you a typical implementation pattern.
    This sample code is marked with “region Sample” and “endregion Sample” texts – the exact syntax is specific for the file type (.cs, .xml, .sql, .aspx, .ascx etc.).
    If you do not need the sample code, you can quickly remove it from a new component by selecting the Factory Guide action “Remove Sample Content From Active Document”:

Even though the description of what Create Component does for you is quite long, using it is really quick and simple:

  1. Click on the Create Component action for the component type that you want to add
  2. Supply the name for the new component

Easy does it. Njoy!