I love dependency injection frameworks ever since I started using them. Specifically, I’m obsessed with using Autofac and I have a hard time developing applications unless I can use a solid DI framework like Autofac! I’ve recently been working with Xamarin and found that I wanted to use dependency injection, but some of the framework doesn’t support this well out of the box. I’ was adamant to get something going though, so I wanted to show you my way to make this work.
Disclaimer: In its current state, this is certainly a bit of a hack. I’ll explain why I’ve taken this approach though!
In your Android projects for Xamarin, any class that inherits from Activity is responsible for being created by the framework. This means where we’d usually have the luxury of passing in dependencies via a constructor and then having Autofac magically wire them up for us isn’t possible. Your constructors for these classes need to remain parameterless, and your OnCreate method is usually where your initialization for your activity will happen. We can work around that though.
My solution to this is to use a bit of a reflection hack coupled with Autofac to allow Autofac resolutions in the constructor as close as possible as to how they would normally work. A solution I wanted to avoid was a globally accessible reference to our application’s lifetime scope. I wanted to make sure that I limited the “leakage” of this not-so-great pattern to as few places as possible. With that said, I wanted to introduce a lifetime scope as a reference only to the classes that were interested in using Autofac where they’d otherwise be unable to.
- Make a static readonly variable in your classes that care about doing Autofac with a particular name that we can lookup via reflection. (An alternative is using an attribute to mark a static variable)
- After building your Autofac container and getting your scope (but prior to using it for anything), use reflection to check all types that have this static scope variable.
- Assign your scope to these static variables on the types that support it.
- In the constructors of these classes (keeping them parameterless so the framework can still do its job!), access your static scope variable and resolve the services you need
Here’s what that looks like in code!
MainActivity.cs
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity { protected override void OnCreate(Bundle savedInstanceState) { var builder = new ContainerBuilder(); // TODO: add your registrations in! var container = builder.Build(); var scope = container.BeginLifetimeScope(); // the static variable i decided to use is called "_autofacHack" // so that it deters people from using it unless they know // what it's for! you could use reflection to find similar // fields with something like an attribute if you wanted. foreach (var field in GetType() .Assembly .GetTypes() .Select(x => x.GetField("_autofacHack", BindingFlags.NonPublic | BindingFlags.Static)) .Where(x => x != null)) { field.SetValue(null, scope); } LoadApplication(scope.Resolve<App>()); }
The class that can take advantage of this would look like the following:
public sealed class MyActivityThatNeedsDependencyInjection : Activity { private static readonly ILifetimeScope _autofacHack; private readonly IMyService _theServiceWeWant; // NOTE: we kept the constructor PARAMETERLESS because we need to public MyActivityThatNeedsDependencyInjection () { _theServiceWeWant= _autofacHack.Resolve<IMyService>(); } protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); // now we can use this service that we "injected" _theServiceWeWant.DoTheCoolStuff(); } }
Summary
Reading this you might think “well I don’t want to pollute my Xamarin code with variables that say _autofacHack, that’s gross”. And I don’t blame you! So this is to serve as a starting point for a greater solution, which I think is something I’ll evolve out for myself and I encourage you to do the same.
Things I’m focused on:
- Minimize where “ugly” code is. A globally accessible scope on a static class seems like it can spread the “ugly” code to too many spots. This approach is intended to help minimize that.
What are some next steps to make that EVEN better? Maybe an attribute so we can call it something nicer? - Write code that feels as close as possible to the “real” thing. Autofac usually allows us to specify services in the constructor and then automatically allows us to get the instances we need. This code is structured to be very similar, but since we’re NOT allowed to change the parameterless constructors, we resolve our services off the container there instead. And because it’s in the constructor, we can assign things to readonly variables as well which is a nice bonus.
The more implementations of this I go to use, the more I plan to refine it! How have you leveraged Autofac in your Xamarin projects?
I’ve been looking for best practices for this and came across your post. Setting the scope via reflection is an interesting idea. I’ve been refactoring an application to use Autofac and have my ILifetimeScope as a static variable in my abstract class of BaseActivity that my other Activity classes inherit from. I was curious to get some thoughts on that as a good/bad way to manage the scope.
Thanks Scott! I love using Autofac, so when I find frameworks/architectures that don’t allow me to do dependency injection with it, I often try to work around it 🙂 I’ve been doing similar things with my work in Unity3D, specifically because they don’t allow you to create instances of their component classes (MonoBehaviours) via dependency injection. My philosophy is that if I need to put some hacks in, I’d like to minimize the surface area of them. If I can hide the hacks and make the rest of the code feel like it’s solid, then I’m headed in the right direction.
Your abstract class + static is an interesting solution. I (to a fault) almost always avoid abstract classes, so I don’t think I ever considered this. Nice!
I would love to hear more if you continue to find other patterns that you find work well!