Mobile Framework for Mobile 5.0 with a Compact (DI) Container

Note: In this blog I show how to deserialize XML into an object that has child objects. 

Although the core requirements for this phase of my current project will result in a Mobile/PDA application being completed by next week, I struggle having to work without a DI container (been real spoiled by Unity on the Web and Desktop side of the house).    

Preliminary research led me to an excellent (beta) DI container on codeplex (http://www.codeplex.com/compactcontainer/) by Mariano Julio Vicario.  Although it doesn't yet have constructor injection it provides setter injection which means I can inject my business logic layer as well as other reusable services.  Important since I've created a very dynamic framework.

I ran into two snags... the first was that it doesn't support .NET 3.5 - the serialization crashes and burns when I plugged it into a prototype application (source attached) that utilizes my framework.  I should clarify that Mariano's TestDI demo does run on the .NET 3.5 framework "as is" however it uses the .NET 2.0 assemblies.  When you create a new project and attempt to utilize the CompactContainer is where you will run into problems. 

The second was that the P&P folks released a new Mobile framework late Feb which replaced the CAB with a DI container.  Naturally I jumped on that, even at risk of scraping my framework, but was dismayed to see that it only supports Mobile 6.1 - so I ordered my new HP IPAQ 211 w/1gig ram but it won't be here until next week (about the time my application will be completed).   I'm chomping at the bit to play with the new Mobile framework....

So Mariano's DI container it is it for my current framework but I still had to get past the first snag - this blog has the details :)

I opted to go with LINQ to SQL and replaced the deserialization process with it.  Below is the line of code I changed to make the switch.

The magic is done with the new Extension class which is referenced at the top of the file as follows:

using CompactContainer.Tools;
using System.Xml.Linq;
 
// BillKrat.2009.03.18 - provide LINQ to XML extensions
using CompactContainer.Extensions;


The following Unit Test shows it in action:

using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Xml.Linq;
using CompactContainer.ConfigurationObjects;
using CompactContainer.Extensions;
 
namespace CompactContainer.Tests
{ 
    /// <summary>
    /// Summary description for UnitTest1
    /// </summary>
    [TestClass]
    public class CompactContainerFixture
    {
        public CompactContainerFixture() {  }
 
        private XElement config = XElement.Parse (
            @"<?xml version='1.0' encoding='utf-8' ?>
            <CompactContainer xmlns='http://tempuri.org/ConfigurationSchema.xsd'>
              <Context Name='default'>
                <ObjectDefinition Name='Demo' 
                                  Type='PasswordManager.Login.LoginViewPresenter' 
                                  Singleton='false' 
                                  FileName='PasswordManager.exe'>
                    <Property Name='Str' 
                              Set='Hi Set of String' />
                    <Property Name='Inte' 
                              Set='28' />
                    <Property Name='Something' 
                              SetWithNewType='TestDI.BehaviourISomething' 
                              FileName='PasswordManager.exe' />
                    <Property Name ='Obj2' 
                              SetWithObjectDefinition='Object2'/>
                </ObjectDefinition>
                <ObjectDefinition Name='Object2' 
                                  Type='PasswordManager.Main.MainViewPresenter' 
                                  Singleton='true' 
                                  FileName='PasswordManager.exe'>
                  <Property Name='Inte2' Set='98' />
                </ObjectDefinition>
              </Context>
            </CompactContainer> ");
 
        /// <summary>
        /// Determines whether this instance [can read configuration file].
        /// </summary>
        [TestMethod]
        public void CanReadConfigurationFile()
        {
            CContainer container = new CContainer();
 
            // Uses new CompactContainerExtensions
            container.Items = config.GetContext();
 
 
            Assert.AreEqual(1, container.Items.Length, 
                "There should only be one context");
            Assert.AreEqual(2, container.Items[0].ObjectDefinitions.Length, 
                "There should be two Object Definitions");
 
            ObjectDefinition demo = container.Items[0].ObjectDefinitions[0];
            ObjectDefinition object2 = container.Items[0].ObjectDefinitions[1];
 
            Assert.AreEqual("default", container.Items[0].Name,
                "The context name should be 'default'");
 
            Assert.AreEqual("Demo", demo.Name);
            Assert.AreEqual("PasswordManager.Login.LoginViewPresenter", demo.Type);
            Assert.IsFalse(demo.Singleton);
            Assert.AreEqual("PasswordManager.exe", demo.FileName);
            Assert.IsNotNull(demo.Properties.First<Property>(
                p => p.Set.Contains("Hi Set of String")));
            Assert.IsNotNull(demo.Properties.First<Property>(
                p => p.Set.Contains("28")));
            Assert.IsNotNull(demo.Properties.First<Property>(
                p => p.SetWithNewType.Contains("TestDI.BehaviourISomething")));
            Assert.IsNotNull(demo.Properties.First<Property>(
                p => p.FileName.Contains("PasswordManager.exe")));
            Assert.IsNotNull(demo.Properties.First<Property>(
                p => p.SetWithObjectDefinition.Contains("Object2")));
 
            Assert.AreEqual("Object2", object2.Name);
            Assert.AreEqual("PasswordManager.Main.MainViewPresenter", object2.Type);
            Assert.IsTrue(object2.Singleton);
            Assert.AreEqual("PasswordManager.exe", object2.FileName);
            Assert.IsNotNull(object2.Properties.First<Property>(
                p => p.Name.Contains("Inte2") && p.Set.Contains("98")));
        }
    }
 
 
}

The new Extension class follows:

using System.Collections.Generic;
using CompactContainer.ConfigurationObjects;
using System.Xml.Linq;
 
namespace CompactContainer.Extensions
{
    /// <summary>
    /// 
    /// </summary>
    public static class XElementExtensions
    {
        /// <summary>
        /// Attrs the STR.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="fieldName">Name of the field.</param>
        /// <returns></returns>
        public static string AttrStr(this XElement element, string fieldName)
        {
            object data = element.Attribute(fieldName);
            return data == null ? "" : element.Attribute(fieldName).Value;
        }
 
        /// <summary>
        /// Attrs the bool.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="fieldName">Name of the field.</param>
        /// <returns></returns>
        public static bool AttrBool(this XElement element, string fieldName)
        {
            object value = element.Attribute(fieldName);
            return value == null ? false :
                "true".Contains(element.Attribute(fieldName).Value.ToLower());
        }
 
        /// <summary>
        /// Gets the properties.
        /// </summary>
        /// <param name="prop">The prop.</param>
        /// <returns></returns>
        public static Property[] GetProperties(this XElement prop)
        {
            List<Property> propList = new List<Property>();
            foreach (XElement pr in prop.Elements())
                propList.Add(new Property
                {
                    FileName = pr.AttrStr("FileName"),
                    KeyType = pr.AttrStr("KeyType"),
                    ListType = pr.AttrStr("ListType"),
                    Name = pr.AttrStr("Name"),
                    Set = pr.AttrStr("Set"),
                    SetDictionary = pr.AttrStr("SetDictionary"),
                    SetList = pr.AttrStr("SetList"),
                    SetWithNewType = pr.AttrStr("SetWithNewType"),
                    SetWithObjectDefinition = pr.AttrStr("SetWithObjectDefinition"),
                    ValueType = pr.AttrStr("ValueType")
                });
            return propList.ToArray();
        }
 
        /// <summary>
        /// Gets the object definitions.
        /// </summary>
        /// <param name="objDef">The obj def.</param>
        /// <returns></returns>
        public static ObjectDefinition[] GetObjectDefinitions(this XElement objDef)
        {
            List<ObjectDefinition> odList = new List<ObjectDefinition>();
            foreach (XElement od in objDef.Elements())
                odList.Add(new ObjectDefinition
                {
                    FileName = od.AttrStr("FileName"),
                    Name = od.AttrStr("Name"),
                    Properties = od.GetProperties(),
                    Singleton = od.AttrBool("Singleton"),
                    Type = od.AttrStr("Type")
                });
            return odList.ToArray();
        }
 
        /// <summary>
        /// Gets the context.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        public static Context[] GetContext(this XElement context)
        {
            List<Context> contextList = new List<Context>();
            foreach (XElement child in context.Elements())
                contextList.Add(new Context
                {
                    Name = child.AttrStr("Name"),
                    ObjectDefinitions = child.GetObjectDefinitions()
                });
            return contextList.ToArray();
        }
    }
}

The following is a short screencast (no audio) that shows the framework in action (the virtual methods in PresenterBase provide hooks into events that are not normally available, i.e., OnLoad for user controls) :
http://global-webnet.net/Webcast/PasswordManager.htm 

http://mobile5framework.codeplex.com/ <= Source code

MobileFlow.txt (5.97 kb)  <= Shows the flow of events for Views and their User Controls (best viewed in notepad with Arial 10 font - alignment)

 


Tags: , ,
Categories:


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know