• An Approach to Extending MSBuild

    download

    I’m working toward setting up a master build for our enterprise code using MSBuild. My first pass was to include a common build project in each of our csproj files. This was a bad idea for two reasons. One it’s a pain to update each csproj file. And two, VS complains when you open a csproj file with modifications. So, it was off to find a different approach, this was inspired by Sayed Ibrahim. The idea is to call your common build project file with the csproj file name and import that, giving you access to that files properties. One issue is the relative paths are now based on your common build projects location, this didn’t end up being to much of an issue. So anyway, here some code … err XML:

        <Target Name="CoreDeploy" DependsOnTargets="CoreBuild">

            <MSBuild Projects="SubProjects\Publish.proj" Targets="Deploy"

                     Properties="ProjectFile=%(ProjectsToBuild.FullPath);OutputDirectory=$(OutputRootDirectory);IISVirtualDirectory=%(ProjectsToBuild.IISVirtualDirectory)">

            </MSBuild>

        </Target>

    </p>

    The above calls into the Publish.proj file, which in turn includes the passed in ProjectFile.

     

        <Import Project="$(ProjectFile)" Condition="$(ProjectFile)!=‘’"/>

        <ItemGroup>

            <ProjectPath Include="$(ProjectFile)"></ProjectPath>

        </ItemGroup>

    </p>

    The above bit of code includes the project file in the Deploy.proj allowing it’s targets, properties, and items to be called.

    Using the above approach I could have common setup project and a common deploy project, I’ll blog about these in following posts.

  • Setting Up AutoMapper For Use From A Container

    I like getting all my service from the container and I like using AutoMapper. So I added the AutoMapper to the container, a WindsorContainer in this case.

    The main interface is IMappingEngine, which is implemented by MappingEngine which also implements IMappingEngineRunner. This contains a getter to an IConfiguration interface which is also an IConfigurationExpression. So to replace Mapper you need an instance of IMappingEngine and then you can configure the engine as follows:

    public CustomMapper(IMappingEngine engine)

    {

        engine = engine;</p>

     

        var runner = </em>engine as IMappingEngineRunner;

        if (runner == null)

            return;

     

        var configuration = runner.Configuration as IConfigurationExpression;

        if (configuration == null)

            return;

     

        configuration.CreateMap<IApplication, B.Application>();

    }

    </p></div>

    Next setting up the container, the MappingEngine needs an IConfiguration, the Configuration needs some IObjectMapper instances.

    private static void RegisterMappingEngine()

    {

        container.Register(AllTypes.Of<IObjectMapper>()</p>

                                .FromAssembly(typeof (IObjectMapper).Assembly)

                                .Configure(configurer => configurer.Named(configurer.ServiceType.Name))

                                .Configure(configurer => configurer.LifeStyle.Transient));

     

        </em>container.Register(Component.For<IConfiguration>().ImplementedBy<Configuration>()

                                .LifeStyle.Transient.ServiceOverrides(

                                ServiceOverride.ForKey("mappers").Eq(

                                    "CustomTypeMapMapper",

                                    "TypeMapMapper",

                                    "NewOrDefaultMapper",

                                    "StringMapper",

                                    "EnumMapper",

                                    "ArrayMapper",

                                    "EnumerableMapper",

                                    "NullableMapper",

                                    "AssignableMapper")

                                ));

     

        _container.Register(Component.For<IMappingEngine>().ImplementedBy<MappingEngine>()

                                .LifeStyle.Transient);

    }

    </p></div>

    These are all set as Transient because each CustomMapper can be Singleton and I don’t want the mappings to over lap between the CustomMappers.

    </p>

  • Testing Log4Net Output

    We have a custom logging framework setup which is using log4net under the covers. When doing that the ILogger should be used, not the ILog. The ILogger sets up the correct Class information.

    Below is some code for testing the output from log4net, this code is just using log4net to write the message, which is not valuable as a test, but when the logging call is replaced with a custom framework you can make sure you’re getting what you expect in your log events.

    private MemoryAppender appender;</p>

    private LoggingEvent </em>event;

     

    [SetUp]

    public void Context()

    {

        appender.Clear();</p>

        // Using log4net here, but should be replace with a custom logging class

        // which calls ILogger.Log under it.

        LogManager.GetLogger(GetType()).Warn("my test desc.", new Exception("my test exception."));

        </em>event = appender.GetEvents()[0];</p>

    }

     

    [FixtureSetUp]

    public void SetupLog4NetMemoryAppender()

    {

        </em>appender = new MemoryAppender();

        appender.Name = "Unit Testing Appender";</p>

        </em>appender.Layout = new log4net.Layout.PatternLayout("%date{dd-MM-yyyy HH:mm:ss,fff} %5level [%2thread] %message (%logger{1}:%line)%n");

        appender.Threshold = Level.All;</p>

        </em>appender.ActivateOptions();

     

        Logger root = ((Hierarchy) LogManager.GetRepository()).Root;

        root.AddAppender(appender);</p>

        root.Repository.Configured = true;

    }

     

    [FixtureTearDown]

    public void RemoveLog4NetMemoryAppender()

    {

        Logger root = ((Hierarchy)LogManager.GetRepository()).Root;

        root.RemoveAppender(</em>appender);

        root.Repository.Configured = true;

    }

     

    [Test]

    public void Should_have_When_logging_type_in_log()

    {

        var data = _event.GetLoggingEventData();

        var info = data.LocationInfo;

        info.ClassName.ShouldEqual("Core.Tests.Instrumentation.When_logging_with_CustomLogger");

        info.MethodName.ShouldEqual("Context");

    }

    </p></div>

    BTW, the code highlighting was done using the Copy As HTML plug in from here.

    </p>

  • TDD Reduces Defects!

    I just read Realizing quality improvement through test driven development. It’s a summary of 4 TDD teams at IBM and Microsoft compared to similar non TDD teams. With all the variables in any software project it’s hard to draw a hard conclusion, but this shows definite trends toward fewer defects minimal increased time, and I’d argue less time, because the reduced number of defects. I think the time measurement here is really subjective as it’s based on the judgment of the project managers. Regardless, I think these types of studies help prove the importance of good software practices. 

  • Open VS2005 Project Files in VS2008

    Here’s an interesting tip from a co worker. If you need to work on a 2005 project in both 2005 and 2008 you can add the following to the Project element: ToolsVersion=”3.5” Resulting in:

  • Thanks to My Digital Life!

    I now have Live Writer installed on my Windows Server 2003 machine. Thanks My Digital Life and Resource Hacker!

    Windows Live team, please change the Live Writer install.

  • Making Sure Your Container Contains Your Basic Services

    I’m working on creating some of the base code for our systems. One of the things we’re doing is moving to using IoC containers for our projects. These projects are all internal and as such they will be sharing some base functionality, like logging and configuration. To that end I wanted to create an implementation of WindsorContainer which has these services and facilities already setup. What I found was the IWindsorInstaller class. Using that in the constructor of my NewWindsorContainer I can call Install with any number of these installers, like:

    public class MyContainer : WindsorContainer
    {</p>
    
    
    
    
    <span class="kwrd">public</span> MyContainer(<span class="kwrd">string</span> settingName)
    {
        Install(<span class="kwrd">new</span> InstrumentationInstaller());
    }
    

    }

    The Installer interface is just one method Install which takes a container and a configuration store. This makes these installer classes easy to implement and test. Below we just make sure the logging facility is registered.

    public class InstrumentationInstaller :IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.AddFacility<MyLoggingFacility>();
        }
    }

    And the test code:     

    [Test]
    public void Install_Expect_Facility_Installed_Can_Resolve_Logger()
    {
        var installer = new InstrumentationInstaller();
        var c = new WindsorContainer();
        installer.Install(c, null);
        var logger = c.Resolve<IMyLogger>();
        Assert.IsNotNull(logger);
    }

    I was initially going to create a facility to load our base services, but I like this approach more because it's simpler and the installer classes follow Single Responsibility Principle.