For my international reader, a bit of context is in order for this post: Microsoft Denmark sponsors a series of Christmas challenges known as the Microsoft Christmas Calendar. Different bloggers alternate hosting a coding challenge for the day, and Microsoft sponsors the prizes.

Today I have the honor of hosting the last challenge for 2010. Contestants from the Danish .NET community have the opportunity to win a Molecular Gastronomy Starter Kit - if any of my international readers feel like joining in, they are welcome, but (probably) not eligible for the prize.

The challenge #

The Managed Extensibility Framework (MEF) enables us to compose applications by annotating types and members with [Import] and [Export] attributes, but what can you do when you have types without these attributes and you can't add the attributes?

For example, how can we compose Mayonnaise from these three classes?

public sealed class EggYolk { }
 
public sealed class OliveOil { }
 
public sealed class Mayonnaise
{
    private readonly EggYolk eggYolk;
    private readonly OliveOil oil;
 
    public Mayonnaise(EggYolk eggYolk, OliveOil oil)
    {
        if (eggYolk == null)
        {
            throw new ArgumentNullException("eggYolk");
        }
        if (oil == null)
        {
            throw new ArgumentNullException("oil");
        }
 
        this.eggYolk = eggYolk;
        this.oil = oil;
    }
 
    public EggYolk EggYolk
    {
        get { return this.eggYolk; }
    }
 
    public OliveOil Oil
    {
        get { return this.oil; }
    }
}

The challenge is to come up with a good solution to that problem. Here are the formal rules:

  • The unit test suite at the end of this post must pass.
  • You are not allowed to edit the unit tests.
  • You are only allowed to add one (1) using directive to the unit test file to reference the namespace of your proposed solution.
  • You must work from the Visual Studio 2010 solution attached to this post. Add a new project that contains your solution. Send me the solution in a .zip file to enter the contest.
  • You are allowed to implement your solution in any language you would like as long as it compiles and runs from Visual Studio 2010 Premium.
  • The winner is chosen by my subjective judgment, but I will emphasize clean code and design. A tip: attempt to get as good scores as possible from Visual Studio's Code Analysis and Code Metrics. Good scores does not guarantee that you win, but bad scores will most likely ensure that you don't.
  • Since many of you are on Christmas vacation the deadline is this year. As long as you submit a solution in 2010 (Danish time) you're a contestant.

There are lots of different ways to skin this cat, so I'm looking forward to your submissions to see all your creative solutions.

This unit test suite is the specification:

using System.ComponentModel.Composition.Hosting;
using Ploeh.Samples.MeffyXmas.MenuModel;
using Xunit;
 
namespace Ploeh.Samples.MeffyXmas.MefMenu.UnitTest
{
    public class ContainerBuilderFacts
    {
        [Fact]
        public void DefaultContainerCorrectlyResolvesOliveOil()
        {
            CompositionContainer container = new ContainerBuilder()
                .Build();
            var oil = container.GetExportedValue<OliveOil>();
            Assert.NotNull(oil);
        }
 
        [Fact]
        public void DefaultContainerCorrectlyResolvesEggYolk()
        {
            CompositionContainer container = new ContainerBuilder()
                .Build();
            var yolk = container.GetExportedValue<EggYolk>();
            Assert.NotNull(yolk);
        }
 
        [Fact]
        public void DefaultContainerCorrectlyResolvesMayonnaise()
        {
            CompositionContainer container = new ContainerBuilder()
                .Build();
            var mayo = container.GetExportedValue<Mayonnaise>();
            Assert.NotNull(mayo);
        }
 
        [Fact]
        public void DefaultContainerReturnsSingletonMayonnaise()
        {
            CompositionContainer container = new ContainerBuilder()
                .Build();
            var mayo1 = container.GetExportedValue<Mayonnaise>();
            var mayo2 = container.GetExportedValue<Mayonnaise>();
            Assert.Same(mayo1, mayo2);
        }
 
        [Fact]
        public void WithTransientMayonnaiseReturnTransientMayonnaise()
        {
            CompositionContainer container = new ContainerBuilder()
                .WithNonSharedMayonnaise()
                .Build();
            var mayo1 = container.GetExportedValue<Mayonnaise>();
            var mayo2 = container.GetExportedValue<Mayonnaise>();
            Assert.NotSame(mayo1, mayo2);
        }
 
        [Fact]
        public void TransientMayonnaiseByDefaultContainsSingletonEggYolk()
        {
            CompositionContainer container = new ContainerBuilder()
                .WithNonSharedMayonnaise()
                .Build();
            var mayo1 = container.GetExportedValue<Mayonnaise>();
            var mayo2 = container.GetExportedValue<Mayonnaise>();
            Assert.Same(mayo1.EggYolk, mayo2.EggYolk);
        }
 
        [Fact]
        public void TransientMayonnaiseByDefaultContainsSingletonOil()
        {
            CompositionContainer container = new ContainerBuilder()
                .WithNonSharedMayonnaise()
                .Build();
            var mayo1 = container.GetExportedValue<Mayonnaise>();
            var mayo2 = container.GetExportedValue<Mayonnaise>();
            Assert.Same(mayo1.Oil, mayo2.Oil);
        }
 
        [Fact]
        public void TransientMayonnaiseCanHaveTransientEggYolk()
        {
            CompositionContainer container = new ContainerBuilder()
                .WithNonSharedMayonnaise()
                .WithNonSharedEggYolk()
                .Build();
            var mayo1 = container.GetExportedValue<Mayonnaise>();
            var mayo2 = container.GetExportedValue<Mayonnaise>();
            Assert.NotSame(mayo1.EggYolk, mayo2.EggYolk);
        }
 
        [Fact]
        public void TransientMayonnaiseCanHaveSingletonOil()
        {
            CompositionContainer container = new ContainerBuilder()
                .WithNonSharedMayonnaise()
                .WithNonSharedEggYolk()
                .Build();
            var mayo1 = container.GetExportedValue<Mayonnaise>();
            var mayo2 = container.GetExportedValue<Mayonnaise>();
            Assert.Same(mayo1.Oil, mayo2.Oil);
        }
 
        [Fact]
        public void TransientMayonnaiseCanHaveTransientOil()
        {
            CompositionContainer container = new ContainerBuilder()
                .WithNonSharedMayonnaise()
                .WithNonSharedOil()
                .Build();
            var mayo1 = container.GetExportedValue<Mayonnaise>();
            var mayo2 = container.GetExportedValue<Mayonnaise>();
            Assert.NotSame(mayo1.Oil, mayo2.Oil);
        }
 
        [Fact]
        public void TransientMayonnaiseCanHaveSingletonEggYolk()
        {
            CompositionContainer container = new ContainerBuilder()
                .WithNonSharedMayonnaise()
                .WithNonSharedOil()
                .Build();
            var mayo1 = container.GetExportedValue<Mayonnaise>();
            var mayo2 = container.GetExportedValue<Mayonnaise>();
            Assert.Same(mayo1.EggYolk, mayo2.EggYolk);
        }
 
        [Fact]
        public void PureTransientMayonnaiseIsTransient()
        {
            CompositionContainer container = new ContainerBuilder()
                .WithNonSharedMayonnaise()
                .WithNonSharedEggYolk()
                .WithNonSharedOil()
                .Build();
            var mayo1 = container.GetExportedValue<Mayonnaise>();
            var mayo2 = container.GetExportedValue<Mayonnaise>();
            Assert.NotSame(mayo1, mayo2);
        }
 
        [Fact]
        public void PureTransientMayonnaiseHasTransientEggYolk()
        {
            CompositionContainer container = new ContainerBuilder()
                .WithNonSharedMayonnaise()
                .WithNonSharedEggYolk()
                .WithNonSharedOil()
                .Build();
            var mayo1 = container.GetExportedValue<Mayonnaise>();
            var mayo2 = container.GetExportedValue<Mayonnaise>();
            Assert.NotSame(mayo1.EggYolk, mayo2.EggYolk);
        }
 
        [Fact]
        public void PureTransientMayonnaiseHasTransientOil()
        {
            CompositionContainer container = new ContainerBuilder()
                .WithNonSharedMayonnaise()
                .WithNonSharedEggYolk()
                .WithNonSharedOil()
                .Build();
            var mayo1 = container.GetExportedValue<Mayonnaise>();
            var mayo2 = container.GetExportedValue<Mayonnaise>();
            Assert.NotSame(mayo1.Oil, mayo2.Oil);
        }
    }
}

ContainerBuilder is the class you must implement, so the unit tests don't compile until you make them. Meffy xmas!

clip_image002



Wish to comment?

You can add a comment to this post by sending me a pull request. Alternatively, you can discuss this post on Twitter or somewhere else with a permalink. Ping me with the link, and I may respond.

Published

Friday, 24 December 2010 09:29:06 UTC

Tags



"Our team wholeheartedly endorses Mark. His expert service provides tremendous value."
Hire me!
Published: Friday, 24 December 2010 09:29:06 UTC