从Autofac func工厂获取每请求依赖项

时间:2017-06-16 19:25:28

标签: c# asp.net-core autofac

我正在使用ASP.NET Core和Autofac。几乎所有内容都按生命周期范围注册(“每个请求”)。所以我的数据库上下文DbContext在整个请求中都是相同的实例。

但是我有一个单身人士,也取决于DbContext。为了避免强制依赖,它被注入Func<Owned<DbContext>>,这意味着每次都有一个新的DbContext实例。

问题是我需要相同的实例,就像请求期间的其他地方一样,而不是新实例。

我想避免强制依赖性错误,但我也想要相同的实例。这可以通过标记或自定义注册吗?

1 个答案:

答案 0 :(得分:4)

从评论中,最少“架构”的痛苦方法可能是通过创建自己的Scoped<T>类来解析当前HttpContext

中的DbContext
// Use an interface, so we don't have infrastructure dependencies in our domain
public interface IScoped<T> where T : class
{
    T Instance { get; }
}

// Register as singleton too.
public sealed class Scoped<T> : IScoped<T> where T : class
{
    private readonly IHttpContextAccessor contextAccessor;
    private HttpContext HttpContext { get; } => contextAccessor.HttpContext;

    public T Instance { get; } => HttpContext.RequestServices.GetService<T>();

    public Scoped(IHttpContextAccessor contextAccessor)
    {
        this.contextAccessor = contextAccessor ?? throw new ArgumentNullException(nameof(contextAccessor));
    }
}

将其注册为

// Microsoft.Extensions.DependencyInjection
services.AddSingleton(typeof(IScoped<>), typeof(Scoped<>);
// Autofac
containerBuilder.RegisterType(typeof(Scoped<>))
            .As(typeof(IScoped<>));

然后将此注入您的验证器服务。

public class CustomerValidator: AbstractValidator<Customer>
{
    private readonly IScoped<AppDbContext> scopedContext;
    protected AppDbContext DbContext { get } => scopedContext.Instance;

    public CustomValidator(IScoped<AppDbContext> scopedContext)
    {
        this.scopedContext = scopedContext ?? throw new ArgumentNullException(nameof(scopedContext));

        // Access DbContext via this.DbContext
    }
}

通过这种方式,您可以注入任何范围内的服务而无需进一步注册。

附加说明

Autofac被认为是“conformer”(参见docs)DI,并且与ASP.NET Core和Microsoft.Extensions.DependencyInjection完美集成。

来自文档

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    // Add services to the collection.
    services.AddMvc();

    // Create the container builder.
    var builder = new ContainerBuilder();

    // Register dependencies, populate the services from
    // the collection, and build the container. If you want
    // to dispose of the container at the end of the app,
    // be sure to keep a reference to it as a property or field.
    builder.RegisterType<MyType>().As<IMyType>();
    builder.Populate(services);
    this.ApplicationContainer = builder.Build();

    // Create the IServiceProvider based on the container.
    return new AutofacServiceProvider(this.ApplicationContainer);
}

Startup类和Microsoft.Extensions.DependencyInjection容器的默认用法存在一些细微差别。

  1. ConfigureServices不再是void,它会返回IServiceProvider。这将告诉ASP.NET Core使用返回的提供程序而不是DefaultServiceProvider中的Microsoft.Extensions.DependencyInjection
  2. 我们返回Autofac容器适配器:new AutofacServiceProvider(this.ApplicationContainer)这是根容器。
  3. 这一点非常重要,因为ASP.NET Core可以在ASP.NET Core中的所有位置使用容器,即使是通过HttpContext.RequestedServices解析每个请求依赖项的中间件。

    由于这个原因,您无法在Autofac中使用.InstancePerRequest()生命周期,因为Autofac无法控制创建范围,只有ASP.NET Core可以执行此操作。因此,没有简单的方法可以让ASP.NET Core使用Autofac自己的Request生命周期。

    相反,ASP.NET Core将创建一个新范围(使用IServiceScopeFactory.CreateScope())并使用范围的Autofac容器来解析每个请求的依赖关系。