Tuesday, October 21, 2008

Adelaide Geek Dinner Handover

As I have recently accepted a new job interstate, I will no longer be organising dinners for fellow developers in Adelaide. However, rather than allow such a fun event to disappear, I am passing the awesome responsibility of organising the dinners to my friend and colleague, Jim Burger, whom many of you have met on previous occasions.

I will be giving Jim a hard time to make sure he continues to arrange a dinner at least once each quarter and I believe it will also help to ensure he remembers to attend them himself ;). Names and contact details of previous attendees and people who have registered their interest will be given to Jim so he can send you the next invite but let him or I know if you'd rather be removed from the list.

If you've never been invited to an Adelaide Geek Dinner before but want to be on the list, just contact Jim or I and you'll surely be invited to the next one.

 Tuesday, October 14, 2008

Strongly Typed Windows Forms Data Binding

I was recently inspired by a question about property names in strings on Stack Overflow. The problem is that Windows Forms data binding takes the names of properties to bind as string parameters and if these properties change or are removed, errors won't surface until the bound control is exercised at run-time. So, using Paul's strongly typed property name ideas, I propose this alternative syntax for programmatic data binding in Windows Forms:

nameTextBox.Bind(t => t.Text, aBindingSource, (Customer c) => c.FirstName);
// Binds the Text property on nameTextBox to the FirstName property
// of the current Customer in aBindingSource, no string literals required.

And the following code to implement support for it:

public static class ControlExtensions
{
    public static Binding Bind<TControl, TDataSourceItem>(this TControl control, Expression<Func<TControl, object>> controlProperty, object dataSource, Expression<Func<TDataSourceItem, object>> dataSourceProperty) where TControl: Control
    {
        return control.DataBindings.Add(PropertyName.For(controlProperty), dataSource, PropertyName.For(dataSourceProperty));
    }
}

public static class PropertyName
{
    public static string For<T>(Expression<Func<T, object>> property)
    {
        var member = property.Body as MemberExpression;
        if (null == member)
        {
            var unary = property.Body as UnaryExpression;
            if (null != unary) member = unary.Operand as MemberExpression;
        }
        return null != member ? member.Member.Name : string.Empty;
    }
}

Thoughts? Overkill?

 Monday, August 04, 2008

Binding... and IList<T> does not inherit from IList.

A note to myself, and to others who may find it useful:

Use System.Collections.ObjectModel.Collection<T> (or a subclass) as the return type for properties exposing collections of related items on your presentation model classes if you intend to bind to the relationships with Windows Forms BindingSources.

Collection<T> appears to be the lowest-level type in Framework 3.5 that provides both the non-generic IList implementation as required by the binding system and also provides strongly-typed programmatic access to the elements of the collection. It is used as the base for BindingList<T>, ObservableCollection<T> and many others and you can easily inherit from it yourself.

Using a return type of IList<T> or ICollection<T> instead, which may be preferred for being less concrete, is not sufficient as neither of these inherit from the non-generic IList and will fail to bind correctly (sometimes throwing exceptions) at run-time when the parent list of the bound relationship is empty.

 Friday, August 01, 2008

Adelaide Geek Dinner August 2008 Announcement

As promised in May, it is time for the August Geek Dinner, specifically the time will be 6:30pm on Saturday 16th August. Everyone seemed to enjoy the food and atmosphere of Caffe Amore on the corner of Pulteney and Pirie Streets last time so we'll be going there again this time.

If you're already on the geek dinner mailing list from previous times expect an invitation shortly. If you've never been invited before or you think your invite may be lost in the mail, just contact me and I'll make sure you get included.

Please RSVP before Wednesday 13th August so I can finalise the booking with the restaurant. Hope to see you all there!

 Saturday, July 05, 2008

TFS Deployer Least Privilege

The original TFS Deployer attempted to access various restricted resources than effectively required it to run as an administrator user. Since some changes were made in February, Deployer can now be executed under a standard user account with some additional considerations which follow.

Team Foundation Server Permissions

The TFS Deployer service account must be a member of the Team Foundation Valid Users server-level group on the server specified by the TeamFoundationServerUrl config setting and also a member of the Readers project-level group for the Team Projects that Deployer will be monitoring.

If you intend to make use of the Retain Build feature, the Deployer service account will also need the Write To Operational Build Store project-level permission on applicable Team Projects.

PowerShell Execution Policy

TFS Deployer can execute Windows Command Scripts if you really need it to but PowerShell scripts are the preferred option. However, PowerShell is built to be secure-by-default and installs with script-running capabilities disabled. It is left for the user to enable scripts after PowerShell has been installed and can be performed by opening the PowerShell console as an administrator and executing either:

Set-ExecutionPolicy AllSigned ; # or
Set-ExecutionPolicy RemoteSigned

The former choice will require you to sign all your deployment scripts in source control with a certificate trusted by the Deployer. Scott Hanselman has a comprehensive post on his blog describing the signing process. The latter choice will not impose this requirement because files retrieved from source control by Deployer are not marked with a zone identifier.

HTTP Namespace Reservation

TFS Deployer utilises self-hosted WCF over HTTP to subscribe to Build Quality Change events from TFS. This technique relies on the Windows HTTP Server API which requires the Deployer service account to have been granted rights to listen on the portion of the HTTP URL namespace it uses for event notification. By default only local administrators have access to the entire namespace and permissions must be granted using the httpcfg tool (on Windows XP and Server 2003) or the netsh tool (on Vista and Server 2008).

Using the netsh tool, from an administrator command prompt, the following command (as a single line) will grant rights to the appropriate URL for TFS Deployer (assuming you are using the default port number):

netsh http add urlacl url=http://+:8881/BuildStatusChangeEvent user=DOMAIN\TfsDeployerUser

Using the httpcfg tool is more difficult as it requires an ACL string in Security Descriptor Definition Language (SDDL). However, Dominick Baier has created a tool to simplify this process and I can recommend it.

Additional Permissions

All deployment scripts executed by TFS Deployer will be executed under the context of the service account unless the scripts explicitly contain alternate credentials. If the scripts copy files to a network share, configure a web site in IIS, or even upgrade the schema of a SQL database, ensure that the service account also has the minimum privileges to perform those actions too.

Any problems, questions, or suggestions... let me know.

 Wednesday, July 02, 2008

Explaining new features in TFS Deployer

Since I began helping Mitch with changes to TFS Deployer to make it compatible with TFS 2008, I have slowly been adding small features that seemed useful in my work environment. However, unless you've read the code, you probably wouldn't know they exist so I'll describe them here.

Quality Mapping Wildcard

TFS Deployer operates on executing scripts for particular build quality transitions. Normally transitions like "Test" to "Production" are common. However you may wish to have a script that runs when the quality is set to "QA" regardless of what is was before. You can now specify asterisk (*) in place of the value of either of the OriginalQuality or the NewQuality attributes in the DeploymentMappings.xml file to specify a match on any quality.

Retain Build

Team Build in TFS 2008 now has Retention Policies to clean out old builds. If you deploy a build with TFS Deployer you don't want the policy deleting the build outputs. You may also want builds to become subject to policy again after you retire them from failed acceptance testing. You can add a RetainBuild attribute to a Mapping element in the D*M*.xml file. A value of "true" will mark the build to be kept upon successful execution of the deployment script. A value of "false" will unmark it, and an absent attribute will leave the retain flag alone.

Shared Deployment Resources

With multiple Team Builds defined and multiple deployment configurations you're bound to end up with scripts with common components you'd like to refactor out to a shared location. You can now specify a TFS path (ie $/MyProj/TeamBuildTypes/Shared/Deployment/) in the TfsDeployer.exe.config file in the SharedResourceServerPath application setting. If provided TFS Deployer will get the files in this directory and place them in the same folder as the deployment script on each script run.

The first two features are in the latest release drop on CodePlex. The latter is only in the latest change set, so check the Source Code tab if you intend to use shared resources. Any problems, questions, or suggestions... let me know.

 Saturday, May 24, 2008

Adelaide Geek Dinner May 2008 Summary

Photo of the guests The May Geek Dinner was long overdue but well worth the wait. I invited a record high number of people, fifteen, but due to a variety of circumstances, a record low number of people made it to the dinner: five, including myself.

I was joined by the always entertaining regulars: Paul Stovell, Keith Zerna, and Jim Burger. We were also pleased to be joined by Nigel Spencer for the first time since I started organising the dinners last year.

Aside from the usual .NET subjects, many other topics were discussed on the night, here are some links in no particular order:

I am writing this summary a week since the night of the dinner and I've managed to forget some of the night's events. Perhaps the other attendees could leave some comments with links to the interesting topics I've missed.

Watch this blog for details of the August 2008 Dinner which I'll likely post after Code Camp SA.

Using IDisposable and Lambdas for temporary values

What follows is some code I wrote last week that I couldn't find anywhere else and I thought was useful. First, is a test method demonstrating my use case, followed by the implementation of the TemporaryValue classes to fulfil the requirement.

The property get lambda expression can be an instance or static property as long as it is read-write. In this implementation there is no exception handling and no multithreading considerations.

[TestMethod]
public void Original_property_value_should_be_restored_after_temporary_value_block()
{
    const string OriginalValue = "_original_value_";
    const string NewValue = "_new_value_";
    var f = new Foo {Bar = OriginalValue};

    Assert.AreSame(OriginalValue, f.Bar);

    using (TemporaryValue.For(() => f.Bar, NewValue))
    {
        Assert.AreSame(NewValue, f.Bar);
    }

    Assert.AreSame(OriginalValue, f.Bar);
}

public static class TemporaryValue 

    public static TemporaryValue For(Expression<Func> propertyGet, TValue newValue) 
    { 
        return new TemporaryValue(propertyGet, newValue); 
    } 

 
public class TemporaryValue : IDisposable 

    private PropertyInfo _propertyInfo; 
    private object _target; 
    private TValue _original; 
 
    public TemporaryValue(Expression<Func> propertyGet, TValue newValue) 
    { 
        var memberExpression = (MemberExpression)propertyGet.Body; 
        _propertyInfo = (PropertyInfo)memberExpression.Member; 
 
        var targetExpression = memberExpression.Expression
        if (null != targetExpression) 
        { 
            _target = Expression.Lambda<Func<object>>(targetExpression).Compile().Invoke(); 
        } 
 
        _original = propertyGet.Compile().Invoke(); 
        Set(newValue); 
    } 
 
    private void Set(TValue value) 
    { 
        _propertyInfo.SetValue(_target, value, null); 
    } 
 
    public void Dispose() 
    { 
        if (null != _propertyInfo) 
        { 
            Set(_original); 
            _propertyInfo = null
            _target = null
            _original = default(TValue); 
        } 
    } 
}

After learning to manipulate lambda expressions in this way I decided it would be a good idea to use a similar approach to remove the need for string literals in an INotifyPropertyChanged implementation. Unfortunately, while I was offline for a few days, Paul got a head start.

 Sunday, April 27, 2008

Adelaide Geek Dinner May 2008

After the success of the local geek dinner in early February, I intended to organise another one in late March but I became a little overwhelmed at home and work and completely forgot to organise it. My apologies to those who had been looking forward to it.

However, I am now officially organising another dinner for Saturday May 17th. Instead of the usual location (where customer satisfaction has taken a turn for me) I will be relocating the dinner to another restaurant. I will strive to choose a replacement that is still located centrally, has good parking and public transport access, and affordable, enjoyable food with the option to split the bill.

At this point I'm considering Caffe Amore on the corner of Pulteney and Pirie Streets in the CBD, about 400m from the previous venue. I enjoyed dinner there with friends a few weeks ago and was quite pleased with their food and service. Assuming I can book a table there for all the guests and no one suggests a better alternative, this will probably be the new venue.

I will be sending an email invitation to all the previous dinner guests but if you don't receive yours before May or haven't attended before and would like to join us, please don't hesitate to contact me as soon as possible so I can give the restaurant an accurate number of guests to expect.

I hope to see a few people who made it to Code Camp Oz this year. I was unfortunately double-booked and unable to attend and would like to hear all about it.