views:

1242

answers:

4

Hello,

I don't know if this is too specific a question, if that is possible, but I'm having to port an app that uses Castle Windsor to Unity so that there isn't a reliance on non-microsoft approved libraries. I know I know but what are you going to do.

Anyway I've managed it but I'm not happy with what I've got. In Windsor I had this:

Register(
            AllTypes.Of(typeof(AbstractPresenter<>)).FromAssemblyNamed("Links.Mvp"),
            AllTypes.Of(typeof(IView)).FromAssemblyNamed("Links.WinForms").WithService.FromInterface());

which I've converted to this in unity

RegisterType<IMainView, MainView>();
        RegisterType<IConfigureLinkView, ConfigureLinkView>();
        RegisterType<IConfigureSourceView, ConfigureSourceView>();
        RegisterType<IConfigureSinkView, ConfigureSinkView>();
        RegisterType<MainPresenter, MainPresenter>();
        RegisterType<ConfigureLinkPresenter, ConfigureLinkPresenter>();
        RegisterType<ConfigureSourcePresenter, ConfigureSourcePresenter>();
        RegisterType<ConfigureSinkPresenter, ConfigureSinkPresenter>();

As you can see I'm having to register every single thing rather than be able to use some sort of auto-configuration. So my question is: is there a better way of doing this in unity?

Thanks,

Adam.

A: 

Cool. This feature is not in unity yet but if you felt a bit ambitious you could setup your own convention based registration. Found below is a snipped that works for the executing assembly and interfaces. Good luck.

P.S. This feels like a big hack, I would probably continue just registering all types by hand.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Reflection;

namespace Forum
{
    class Program
    {
        static void Main(string[] args)
        {
            // get your assemblies and types you can register
            Assembly a = Assembly.GetExecutingAssembly();
            var types = a.GetTypes();            
            var bindTo = from t in types
                         where t.IsAbstract || t.IsInterface
                         select t;

            // apply your conventions to filter our types to be registered
            var interfacePairs = from t in bindTo.Where(x => x.IsInterface)
                                 let match = types.FirstOrDefault(x => x.Name ==     t.Name.Substring(1))
                                 where match != null
                                 select new Pair { To = t, From = match };
            var abstractPairs = new Pair[] {};


            // setup the generic form of the method to register the types
            var thisType = typeof(Program);
            var bindings = BindingFlags.Static | BindingFlags.Public;
            MethodInfo genericMethod = thisType.GetMethod("RegisterType", bindings);            

            // register all your types by executing the 
            // specialized generic form of the method
            foreach (var t in interfacePairs.Concat(abstractPairs))
            {
                Type[] genericArguments = new Type[] { t.To, t.From };
                MethodInfo method = genericMethod.MakeGenericMethod(genericArguments);
                method.Invoke(null, new object [] {});
            }

            Console.ReadKey();
        }

        public static void RegisterType<To, From>()
        {
            Console.WriteLine("Register { To: {0} From: {1} }", typeof(To), typeof(From));
        }

        // Test classes that should be picked up
        interface ITest { }
        class Test : ITest { }

        class Pair
        {
            public Type To { get; set; }
            public Type From { get; set; }
        }        
    }
}
smaclell
Not so much of a hack, as the source to CastleWindsor does something similar (be it, far more elegantly). A better idea for him would be to rip out the code used for Register(), and make an extension method off of the Unity container/
eduncan911
Just so I am clear you mean taking it out of Windsor then applying it to Unity? Sure why not! Ya... elegance? Maybe next project ; ).
smaclell
A: 

AFAIK there's no way to do it in Unity. Unity is simply much less mature and worse architected container than Windsor, and as a result of that many things are harder/impossible with it.

Krzysztof Koźmic
+2  A: 

Check this out:

        var container = new UnityContainer();

        container
            .ConfigureAutoRegistration()
            .LoadAssemblyFrom("Plugin.dll")
            .IncludeAllLoadedAssemblies()
            .ExcludeSystemAssemblies()
            .ExcludeAssemblies(a => a.GetName().FullName.Contains("Test"))
            .Include(If.Implements<ILogger>, Then.Register().UsingPerCallMode())
            .Include(If.ImplementsITypeName, Then.Register().WithTypeName())
            .Include(If.Implements<ICustomerRepository>, Then.Register().WithName("Sample"))
            .Include(If.Implements<IOrderRepository>,
                     Then.Register().AsSingleInterfaceOfType().UsingPerCallMode())
            .Include(If.DecoratedWith<LoggerAttribute>,
                     Then.Register()
                            .AsInterface<IDisposable>()
                            .WithTypeName()
                            .UsingLifetime<MyLifetimeManager>())
            .Exclude(t => t.Name.Contains("Trace"))
            .ApplyAutoRegistration();
Artem Govorov
shameless self promotion! I love it.
smaclell
Also very nice source code. If I have to use Unity I will definitely include it ;).
smaclell
A: 

I just stumbled across this question looking for information on convention-based registration for Windsor and while this is a fairly old question I thought I'd leave an answer for others who may be looking for this sort of capabilities in Unity.

Last year I wrote a convention-based registration extension for Unity which you can read about here. The actual download is available on google code here. The basic usage is:

  _container
        .Using<IConventionExtension>()
        .Configure(x =>
            {
                x.Conventions.Add<InterfaceImplementionNameMatchConvention>();
                x.Assemblies.Add(Assembly.GetExecutingAssembly());
            })
        .Register();

There is also a ClosingTypeConvention for auto-registering open generic types:

  _container
                .Using<IConventionExtension>()
                .Configure(x =>
                    {
                        x.Conventions.Add(new ClosingTypeConvention(typeof (IRepository<>)));
                        x.Assemblies.Add(Assembly.GetExecutingAssembly());
                    })
                .Register();
Derek Greer