Foreach now captures variables! (Access to modified closure)

Foreach has changed in C# 5.0!

Consider the following piece of code in C# < 5.0:

public class Test
{
    public static void Main()
    {
        var words = new[] { "foo", "bar", "baz", "beer" };
        var actions = new List<Action>();
        foreach (string word in words)
        {
            actions.Add(() => Console.WriteLine(word));
        }

        actions.ForEach(e => e());
    }
}

What will this print?

Some of you will see the warning that ReSharper will print on line 9.

Access to foreach variable in closure. May have different behaviour when compiled with different versions of compiler

Notice the second sentence, and remember this warning, we’ll get back to it!

Now go ahead, try and run this in Visual Studio 2010. This will be your result:

beer beer beer beer

While I do love beer, this is not what I expect.

So how do we fix it? Well, either let ReSharper fix it (Alt+Enter -> Enter), or manual, capture the current word in a different variable:

public class Test
{
    public static void Main()
    {
        var words = new[] { "foo", "bar", "baz", "beer" };
        var actions = new List<Action>();
        foreach (string word in words)
        {
            string temp = word;
            actions.Add(() => Console.WriteLine(temp));
        }

        actions.ForEach(e => e());
    }
}

Problem solved. The code above has identical results in Visual Studio 2012.

However…

Using the first piece of code (without our temp variable) in Visual Studio 2012 the result is as follows:

foo bar baz beer

Wait what?

The compiler has changed (note that even for .NET 3.5, 4, and 4.5 in Visual Studio 2012 the 4.5 compiler is used!).

Meaning that our variable word is now declared inside of the foreach loop, and not outside.

This change can be found in the C# 5.0 spec, page 247-248, found on your machine when you’ve installed VS2012 (not Express) in: C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC#\Specifications\1033

If v was declared outside of the while loop, it would be shared among all iterations, and its value after the for loop would be the final value, 13, which is what the invocation of f would print. Instead, because each iteration has its own variable v, the one captured by f in the first iteration will continue to hold the value 7, which is what will be printed. (Note: earlier versions of C# declared v outside of the while loop.)

Note 1: read the file to get the meaning of ‘v’ and those values (like 13 and 7).
Note 2: I’ve tweeted to some guys to get the spec online.

While this is not necessarily a problem for projects coming from 2010 and upgrading to 2012, it can be an issue when you are doing round-tripping, for example in mixed teams. Developers using 2012 need to use the old behavior.

Worse, your build system is not at 2012 yet, so your result are different!

Watch out for this!

Have a good one,

-Kristof

When using an enum in PowerShell, use the member’s name, not the member’s value

Consider the following enum in C#:

enum State
{
    Started,
    Stopped,
    Unknown
}

Note that I have not added an explicit value for the enum members. They will be generated by the compiler. As stated in the C# spec:

… its associated value is set implicitly, as follows:

  • If the enum member is the first enum member declared in the enum type, its associated value is zero.
  • Otherwise, the associated value of the enum member is obtained by increasing the associated value of the textually preceding enum member by one. This increased value must be within the range of values that can be represented by the underlying type, otherwise a compile-time error occurs.

Found at http://www.microsoft.com/en-us/download/details.aspx?id=7029, page 400-401 (I can’t find the version for 4.5 though…).

Now what are the consequences of this? Consider the following piece of PowerShell:

$result = $serviceController.GetServiceStatus()
if($result -eq 1)
{
    MyLib.StartService()
}

This will work, because PowerShell implicitly converts the int to the actual enum member.

However since we are assuming the value can go wrong. In the next version you add extra values, say for example to represent a starting/stopping service:

enum State
{
    Starting,
    Started,
    Stopping,
    Stopped,
    Unknown
}

Since now all the values are shifted when you run your PowerShell again you start the service when it’s already started 😉 .

Solution?

First of all (as a consumer), use the enum’s member name instead of its value:

$result = $serviceController.GetServiceStatus()
if($result -eq [MyLib.State]::Stopped)
{
    MyLib.StartService()
}

This will ensure that you get the value for Started, not for anything else.

As a developer of a library you should ensure that you never mess up the order of an enum, by adding new values as last, or (prefered) set the value yourself:

enum State
{
    Started = 0,
    Stopped = 1,
    Unknown = 2,
}

Becomes:

enum State
{
    Starting = 3,
    Started = 0,
    Stopping = 4,
    Stopped = 1,
    Unknown = 2,
}

And now you can also perfectly reorder them so the numbers are sequential:

enum State
{
    Started = 0,
    Stopped = 1,
    Unknown = 2,
    Starting = 3,
    Stopping = 4,
}

Hope you have a good one,

-Kristof

ServerConnection and Login failed for user. Reason: Attempting to use an NT account name with SQL Server Authentication

Today I had to work with the ServerConnection class.

This class provides a method to specify the connection to the Server class.

So usage would be like this:

# http://sqlblog.com/blogs/allen_white/archive/2008/04/28/create-database-from-powershell.aspx
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO')  
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.ConnectionInfo')  

$sqlServer = "server"
$username = "username"
$password= "password"

$serverConnection = new-object Microsoft.SqlServer.Management.Common.ServerConnection($sqlServer, $username, $password)

$server = new-object Microsoft.SqlServer.Management.Smo.Server($serverConnection)

Write-Host ("SQL Version: {0}" -f $server.Information.Version)

$server.databases | % { Write-Host $_.Name }

Now this works for SQL accounts, but not for domain accounts.

My username was in the form of DOMAIN\username, but that failed.

Checking the SQL Server log, it yielded this:

Login failed for user. Reason: Attempting to use an NT account name with SQL Server Authentication
Login failed for user. Reason: Attempting to use an NT account name with SQL Server Authentication

So to use the domain account with this object you need to create the $serverConnection like this AND you need to specify your username in the form of: username@domain.local (FQN). Entering DOMAIN\username doesn’t seem to work.

$sqlServer = "server"
$username = "username@domain.local"
$password= "password"

$serverConnection = new-object Microsoft.SqlServer.Management.Common.ServerConnection($sqlServer)

$serverConnection.ConnectAsUser = $true
$serverConnection.ConnectAsUsername = $username
$serverConnection.ConnectAsUserPassword = $password

You need to use the ConnectAsUsername and ConnectAsUserPassword to use domain accounts and set the ConnectAsUser property to true.

When I connect with those options I get the access I need.

You can verify it by executing the following query:

$conn.ExecuteScalar("SELECT SUSER_NAME() as [foo]")

Which nicely yields:

username@domain.local

Have a good one,

-Kristof