获取在Unity中实现接口的所有类型

时间:2015-02-13 21:48:03

标签: c# dependency-injection unity-container

如果您想了解解决方案,请跳至更新:

我有一个应用程序,它使用以下代码来获取和运行许多工作方法

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();
}

此代码完美无缺。但是,一些较新的作业利用依赖注入来填充其构造函数,因此这种方法将无法继续使用。所以我想知道是否有办法以统一的方式做到这一点?

我最初的想法是,我将继续使用上半部分,然后使用resolve替换foreach逻辑,使其看起来像以下内容。

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();
}

问题在于,一旦我定义了UnityContainer,实现IJob的返回类型列表突然变得臃肿,所有这些垃圾Microsoft.Practices类如下所示

enter image description here

更新

事实证明,如果在Unity存在的情况下重新选择Assemblies,它将尝试反映到Unity的程序集中,如果由于缺少IServiceLocator的元数据扩展而使用ToList终结将引发异常。要解决此问题,在GetAssemblies()之后附加一个where子句以将范围限制到所需的命名空间将允许应用程序正常运行。

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();
}

3 个答案:

答案 0 :(得分:2)

不是搜索 所有 程序集,而是通过自定义属性对其进行过滤。这样就可以大大缩小搜索范围。

这是创建自定义装配级别属性的方法

Custom Assembly Attributes

答案 1 :(得分:1)

在Unity中,您需要注意以下几项工作:

  1. 您需要使用其他名称注册每个实例。未命名的实例无法解析为数组或IEnumerable<T>
  2. 您必须在ResolveAllInjectionConstructor内注册时明确调用ResolvedArrayParameter方法。
  3. 这是一个演示应用程序:

    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;
            }
        }
    }
    

答案 2 :(得分:0)

这是我的贡献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);
        });
});