Reputation: 1018
Please skip to the UPDATE if you would like to just know the solution:
I have an application that uses the following code to get and run a number of worker methods
var type = typeof(IJob);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
.Where(x => x.IsClass && type.IsAssignableFrom(x));
foreach (Type t in types)
{
IJob obj = Activator.CreateInstance(t) as IJob;
obj.Run();
}
This code works perfectly as is. However, some of the newer jobs utilize dependency injection to populate their constructors so this method will not be viable going forward. So I was wondering if there's a way to do this with unity?
My original thought was that I would continue with the first half and then replace the foreach logic with resolve so that it looks something like the following.
var type = typeof(IJob);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
.Where(x => x.IsClass && type.IsAssignableFrom(x));
foreach (Type t in types)
{
IJob obj = Container.Resolve(t) as IJob;
obj.Run();
}
The problem is that as soon as I define my UnityContainer the returned types list that implement IJob suddenly gets bloated with all of these garbage Microsoft.Practices classes as shown below
It turns out then when refelecting over Assemblies if Unity is present it will attempt to reflect into Unity's assemblies which if Finalized with a ToList will throw an exception due to a missing metadata extension of IServiceLocator. To work around this appending a where clause after GetAssemblies() to limit scope to your desired namespace will allow the application to run properly.
var type = typeof(IJob);
var types = AppDomain.CurrentDomain.GetAssemblies()
.Where(x => x.FullName.StartsWith("YourNamespace"))
.SelectMany(x => x.GetTypes())
.Where(x => x.IsClass && type.IsAssignableFrom(x));
foreach (Type t in types)
{
IJob obj = Container.Resolve(t) as IJob;
obj.Run();
}
Upvotes: 4
Views: 9375
Reputation: 48230
Instead of searching through all assemblies, filter them by a custom attribute. This way you narrow the searching dramatically.
This is how to create a custom assembly level attribute
Upvotes: 2
Reputation: 1
Here is my contribution fellas:
//Register all IJob implementations that are not generic, abstract nor decorators
Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "SomeFilter*.dll")
.Select(file => Assembly.LoadFile(file))
.ForEach(s =>
{
s.GetTypes()
.Where(type => typeof(IJob).IsAssignableFrom(type) && (!type.IsAbstract && !type.IsGenericTypeDefinition))
.Select(type => new { type, ctor = type.GetConstructors().Any(ct => ct.GetParameters().Any(p => p.ParameterType == typeof(IJob))) == false })
.Select(type => type.type)
.ForEach<Type>(o =>
{
string jobFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, string.Format("{0}.xml", Path.GetFileNameWithoutExtension(o.Assembly.Location)));
var typeLoadHelper = new SimpleTypeLoadHelper();
typeLoadHelper.Initialize();
XMLSchedulingDataProcessor processor = new XMLSchedulingDataProcessor(typeLoadHelper);
processor.AddJobGroupToNeverDelete("XMLSchedulingDataProcessorPlugin");
processor.AddTriggerGroupToNeverDelete("XMLSchedulingDataProcessorPlugin");
processor.ProcessFileAndScheduleJobs(jobFile, jobFile, this.Scheduler);
});
});
Upvotes: 0
Reputation: 56859
In Unity, there are a couple of things you need to take care of to get this working:
IEnumerable<T>
.ResolveAll
method explicitly during registration inside of an InjectionConstructor
and ResolvedArrayParameter
.Here is a demo application:
using Microsoft.Practices.Unity;
using System;
using System.Collections.Generic;
using System.Linq;
namespace UnityExperiment
{
class Program
{
static void Main(string[] args)
{
// Begin composition root
var container = new UnityContainer();
container.AddNewExtension<JobContainerExtension>();
container.RegisterType<IService1, Service1>(new InjectionConstructor(
new ResolvedArrayParameter<IJob>(container.ResolveAll<IJob>().ToArray())));
container.RegisterType<IService2, Service2>(new InjectionConstructor(
new ResolvedArrayParameter<IJob>(container.ResolveAll<IJob>().ToArray())));
// End composition root
var service1 = container.Resolve<IService1>();
var service2 = container.Resolve<IService2>();
}
}
public class JobContainerExtension : UnityContainerExtension
{
protected override void Initialize()
{
var interfaceType = typeof(IJob);
var implementationTypes = AppDomain.CurrentDomain.GetAssemblies()
.Where(x => x.FullName.StartsWith("UnityExperiment"))
.SelectMany(x => x.GetTypes())
.Where(x => x.IsClass && interfaceType.IsAssignableFrom(x));
foreach (Type implementationType in implementationTypes)
{
// IMPORTANT: Give each instance a name, or else Unity won't be able
// to resolve the collection.
this.Container.RegisterType(interfaceType, implementationType,
implementationType.Name, new ContainerControlledLifetimeManager());
}
}
}
public interface IJob
{
}
public class Job1 : IJob
{
}
public class Job2 : IJob
{
}
public class Job3 : IJob
{
}
public interface IService1
{
}
public class Service1 : IService1
{
private readonly IJob[] jobs;
public Service1(IJob[] jobs)
{
this.jobs = jobs;
}
}
public interface IService2
{
}
public class Service2 : IService2
{
private readonly IEnumerable<IJob> jobs;
public Service2(IEnumerable<IJob> jobs)
{
this.jobs = jobs;
}
}
}
Upvotes: 1