Ninject等效于MEF AssemblyCatalog [英] Ninject equivalent to MEF AssemblyCatalog
问题描述
在MEF中,AssemblyCatalog
用于扫描部件中的所有导出类型并配置容器. Ninject有等效功能吗?
In MEF, an AssemblyCatalog
is used to scan an assembly for all exported types and configure the container. Is there an equivalent with Ninject?
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
var container = new CompositionContainer(catalog);
推荐答案
In Ninject, the ability to use attributes on types and scan assemblies to add bindings is provided by an extension named Ninject.Extensions.Conventions.
-
添加约定包,例如使用Package Manager控制台:
Add the conventions package, e.g. using the Package Manager Console:
Install-Package Ninject.Extensions.Conventions
创建一些自定义属性以用于您的服务.这相当于MEF中的ExportAttribute
:
Create some custom attribute to use on your services. This will be the equivalent to the ExportAttribute
in MEF:
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; }
}
创建一个自定义BindingGenerator ,使用我们的ExportAttribute
将类型绑定到给定的接口:
Create a custom BindingGenerator that uses our ExportAttribute
to bind a type to the given interface:
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();
}
}
}
创建扩展方法以在内核上使用并添加约定:
Create an extension method to use on the kernel and add the convention:
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>());
}
}
现在,对于如此常见的事情,这似乎需要大量工作,但是它非常灵活且可扩展.它可以完成您想要执行的任何操作.将其放在实用程序类库中,并在需要的地方使用它,像这样:
Now this seems like a lot of work for something so common, but it's very flexible and extensible. It does whatever you want it to do. Put it in a utilities class library and use it wherever you want like this:
[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>());
}
}
这篇关于Ninject等效于MEF AssemblyCatalog的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!