0

I am using Autofac and I would like to isolate the main application from all the dependencies I register in the Autofac container.

This is a possible scenario:

I have a solution containing 4 projects. ProjectA is main application, a C# console program, containing the usual static class Program which I renamed to ClassA. ProjectB contains only ClassB and projectC contains ClassC. Both ClassB and ClassC are behind their interfaces, IClassB and IClassC, which are placed at projectD. ClassA calls a method of ClassB through IClassB. ClassB calls a method of ClassC through IClassC.

Inside ClassA I build the Autofac container and register the assemblies from projects B and C:

static class ClassA
{
    static void Main()
     {
        var builder = new ContainerBuilder();
        builder.RegisterType<ClassB>().As<IClassB>();
        builder.RegisterType<ClassC>().As<IClassC>();
        IContainer container = builder.Build();
    }
}

At this point ProjectA has references to all other projects, even if alternatively register the dependencies using app.config based Autofac configuration.

Is there any way I make ProjectA to depend only on ProjectD where the interfaces are placed instead of depending on all projects?

Pedro Duarte
  • 815
  • 6
  • 7

1 Answers1

1

This is what you can do in two steps:

  1. First step would be having all project dependencies in the same place. To do this for projects B,C,D click Properties > Build > Output path set the bin/debug directory of project A. Now when you compile the solution you should see the dll's for A,B,C,D in project A's bin/debug folder.

  2. Next you need to get all the types from the assembly you specify an register them in the dependency injection container of choice, in this case Autofac, like this:


    class ClassA
    {
        static void Main(string[] args)
        {
            // specify here the projects needed to resolve your types
            var projects = new List<string> { "ProjectB", "ProjectC", "ProjectD" };
            var types = GetAllTypes(projects);
            var interfaces = types.Where(t => t.IsInterface).ToList();

            // init autofac container builder
            var builder = new ContainerBuilder();

            // add types to the builder
            foreach (var interfaceType in interfaces)
            {
                var implementingType = types.FirstOrDefault(t => interfaceType.IsAssignableFrom(t) && !t.IsInterface);

                if (implementingType != null)
                {
                    builder.RegisterType(implementingType).As(interfaceType);
                }
            }

            var container = builder.Build();

            // test ClassB and ClassC can be instantiated through their interface
            using (var scope = container.BeginLifetimeScope())
            {
                var classB = scope.Resolve<IClassB>();
                var classC = scope.Resolve<IClassC>();
            }
        }

        // get all the classes and interfaces from the selected projects
        private static List<Type> GetAllTypes(List<string> projects) =>
            Directory.EnumerateFiles(AppDomain.CurrentDomain.BaseDirectory)
                .Where(f => f.ToLower().EndsWith(".dll"))
                .Where(f => projects.Any(a => Assembly.ReflectionOnlyLoadFrom(f).GetName().Name.StartsWith(a)))
                .Select(a => Assembly.ReflectionOnlyLoadFrom(a).FullName)
                .Select(Assembly.Load)
                .SelectMany(a => a.GetTypes())
                .ToList();

    }

Note - this approach assumes that you want to register one type per interface. Doing something else requires more complex registration strategies, but you can always add that later.

  • Seems like a way to do it indeed. I will wait some more time to see if other alternatives are proposed before I mark your reply as accepted. – Pedro Duarte Jun 14 '17 at 14:53