Unity for Metro Applications

Jan 22, 2012 at 1:23 PM
Edited Jan 22, 2012 at 1:57 PM
randylevy wrote:

Unity 3.0 preview is out and I don't see anything specifically targeting Metro Style applications.  You could submit a feature request on the uservoice site.

FYI, I have a Unity port in progress for Unity (until one is available) at http://solrcontrib.codeplex.com/.   The intent for this port is to support a Multi-targeted environment for Unity (so I can use the real thing on Desktop, Silverlight and Windows Phone) while sharing the same code with Metro applications using my port; I'm not trying to be Unity, simply maintaining compatibility with it so all platforms will compile the same code (obviously it will have a limited feature set).   In the TDD process I have pulled in over 170 test fixtures from Unity, Prism and MetroIoc.  I'm abandoning Prism for the most part but kept the EventAggregator and some of the concepts (as you'll see in code below).

If you are interested in helping - I am interested in the help.

Below is my BootstrapperBase which shows Unity's part:

using Gwn.Library.Base.Exceptions;
using Gwn.Library.Base.Loggers;
using Gwn.Library.Base.Modules;
using Gwn.Library.Interfaces;
using Microsoft.Practices.Prism.Events;
using Microsoft.Practices.Unity;

namespace Gwn.Library.Base
{
    public class BootstrapperBase
    {
        private IModuleCatalog _moduleCatalog;
        public IUnityContainer Container { get; set; }

        public BootstrapperBase() { }

        public virtual void Run()
        {
            var container = new UnityContainer();
            Run(container);
        }

        private void Run(IUnityContainer container)
        {
            Container = container;
            InitializeContainer();

            _moduleCatalog = Container.Resolve<IModuleCatalog>();

            RegisterModules(_moduleCatalog);
            InitializeModules(_moduleCatalog);
        }

        private void InitializeContainer()
        {
            Container
                .RegisterType<IErrorBase, ExceptionBase>()
                .RegisterType<ILogger, DefaultLogger>()
                .RegisterType<IEventAggregator,EventAggregator>(new ContainerControlledLifetimeManager())
                .RegisterType<IModuleCatalog, ModuleCatalog>(new ContainerControlledLifetimeManager())
                ;
        }

        protected virtual void InitializeModules(IModuleCatalog catalog)
        {
            foreach (IModule module in catalog.GetModules())
                module.Initialize();

            ModulesInitialized(catalog);
        }

        protected virtual void RegisterModules(IModuleCatalog catalog) { }
        protected virtual void ModulesInitialized(IModuleCatalog catalog) { }

    }
}
The following is my MockBootstrapper for unit test purposes:
using Gwn.Library.Base;
using Gwn.Library.Interfaces;

namespace Gwn.Library.Tests.Mocks
{
    public class MockBasicBootstrapper : BootstrapperBase
    {
        protected override void RegisterModules(IModuleCatalog catalog)
        {
            catalog
                .Add(typeof(MockModule))
                .Add(typeof(MockModule2));
        }
    }
}

With both the MockModule and MockModule2 classes containing essentially the following code:
using System;
using System.Collections.Generic;
using Gwn.Library.Base;
using Gwn.Library.Events;
using Microsoft.Practices.Prism.Events;
using Microsoft.Practices.Unity;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Gwn.Library.Tests.Mocks
{
    public class MockModule : ModuleBase
    {
        private int ProcessCounter;
        public bool IsInitialized { get; set; }
        public int Counter { get; set; }
        public object LocalEventData { get; set; }

        public MockModule(IUnityContainer container) : base(container) { }

        public override void Initialize()
        {
            IsInitialized = true;
            Counter++;
        }

        public override T GetUnitTestResults<T>(params object[] para)
        {
            var results = new List<string>();

            ErrorEvent += (s, e) =>{results.Add(e.GetException().Message);};
            LoggerEvent += (s, e) =>{results.Add(e.Message);};

            try { Throw<Exception>(para[0].ToString()); } catch { }

            Log(para[1].ToString());

            // Publish an event 
            EventAggregator.GetEvent<ProcessEvent>()
                .Publish(new ProcessEventArgs
                {
                    Id = Id, // Localized event
                    Data = new DataEventArgs<object>(para[2])
                });

            return (T)(object)
                new Tuple<bool, int, List<string>, object>(
                    IsInitialized, Counter, results, LocalEventData??"Failed");
        }

        public override void ProcessEventLocalHandler(ProcessEventArgs e)
        {
            ProcessCounter++;
            LocalEventData = e.Data;

            if (ProcessCounter > 1)
                Assert.Fail("Should not execute more than once!");
        }
    }
}

And finally - the Unit test:
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Gwn.Library.Tests.Mocks;
using Gwn.Library.Interfaces;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Prism.Events;

namespace Gwn.Library.Tests
{
    [TestClass]
    public class BootstrapperFixture
    {
        [TestMethod]
        public void CanRunBootstrapper()
        {
            var bootstrapper = new MockBasicBootstrapper();
            bootstrapper.Run();

            var container = bootstrapper.Container;
            Assert.IsNotNull(container);

            var catalog = container.Resolve<IModuleCatalog>();
            Assert.IsNotNull(catalog);

            // parameter array for test
            var para = new List<object> { "Hello", "World", "Aggregated Event" }.ToArray();

            // Test for proper initialization of MockModule
            //                                   ----------
            var mockModule = catalog.GetModule("MockModule");
            Assert.IsNotNull(mockModule);
            var results = ((ITest)mockModule)
                .GetUnitTestResults<Tuple<bool,int,List<string>,object>>(para);
            TestResults(results);

            // Test for proper initialization of MockModule2
            //                                   -----------
            var mockModule2 = catalog.GetModule("MockModule2");
            Assert.IsNotNull(mockModule2);
            results = ((ITest)mockModule2)
                .GetUnitTestResults<Tuple<bool, int, List<string>,object>>(para);
            TestResults(results);    

        }

        private void TestResults(Tuple<bool,int,List<string>,object> results)
        {
            var dataEventArgs = results.Item4 as DataEventArgs<object>;

            Assert.IsTrue(results.Item1, "Module should have been initialized");
            Assert.AreEqual(1, results.Item2, "Module should have been initialized only once");
            Assert.AreEqual("Hello", results.Item3[0], "Thrown exception wasn't captured");
            Assert.AreEqual("World", results.Item3[1], "Logger value wasn't captured");
            Assert.AreEqual("Aggregated Event", dataEventArgs.Value, "Aggregated Event should have fired!");
        }

    }
}