Tuesday, October 14, 2014

Intro To IoC

You can find lots of DI / IoC frameworks (Dependency Injection / Inversion of Control) , both open-source and not. The problem I had was finding something simple enough for what we needed. Here's the scenario:

I am currently playing around with some cross-platform POCs (Proof Of Concept). I wanted to cover all the platforms, so we're looking at Android, iOS, Windows Phone and Windows Store apps. Using Xamarin, building cross-platform applications is pretty nice, because you can re-use almost all your code in Shared Projects, and just segregate out the platform-specific stuff. The platform-specific projects would then contain minimal code ... only what's different for that platform. Xamarin works great for the first 3, but it currently doesn't do Windows Store apps (but that's not a problem, because you're still going to have to treat the Windows Store project as a platform-specific project anyway).

OK, now that I've got that plug for Xamarin out of the way, what issues was I having that I needed an IoC? Let's look at Web Services ... some of the platform-specific projects could only use a Web Service Reference, some could only use a Service Reference. The issue I ran into is that I couldn't put any generated web reference code into the Shared Projects, because what compiled OK for one platform wouldn't compile OK for another platform. So, what you do is use an IoC in the Shared Projects and the Android app could then create its web service reference object & add it to the IoC and the Windows Store app could create its service reference object add it to the IoC.  Then, in the Shared Project that actually needed to call the various web service methods, it just grabbed the instance out of the IoC and everybody's happy!

At first I was thinking, "Well, why do I need an IoC? Can't I just pass the reference to the web service object in the constructor of the Shared class?" Well, yes, I could do that. But here's why my husband, Gary, gets paid the big bucks ... he explained to me that IoC is better because it's more flexible. Think about these cross-platform apps ... what kinds of things are going to be platform-specific? Well, as it turns out, just about everything you might need to use: such as access to GPS, voice recognition, accelerometor, device orientation; just to name a few. These are the kinds of things that could be put into the IoC ... you can see how this could quickly get out of hand if we had to add a bunch of these object to the various class constructors! Not a pretty sight!!!

So, next we tried looking for some already existing IoC frameworks. The problem I had was still a cross-platform incompatibility. Xamarin has a built-in IoC, but since I needed to also target Windows Store apps, I couldn't use the Xamarin IoC. I tried several others, including TinyIoC, but nothing I tried would compile in all the platforms. So, back to the drawing board. We came up with a pretty simple IoC Dependency Manager and I can share the code with you here. I simply didn't need all the bells and whistles of a more complex IoC framework, this simple class suited my purpose just fine. Something more complex is beyond the scope of this blog post (and, anyway, I haven't written anything more complex)!

Here's the code:

public class DependencyManager
{
    private Dictionary<Type , object > DependencyManagerList { get; set; }
    private static DependencyManager m_DependencyManager;
    private static object m_lock = typeof( DependencyManager);
    public static DependencyManager Current
    {
        get
        {
            lock (m_lock)
            {
                if (m_DependencyManager == null)
                    m_DependencyManager = new DependencyManager();
                return m_DependencyManager;
            }
        }
    }
    private DependencyManager()
    {
        this.DependencyManagerList = new Dictionary<Type , object >();
    }
    public void Register<T>( object o)
    {
        if (!DependencyManagerList.ContainsKey( typeof(T)))
            DependencyManagerList.Add( typeof(T), o);
    }
    public object Resolve<T>()
    {
        if (DependencyManagerList.ContainsKey( typeof(T)))
            return DependencyManagerList[ typeof(T)];
        else
            return null;
    }
    public void Remove<T>()
    {
        if (DependencyManagerList.ContainsKey( typeof(T)))
            DependencyManagerList.Remove( typeof(T));
    }
}

Couldn't be much simpler than that, eh?

Here's an example of how to use it.

First, you need to use an Interface, since that's what the "Type" is in the Dictionary key for the DependencyManagerList. Here's mine (the "Message" in the code below is a class, I'm not actually going to define it here in the sample code, it's not necessary):

public interface IServiceClient : INotifyPropertyChanged
{
    Dictionary<string , Message > Messages { get; set; }
    void GetMessage( Message msg);
    object Data { get; set; }
    string Json { get; set; }
}

In the App.xaml.cs, you have a declaration for the IoC:

protected DependencyManager ioc = DependencyManager.Current;

... and in the OnLaunched event of the App, you'd register the ServiceClient object like so (note that I'm not including the code for the ServiceClient class, because it's not relevant to showing how this works ... but it MUST implement the IServiceClient Interface):

NameSearchStore.Classes.ServiceClient o = new NameSearchStore.Classes.ServiceClient ();
ioc.Register< IServiceClient>(o);

Now, in my ViewModel, where I need to use this ServiceClient, I declare it using the Interface:

protected IServiceClient Client;

And get the instance in the constructor of the ViewModel (it doesn't have to be ViewModel, it can be any class, but I'm using MVVM):

this.Client = (IServiceClient)DependencyManager.Current.Resolve<IServiceClient>();

Then, to call a method on the WebService, you just use the Interface methods, such as:

Message msg = new Message();
// more code here for setting up the msg info
this.Client.GetMessage(msg);

Enjoy and happy coding!

No comments:

Post a Comment