Ninject相当于MEF AssemblyCatalog

时间:2014-12-20 08:12:22

标签: c# .net dependency-injection ninject mef

在MEF中,AssemblyCatalog用于扫描所有导出类型的程序集并配置容器。是否与Ninject等效?

var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
var container = new CompositionContainer(catalog);

1 个答案:

答案 0 :(得分:4)

在Ninject中,使用类型和扫描程序集上的属性来添加绑定的功能由名为Ninject.Extensions.Conventions的扩展程序提供。

  1. 添加约定套餐,例如使用程序包管理器控制台:

    Install-Package Ninject.Extensions.Conventions
    
  2. 创建一些自定义属性以用于您的服务。这相当于MEF中的ExportAttribute

    public enum ExportScope
    {
        Singleton,
        Transient
    }
    
    [AttributeUsage(AttributeTargets.Class)]
    public class ExportAttribute : Attribute
    {
        public ExportAttribute(Type serviceInterface, ExportScope scope = ExportScope.Singleton)
        {
            this.ServiceInterface = serviceInterface;
            this.Scope = scope;
        }
    
        public Type ServiceInterface { get; set; }
    
        public ExportScope Scope { get; set; }
    }
    
  3. 创建一个自定义BindingGenerator ,它使用我们的ExportAttribute将类型绑定到给定的接口:

    public class ExportAttributeBindingGenerator : IBindingGenerator
    {
        public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
        {
            var attribute = type.GetCustomAttribute<ExportAttribute>();
            var serviceType = attribute.ServiceInterface;
    
            if (!serviceType.IsAssignableFrom(type))
            {
                throw new Exception(string.Format("Error in ExportAttribute: Cannot bind type '{0}' to type '{1}'.", 
                    serviceType, type));
            }
    
            var binding = bindingRoot.Bind(serviceType).To(type);
    
            switch (attribute.Scope)
            {
                case ExportScope.Singleton:
                    yield return (IBindingWhenInNamedWithOrOnSyntax<object>) binding.InSingletonScope();
                    break;
                case ExportScope.Transient:
                    yield return (IBindingWhenInNamedWithOrOnSyntax<object>) binding.InTransientScope();
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
    }
    
  4. 创建扩展方法以在内核上使用并添加约定:

    public static class NinjectBindingExtensions
    {
        public static void BindExportsInAssembly(this IBindingRoot root, Assembly assembly)
        {
            root.Bind(c => c.From(assembly)
                              .IncludingNonePublicTypes()
                              .SelectAllClasses()
                              .WithAttribute<ExportAttribute>()
                              .BindWith<ExportAttributeBindingGenerator>());
        }
    }
    
  5. 现在这对于一些如此常见的事情来说似乎很多工作,但它非常灵活和可扩展。它做任何你想做的事。将它放在一个实用程序类库中,并在任何你想要的地方使用它:

    [Export(typeof(IFoo))]
    public class Foo : IFoo
    {
    
    }
    
    [Export(typeof(IBar), ExportScope.Transient)]
    public class Bar : IBar
    {
    
    }
    
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestNinjectConventions()
        {
            var kernel = new StandardKernel();
    
            kernel.BindExportsInAssembly(typeof(IFoo).Assembly);
    
            kernel.Get<IFoo>().Should().Be(kernel.Get<IFoo>());
            kernel.Get<IBar>().Should().NotBe(kernel.Get<IBar>());
        }
    }