c# - 在 Autofac 中,当 A 是单个实例并且 B 使用 InstancePerRequest 注册时,如何注册 Owned<B> 关系?

标签 c# autofac autofac-configuration

我使用的是 Autofac 3.5.2 版。

我想注册一个需要 B 的单例类 A,直到将来某个时候。如果 B 是使用 InstancePerRequest 注册的,你该怎么做? ?

使用 Owned<B>直接不起作用,因为每个请求的生命周期范围不存在。你得到 DependencyResolutionException如所见here .

一个解决方案是让 A 直接依赖于 ILifetimeScope . A 以 MatchingScopeLifetimeTags.RequestLifetimeScopeTag 开始范围当它需要 B 的实例时。

    using (var scope = this.lifetimeScope.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag))
    {
        var b = scope.Resolve<B>();
        b.DoSomething();
    }

我不喜欢这种方法,因为它是服务定位器模式。看这个fiddle .

第二种解决方案是将 B 注册为 InstancePerRequest并且在 B 的正确拥有范围内。注册 B 将如下所示:builder.RegisterType<B>().InstancePerRequest(new TypedService(typeof(B))); 完整的例子是 here .

我更喜欢第二种解决方案,但它也有问题:

  1. 当注册 B 的依赖项时,他们必须知道将自己注册到 B 拥有的范围内,这感觉就像代码的味道。

  2. 当 B 有很多依赖项并且这些依赖项有依赖项等时,它不能很好地扩展。所有这些依赖项都需要额外注册,如上面的问题 1。

您建议如何解决这个问题?感谢您的帮助。

更新

我已经移动了OwnedPerRequest<T>相关代码放入this answer下面。

最佳答案

此答案基于 Nicholas' comment 和他关于 IRegistrationSource 的博文

它是基于现有的 Owned<T> class 及其 registration source .

public class OwnedPerRequest<T> : Owned<T>
{
    public OwnedPerRequest(T value, IDisposable lifetime) : base(value, lifetime) { }
}

public class OwnedPerRequestInstanceRegistrationSource : IRegistrationSource
{
    public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
    {
        if (service == null)
            throw new ArgumentNullException(nameof(service));
        if (registrationAccessor == null)
            throw new ArgumentNullException(nameof(registrationAccessor));

        var swt = service as IServiceWithType;
        if (swt == null
             || !(swt.ServiceType.IsGenericType
                    && swt.ServiceType.GetGenericTypeDefinition() == typeof(OwnedPerRequest<>)))
            return Enumerable.Empty<IComponentRegistration>();

        var ownedInstanceType = swt.ServiceType.GetGenericArguments()[0];
        var ownedInstanceService = swt.ChangeType(ownedInstanceType);

        return registrationAccessor(ownedInstanceService)
            .Select(r =>
            {
                var rb = RegistrationBuilder.ForDelegate(swt.ServiceType, (c, p) =>
                {
                    var lifetime = c.Resolve<ILifetimeScope>().BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag);
                    try
                    {
                        var value = lifetime.ResolveComponent(r, p);
                        return Activator.CreateInstance(swt.ServiceType, value, lifetime);
                    }
                    catch
                    {
                        lifetime.Dispose();
                        throw;
                    }
                });

                return rb
                    .ExternallyOwned()
                    .As(service)
                    .Targeting(r)
                    .CreateRegistration();
            });
    }

    public bool IsAdapterForIndividualComponents => true;

    public override string ToString() => "OwnedPerRequestInstanceregistrationSource";
}

关于c# - 在 Autofac 中,当 A 是单个实例并且 B 使用 InstancePerRequest 注册时,如何注册 Owned<B> 关系?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39124154/

相关文章:

c# - 空 gridview 虽然 sqldatasource 有值

structuremap - Autofac vs. Structuremap,如何注入(inject)接口(interface)的所有实例?

c# - 如何在autofac中的解析时间上传递参数

c# - MassTransit 6.0 中的 NLog 与 Autofac?

c# - 使用可选参数对象注册 autofac 组件

c# - 减少不断增加的构造函数服务参数

c# - 动态更改 aspx 页面的文档类型

c# - 如何从匿名列表中删除项目

c# - 代码契约(Contract)静态检查器似乎没有意识到 ReadOnlyCollection<T> 构造函数的 Contract.Ensures

c# - 在 ASP.NET Core 3.0 Preview 5 或更高版本中配置 AutoFac