项目
版本

注册源

一个 注册源 是一种动态向 Autofac 组件上下文(如容器或生命周期范围)添加注册的方式。

注册源是通过实现 IRegistrationSource 接口创建的。许多 Autofac 功能都是通过这种方式实现的——例如,隐式关系类型 是通过注册源添加的。(你不会真的认为我们手动为每个单例类型都向容器中注册了它们吧?)Nick Blumhardt 有一篇关于这如何工作的很棒的博客文章。

当您没有可以添加到容器中的有限数量的注册时,注册源非常有用。很多时候,程序集扫描 或者 模块使用 可以解决动态注册问题……但当其他方法都失败或者这些方式无法满足您的需求时,可能需要使用注册源。

任何未注册的具体类型源

AnyConcreteTypeNotAlreadyRegisteredSource,我们称之为 ACTNARS,是随附于 Autofac 的一个示例注册源,它允许您根据是否已明确注册,从 Autofac 容器中解析任何具象类型。从其他控制反转容器迁移到 Autofac 的人可能会习惯这种行为,因此 ACTNARS 是一种弥合这种差距的方法。

您可以使用 Autofac.Features.ResolveAnything.AnyConcreteTypeNotAlreadyRegisteredSource,将其添加到您的容器中。

var builder = new ContainerBuilder();
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
var container = builder.Build();

可逆注册源

ContravariantRegistrationSource 对于注册需要在 可逆上下文中(使用比原始指定更通用/不那么衍生的类型)后来解析的类型很有帮助。

如果使用此源,请先进行注册。 正如标准注册一样,注册源按注册顺序进行评估。如果您首先注册一个开放泛型(它也是内部的注册源),然后注册 ContravariantRegistrationSource,则不会得到预期的结果。

在处理器模式中使用 ContravariantRegistrationSource 很常见:

public interface IHandler<in TCommand>
{
  void Handle(TCommand command);
}

public class BaseCommandHandler : IHandler<BaseCommand>
{
  public void Handle(BaseCommand command)
  {
     Console.WriteLine(command.GetType().Name);
  }
}

public class BaseCommand
{
}

public class DerivedCommand : BaseCommand
{
}

var builder = new ContainerBuilder();

// 在注册其他内容之前先注册此内容!
builder.RegisterSource(new ContravariantRegistrationSource());
builder.RegisterType<BaseCommandHandler>().As<IHandler<BaseCommand>>();

var container = builder.Build();

// a 和 b 都是 BaseCommandHandler。
var a = container.Resolve<IHandler<BaseCommand>>();
a.Handle(new BaseCommand()); // 打印 BaseCommand
var b = container.Resolve<IHandler<DerivedCommand>>();
b.Handle(new DerivedCommand()); // 打印 DerivedCommand

实现注册源

通过一个简单的示例来展示如何实现注册源是最好的方法。

假设您有一个工厂,负责生成某种事件处理器类。您需要通过工厂而不是通过 Autofac 生成它们,因此处理器本身不会与 Autofac 注册。同时,也没有好的方式说“当有人请求任何事件处理器时,通过这个特殊工厂生成它”。这是一个很好的例子,说明何时可以使用注册源。

对于示例,让我们定义一个简单的事件处理器基/抽象类和几个实现。

public abstract class BaseHandler
{
  public virtual string Handle(string message)
  {
    return "Handled: " + message;
  }
}

public class HandlerA : BaseHandler
{
  public override string Handle(string message)
  {
    return "[A] " + base.Handle(message);
  }
}

public class HandlerB : BaseHandler
{
  public override string Handle(string message)
  {
    return "[B] " + base.Handle(message);
  }
}

现在,让我们创建一个工厂接口和实现。

public interface IHandlerFactory
{
  T GetHandler<T>() where T : BaseHandler;
}

public class HandlerFactory : IHandlerFactory
{
  public T GetHandler<T>() where T : BaseHandler
  {
    return (T)Activator.CreateInstance(typeof(T));
  }
}

最后,让我们创建一些使用处理器的消费者类。

public class ConsumerA
{
  private HandlerA _handler;
  public ConsumerA(HandlerA handler)
  {
    this._handler = handler;
  }

  public void DoWork()
  {
    Console.WriteLine(this._handler.Handle("ConsumerA"));
  }
}


public class ConsumerB
{
  private HandlerB _handler;
  public ConsumerB(HandlerB handler)
  {
    this._handler = handler;
  }

  public void DoWork()
  {
    Console.WriteLine(this._handler.Handle("ConsumerB"));
  }
}

现在我们有了服务和消费者,让我们创建一个注册源。在示例源中,我们将……

  1. 检查是否正在请求 BaseHandler 类型。如果不是,则源不会提供任何注册来满足解析请求。
  2. 为请求的具体 BaseHandler 子类构建动态注册,这将包括调用提供者/工厂获取实例的 lambda。
  3. 将动态注册返回给解析操作,以便它完成工作。

这是注册源的代码。

using Autofac;
using Autofac.Core;
using Autofac.Core.Activators.Delegate;
using Autofac.Core.Lifetime;
using Autofac.Core.Registration;

public class HandlerRegistrationSource : IRegistrationSource
{
  public IEnumerable<IComponentRegistration> RegistrationsFor(
    Service service,
    Func<Service, IEnumerable<ServiceRegistration>> registrationAccessor)
  {
    var swt = service as IServiceWithType;
    if(swt == null || !typeof(BaseHandler).IsAssignableFrom(swt.ServiceType))
    {
      // 如果不是对基类处理器类型的请求,则跳过。
      return Enumerable.Empty<IComponentRegistration>();
    }

    // 这就是神奇的地方!
    var registration = new ComponentRegistration(
      Guid.NewGuid(),
      new DelegateActivator(swt.ServiceType, (c, p) =>
        {
          // 在此示例中,假设工厂本身已注册到 Autofac,因此我们可以解析工厂。如果您想在此处硬编码工厂,也可以这样做。
          var provider = c.Resolve<IHandlerFactory>();

          // 我们的工厂接口是泛型的,所以我们需要使用反射来调用方法。
          var method = provider.GetType().GetMethod("GetHandler").MakeGenericMethod(swt.ServiceType);

          // 最终,从工厂返回对象。
          return method.Invoke(provider, null);
        }),
      new CurrentScopeLifetime(),
      InstanceSharing.None,
      InstanceOwnership.OwnedByLifetimeScope,
      new [] { service },
      new Dictionary<string, object>());

    return new IComponentRegistration[] { registration };
  }

  public bool IsAdapterForIndividualComponents { get{ return false; } }
}

最后一步是将所有内容注册到 Autofac——注册源、工厂和消费者类。但是请注意,我们不必注册实际的处理器,因为注册源会处理这些。

var builder = new ContainerBuilder();
builder.RegisterType<HandlerFactory>().As<IHandlerFactory>();
builder.RegisterSource(new HandlerRegistrationSource());
builder.RegisterType<ConsumerA>();
builder.RegisterType<ConsumerB>();
var container = builder.Build();

现在,当您解析处理器消费者之一时,您将获得正确的处理器。

using(var scope = container.BeginLifetimeScope())
{
  var consumer = scope.Resolve<ConsumerA>();

  // 调用此操作将在控制台上输出:
  // [A] Handled: ConsumerA
  consumer.DoWork();
}
在本文档中