组件概念
组件可以通过反射创建;通过提供现成的实例;或通过lambada表达式。
反射组件
反射生成的组件通常安装类型注册
var builder = new ContainerBuilder(); builder.RegisterType<ConsoleLogger>(); builder.RegisterType(typeof(ConfigReader));
使用基于反射的组件时,Autofac会自动使用类的构造函数,其中包含的参数最对,可以从容器中获取。
例如假设您有一个包含三个构造函数的类,如
public class MyComponent
{
public MyComponent() { /* ... */ }
public MyComponent(ILogger logger) { /* ... */ }
public MyComponent(ILogger logger, IConfigReader reader) { /* ... */ }
}
现在在容器中注册组件和服务,如下:
var builder = new ContainerBuilder();
builder.RegisterType<MyComponent>();
builder.RegisterType<ConsoleLogger>().As<ILogger>();
var container = builder.Build();
using(var scope = container.BeginLifetimeScope())
{
var component = scope.Resolve<MyComponent>();
}
Autofac将看到您已经ILogger注册,但您没有IConfigReader注册。在这种情况,将选择第二个构造函数,因为那是可以在容器中找到的参数最多的构造函数。
指定构造函数
基于反射的组件注意事项:组件类型RegisterType必须是具体类型。
指定构造函数,可以使用UsingConstructor方法注册组件以及在构造函数中表示参数类型的类型来覆盖自动选择。
builder.RegisterType<MyComponent>()
.UsingConstructor(typeof(ILogger), typeof(IConfigReader));
注意:您在解决时使用必须参数,否则在尝试解析对象时会出现错误。
实例组件
在某些情况下,您可能希望预先生成对象的实例并将其添加到容器中以供注册的组件使用。您可以使用以下RegisterInstance方法执行此操作:
var output = new StringWriter(); builder.RegisterInstance(output).As<TextWriter>();
执行此操作时需要考虑的事项是Autofac会自动处理已注册组件的处理,您可能希望自己控制生命周期,而不是为Dispose您的对象调用Autofac 。在这种情况下,您需要使用以下ExternallyOwned方法注册实例:
var output = new StringWriter();
builder.RegisterInstance(output)
.As<TextWriter>()
.ExternallyOwned();
Lambda表达式组件
反射是组件创建的一个非常好的默认选择。但是,当组件创建逻辑超出简单的构造函数调用时,事情会变得混乱。
Autofac可以接受委托或lambda表达式用作组件创建者:
builder.Register(c => new A(c.Resolve<B>()));
c提供给表达式的参数是正在创建组件的组件上下文(IComponentContext对象)。您可以使用它来解析容器中的其他值,以帮助创建组件。使用它而不是闭包来访问容器非常重要,这样才能正确地支持确定性处理和嵌套容器。
使用此上下文参数可以满足其他依赖项 - 在此示例中,A需要B可能具有其他依赖项的类型的构造函数参数。
表达式创建的组件提供的默认服务是表达式的推断返回类型。
下面是一些需求通过反射组件创建遇到的问题,但lambda表达式很好地解决了这些问题。
复杂参数
无法始终使用简单常量值声明构造函数参数。不要使用XML配置语法来解释如何构造某种类型的值,而是使用代码:
builder.Register(c => new UserSession(DateTime.Now.AddMinutes(25)));
属性注入
虽然Autofac提供了更为一流的属性注入方法,但您也可以使用表达式和属性初始值设定项来填充属性:
builder.Register(c => new A(){ MyB = c.ResolveOptional<B>() });
该ResolveOptional方法将尝试解析该值,但如果未注册该服务,则不会抛出异常。(如果服务已注册但无法正确解析,您仍会收到异常。)这是解析服务的选项之一。
在大多数情况下不推荐使用注射物。诸如Null对象模式,重载构造函数或构造函数参数默认值之类的替代方案使得可以使用构造函数注入创建具有可选依赖性的更清晰的“不可变”组件。
通过参数值选择实现
隔离组件创建的一大好处是可以改变具体类型。这通常在运行时完成,而不仅仅是配置时间:
builder.Register<CreditCard>(
(c, p) =>
{
var accountId = p.Named<string>("accountId");
if (accountId.StartsWith("9"))
{
return new GoldCard(accountId);
}
else
{
return new StandardCard(accountId);
}
});
在此示例中,CreditCard由两个类实现,GoldCard并且StandardCard- 实例化哪个类取决于在运行时提供的帐户ID。
通过p此示例中指定的可选第二个参数将参数提供给创建函数。
使用此注册看起来像:
var card = container.Resolve<CreditCard>(new NamedParameter("accountId", "12345"));
如果CreditCard声明了创建实例的委托并使用了委托工厂,则可以实现更清晰,类型安全的语法。
打开通用组件
Autofac支持开放泛型类型。使用RegisterGeneric()构建器方法:
builder.RegisterGeneric(typeof(NHibernateRepository<>))
.As(typeof(IRepository<>))
.InstancePerLifetimeScope();
当从容器请求匹配的服务类型时,Autofac会将其映射到实现类型的等效封闭版本:
// Autofac will return an NHibernateRepository<Task> var tasks = container.Resolve<IRepository<Task>>();
服务与组件
注册组件时,必须告诉Autofac该组件公开哪些服务。默认情况下,大多数注册只会将自己暴露为注册的类型:
// This exposes the service "CallLogger" builder.RegisterType<CallLogger>();
组件只能通过它们公开的服务来解析。在这个简单的例子中它意味着:
// This will work because the component // exposes the type by default: scope.Resolve<CallLogger>(); // This will NOT work because we didn't // tell the registration to also expose // the ILogger interface on CallLogger: scope.Resolve<ILogger>();
您可以使用您喜欢的任意数量的服务公开组件:
builder.RegisterType<CallLogger>()
.As<ILogger>()
.As<ICallInterceptor>();
公开服务后,您可以根据该服务解析组件。但请注意,一旦将组件公开为特定服务,将覆盖默认服务(组件类型):
// These will both work because we exposed // the appropriate services in the registration: scope.Resolve<ILogger>(); scope.Resolve<ICallInterceptor>(); // This WON'T WORK anymore because we specified // service overrides on the component: scope.Resolve<CallLogger>();
如果要将组件公开为一组服务以及使用默认服务,请使用以下AsSelf方法:
builder.RegisterType<CallLogger>()
.AsSelf()
.As<ILogger>()
.As<ICallInterceptor>();
现在所有这些都可行:
// These will all work because we exposed // the appropriate services in the registration: scope.Resolve<ILogger>(); scope.Resolve<ICallInterceptor>(); scope.Resolve<CallLogger>();
如果多个组件公开同一服务,Autofac将使用最后注册的组件作为该服务的默认提供者:
builder.RegisterType<ConsoleLogger>().As<ILogger>(); builder.RegisterType<FileLogger>().As<ILogger>();
在这种情况下,FileLogger将是默认值,ILogger因为它是最后一个注册的。
要覆盖此行为,请使用PreserveExistingDefaults()修饰符:
builder.RegisterType<ConsoleLogger>().As<ILogger>(); builder.RegisterType<FileLogger>().As<ILogger>().PreserveExistingDefaults();
在这种情况下,ConsoleLogger将默认为ILogger因为后来注册FileLogger使用PreserveExistingDefaults()。
条件注册4.4.0
在大多数情况下,如上一节“默认注册”中所述,覆盖注册就足以在运行时解析正确的组件。确保事情按正确的顺序登记; 使用PreserveExistingDefaults(); 并利用lambda / delegate注册来处理更复杂的条件和行为可以让你走得很远。
可能有一些情况可能不是您想要的方式:
- 如果其他人正在处理该功能,您不希望系统中存在该组件。例如,如果您解析某项
IEnumerable<T>服务,则无论您是否使用过,都将返回实现该服务的所有已注册组件PreserveExistingDefaults()。通常这很好,但有一些边缘情况,你可能不希望这样。 - 如果某个其他组件未注册,您只想注册该组件; 或者只有一些其他部件被注册。您无法解决正在构建的容器中的问题,也不应更新已构建的容器。能够基于其他注册有条件地注册组件可能是有帮助的。
在这些情况下,有两种注册扩展可以提供帮助:
OnlyIf()- 提供一个使用a的lambdaIComponentRegistry来确定是否应该进行注册。IfNotRegistered()- 如果已注册某些其他服务,则停止注册的快捷方式。
这些扩展在运行时运行,并将ContainerBuilder.Build()按实际组件注册的顺序执行。以下是一些显示它们如何工作的示例:
var builder = new ContainerBuilder();
// Only ServiceA will be registered.
// Note the IfNotRegistered takes the SERVICE TYPE to
// check for (the As<T>), NOT the COMPONENT TYPE
// (the RegisterType<T>).
builder.RegisterType<ServiceA>()
.As<IService>();
builder.RegisterType<ServiceB>()
.As<IService>()
.IfNotRegistered(typeof(IService));
// HandlerA WILL be registered - it's running
// BEFORE HandlerB has a chance to be registered
// so the IfNotRegistered check won't find it.
//
// HandlerC will NOT be registered because it
// runs AFTER HandlerB. Note it can check for
// the type "HandlerB" because HandlerB registered
// AsSelf() not just As<IHandler>(). Again,
// IfNotRegistered can only check for "As"
// types.
builder.RegisterType<HandlerA>()
.AsSelf()
.As<IHandler>()
.IfNotRegistered(typeof(HandlerB));
builder.RegisterType<HandlerB>()
.AsSelf()
.As<IHandler>();
builder.RegisterType<HandlerC>()
.AsSelf()
.As<IHandler>()
.IfNotRegistered(typeof(HandlerB));
// Manager will be registered because both an IService
// and HandlerB are registered. The OnlyIf predicate
// can allow a lot more flexibility.
builder.RegisterType<Manager>()
.As<IManager>()
.OnlyIf(reg =>
reg.IsRegistered(new TypedService(typeof(IService))) &&
reg.IsRegistered(new TypedService(typeof(HandlerB))));
// This is when the conditionals actually run. Again,
// they run in the order the registrations were added
// to the ContainerBuilder.
var container = builder.Build();
将参数传递给寄存器
Autofac提供了名称、类型、灵活参数匹配
* 带反射的参数
public class ConfigReader : IConfigReader
{
public ConfigReader(string configSectionName)
{
// Store config section name
}
// ...read configuration based on the section name.
}
//利用表达式注册
builder.Register(c => new ConfigReader("sectionName")).As<IConfigReader>();
//将参数传递给反射组件注册
// Using a NAMED parameter:
builder.RegisterType<ConfigReader>()
.As<IConfigReader>()
.WithParameter("configSectionName", "sectionName");
// Using a TYPED parameter:
builder.RegisterType<ConfigReader>()
.As<IConfigReader>()
.WithParameter(new TypedParameter(typeof(string), "sectionName"));
// Using a RESOLVED parameter:
builder.RegisterType<ConfigReader>()
.As<IConfigReader>()
.WithParameter(
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(string) && pi.Name == "configSectionName",
(pi, ctx) => "sectionName"));
* Lambda表达式的参数
// Use TWO parameters to the registration delegate:
// c = The current IComponentContext to dynamically resolve dependencies
// p = An IEnumerable<Parameter> with the incoming parameter set
builder.Register((c, p) =>
new ConfigReader(p.Named<string>("configSectionName")))
.As<IConfigReader>();
var reader = scope.Resolve<IConfigReader>(new NamedParameter("configSectionName", "sectionName"));
1、公共
public delegate ContainerBuilder AutofacDele(ContainerBuilder c);
public class AutofacExtend
{
private static IContainer _container { get; set; }
/// <summary>
/// 注册事件,默认只运行mvc和api的
/// </summary>
public static event AutofacDele AutofacEvent;
public static void InitAutofac()
{
var builder = new ContainerBuilder();
if(AutofacEvent != null)
builder = AutofacEvent(builder);
// MVC注册controller,使用属性注入
builder.RegisterControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();
// API
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
_container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(_container)); //mvc
}
public static T GetFromFac<T>()
{
return _container.Resolve<T>();
}
}
2、根据不同项目注入
public class AutofacExt
{
/// <summary>
/// 应用启动时依赖注入
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
public static ContainerBuilder AutofacFun(ContainerBuilder builder)
{
//注册数据库基础操作和工作单元 泛型注册
builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IRepository<>));
builder.RegisterType(typeof(UnitWork)).As(typeof(IUnitWork));
builder.RegisterType(typeof(AuthoriseFactory));
//注册app层 组装扫描注册
builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(ClassifyManagerApp)));
builder.RegisterType(typeof(ClassifyManagerApp));
//注册Repository
builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(UsersRepository)))
.Where(t => t.Name.EndsWith("Repository"))
//.Except<BlogsRepository>() //排除
.AsImplementedInterfaces();//注册公共所有的接口
return builder;
}
/// <summary>
/// 初始化
/// </summary>
public static void InitAutofac()
{
AutofacExtend.AutofacEvent += AutofacFun;
AutofacExtend.InitAutofac();
}
}
3、在Global类中Application_Start或测试类中(在项目执行前)添加
AutofacExt.InitAutofac();
4、GetFromFac方法的只用


