Ben
Ben

Reputation: 62454

How do I resolve C# dependencies automatically?

I've been reading about Unity's dependency injection and I understand it's a thing and that it allows you to type a class to an interface. What I'm curious about is, do I HAVE to? In the below scenario there's a TerrainGenerator and TileCreator in the same space. How can I get the TileCreator within the generator as a dependency?

enter image description here

http://geekswithblogs.net/danielggarcia/archive/2014/01/23/introduction-to-dependency-injection-with-unity.aspx walks me through registering a type, but I read somewhere that as long as the class is visible in the Unity Assets section it'll be able to auto inject it, I just can't figure out the syntax (if it's possible).

Update

I could put all the classes in a single file... with a large system that could be pretty annoying. In the meantime it's an approach I'll try - better than having it not work at all.

update Seems like Unity should be able to look at a class' constructor and perform these resolutions automatically and inject them in my class' constructor. Is that possible?

Upvotes: 15

Views: 3124

Answers (4)

yonisha
yonisha

Reputation: 3096

No, you don't have to use interfaces, you can register and resolve concrete types as well.

For example, you can register the TerrainGenerator and TileCreator as follows:

var myTileCreator = new TileCreator();
container.RegisterType<TerrainGenerator>(new PerThreadLifetimeManager(), new InjectionFactory(c => new TerrainGenerator(myTileCreator)));

To resolve TerrainGenerator:

TerrainGenerator generator = container.Resolve<TerrainGenerator>();

To resolve TerrainGenerator with a different TileCreator:

TerrainGenerator generator = container.Resolve<TerrainGenerator>(new ParameterOverride("tileCreator", new TileCreator()));

You may want to read Dependency Injection with Unity - Patterns and Practices for more useful information like properties injection and alike.

Hope that helps.

Upvotes: 1

AVS
AVS

Reputation: 531

Don't think it matters if you have the classes in the same file or not. Unity needs to know how to create the instance given the type.

If RegisterInstance is used, the specific object passed as argument is returned everytime Resolve is called for the type. If the type is registered using RegisterType (or not registered at all for concrete classes), Unity will try to instantiate the type by using the constructor with most number of arguments. For each of the parameter types, Unity will try to resolve them recursively.

Registering mappings for interface types to concrete types is mandatory but registering concrete types themselves is optional.

Sample code:

using Microsoft.Practices.Unity;
using System;

namespace Unity
{  
    interface IFooBar
    {
        string Message();
    }

    class Foo
    {
        string msg;

        public Foo()
        {
            msg = "Hello";
        }

        public override string ToString()
        {
            return msg;
        }
    }

    class Bar
    {
        private Foo _f;
        private IFooBar _fb;
        public Bar(Foo f, IFooBar fb)
        {
            this._f = f;
            this._fb = fb;
        }

        public override string ToString()
        {
            return _f.ToString() + " World " + _fb.Message();
        }
    }

    class FooBar : IFooBar
    {
        public string Message()
        {
            return "Unity!";
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            UnityContainer container = new UnityContainer();
            container.RegisterType<IFooBar, FooBar>(); // required
            container.RegisterType<Foo>(); // optional
            container.RegisterType<Bar>(); // optional

            var mybar = container.Resolve<Bar>();

            Console.WriteLine(mybar);
        }
    }
}

https://msdn.microsoft.com/en-us/library/microsoft.practices.unity.iunitycontainer_methods(v=pandp.20).aspx

Upvotes: 1

Joe Sonderegger
Joe Sonderegger

Reputation: 804

I always use the following code. When I load an application, the application looks in the directory for all Dlls. This way when you load a class with reflection it searches for the Dlls and exes. You can also add some more paths to search.

AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(currentDomain_AssemblyResolve);

Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        string defaultFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

        string assemblyName = new AssemblyName(args.Name).Name;

        string assemblyNameDll = assemblyName + ".dll";
        string assemblyNameExe = assemblyName + ".exe";

        string assemblyPathDll = Path.Combine(defaultFolder, assemblyNameDll);
        string assemblyPathExe = Path.Combine(defaultFolder, assemblyNameExe);

        string assemblyPathToUse = null;
        if (File.Exists(assemblyPathDll))
        {
            assemblyPathToUse = assemblyPathExe;
        }
        else if (File.Exists(assemblyPathExe))
        {
            assemblyPathToUse = assemblyPathExe;
        }
        else
        {
            IEnumerable<string> merge = AssemblyFolders.Values;
            if (!string.IsNullOrEmpty(TempLoadingFolder))
            {
                merge = AssemblyFolders.Values.Union(new List<string>() { TempLoadingFolder });
            }
            foreach (var folder in merge)
            {
                assemblyPathDll = Path.Combine(folder, assemblyNameDll);
                assemblyPathExe = Path.Combine(folder, assemblyNameExe);

                if (File.Exists(assemblyPathDll))
                {
                    assemblyPathToUse = assemblyPathDll;
                    break;
                }
                else if (File.Exists(assemblyPathExe))
                {
                    assemblyPathToUse = assemblyPathExe;
                    break;
                }
            }
        }

        Assembly assembly = null;

        if (assemblyPathToUse != null && File.Exists(assemblyPathToUse))
        {
            assembly = Assembly.LoadFrom(assemblyPathToUse);
        }
        return assembly;
    }

Upvotes: 4

Nate
Nate

Reputation: 660

If you are looking for DI for the Unity3d engine, maybe this would work (I've not used it, but the feedback is positive) https://github.com/modesttree/Zenject

If you are talking about Microsoft's Unity DI library, you should be able to do this:

container.RegisterTypes(
    AllClasses.FromLoadedAssemblies(),
    WithMappings.FromMatchingInterface,
    WithName.Default);

Upvotes: 8

Related Questions