C# 从Autofac func工厂获取每个请求的依赖项

C# 从Autofac func工厂获取每个请求的依赖项,c#,asp.net-core,autofac,C#,Asp.net Core,Autofac,我正在使用ASP.NET内核和Autofac。几乎所有内容都是按照生命周期范围(“每个请求”)注册的。因此,我的数据库上下文在整个请求中都是相同的实例 但是,我有一个单例,它也依赖于DbContext。为了避免捕获依赖,它被注入为Func,这意味着每次都会有一个新的DbContext实例 问题是我需要与请求过程中其他地方相同的实例,而不是新实例 我希望避免捕获依赖性错误,但我也希望使用相同的实例。通过标记或自定义注册,这是可能的吗?从注释中可以看出,最不“架构”痛苦的方法可能是创建自己的范围类,

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

但是,我有一个单例,它也依赖于
DbContext
。为了避免捕获依赖,它被注入为
Func
,这意味着每次都会有一个新的
DbContext
实例

问题是我需要与请求过程中其他地方相同的实例,而不是新实例


我希望避免捕获依赖性错误,但我也希望使用相同的实例。通过标记或自定义注册,这是可能的吗?

从注释中可以看出,最不“架构”痛苦的方法可能是创建自己的
范围
类,该类将从当前
HttpContext

// 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));
    }
}
//使用接口,因此我们的域中没有基础结构依赖项
公共接口的作用域为T:class
{
T实例{get;}
}
//也注册为单身人士。
公共密封类作用域:T:class的作用域
{
专用只读IHttpContextAccessor上下文访问器;
私有HttpContext HttpContext{get;}=>contextAccessor.HttpContext;
公共T实例{get;}=>HttpContext.RequestServices.GetService();
公共作用域(IHttpContextAccessor contextAccessor)
{
this.contextAccessor=contextAccessor??抛出新的ArgumentNullException(nameof(contextAccessor));
}
}
注册为

// Microsoft.Extensions.DependencyInjection
services.AddSingleton(typeof(IScoped<>), typeof(Scoped<>);
// Autofac
containerBuilder.RegisterType(typeof(Scoped<>))
            .As(typeof(IScoped<>));
//Microsoft.Extensions.DependencyInjection
AddSingleton(typeof(IScoped),typeof(Scoped);
//自动传真
containerBuilder.RegisterType(类型(范围))
.As(类型(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
    }
}
公共类CustomerValidator:AbstractValidator
{
private readonly(只读)是一款可扩展的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);
}
公共IServiceProvider配置服务(IServiceCollection服务)
{
//将服务添加到集合中。
services.AddMvc();
//创建容器生成器。
var builder=new ContainerBuilder();
//注册依赖项,从中填充服务
//集合,并构建容器。如果需要
//要在应用程序结束时处置容器,
//请确保将其作为属性或字段保留引用。
builder.RegisterType().As();
建造商。填充(服务);
this.ApplicationContainer=builder.Build();
//基于容器创建IServiceProvider。
返回新的AutofacServiceProvider(this.ApplicationContainer);
}
Startup
class和
Microsoft.Extensions.DependencyInjection
容器的默认用法有一些细微的区别

  • ConfigureServices
    不再是
    void
    ,它返回
    IServiceProvider
    。这将告诉ASP.NET Core使用返回的提供程序,而不是
    Microsoft.Extensions.DependencyInjection
    中的
    DefaultServiceProvider
  • 我们返回Autofac容器适配器:
    newautofacserviceprovider(this.ApplicationContainer)
    ,它是根容器
  • 这对于使ASP.NET Core在ASP.NET Core中的任何地方使用容器都很重要,甚至在通过
    HttpContext.RequestedServices
    解决每个请求依赖关系的中间件中也是如此

    因此,您不能在Autofac中使用
    .InstancePerRequest()
    生存期,因为Autofac无法控制创建作用域,只有ASP.NET Core可以这样做。因此,要使ASP.NET Core使用Autofac自己的请求生存期,没有简单的方法


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

    您不能重新设计singleton服务以反转依赖关系吗?如果它真的需要DbContext,为什么首先是singleton?@Tseng No,很遗憾,我不能。它是一个FluentValidation验证器,有几十个lambda表达式,需要编译一个nd缓存,仅一次。但并不阻止它被重构。不知道你的单例服务是什么样子,但将fluent验证拆分为它自己的单例服务,然后将其与DbContext一起注入到你使用的范围服务中可能是有意义的。但如果没有更多,很难给你具体的提示information@Tseng这个singleton服务是一个验证程序类,由FluentValidation库注册和使用。在这方面我无能为力。这个特定的验证程序需要在做出决定之前检查数据库中的某些内容,这就是为什么要注入DbContext。但我希望与其他地方的实例相同,而不是一个新实例。嗯,一个该类的具体示例及其用法将很有用,但仍然有“bruteforce”方法可用:注入
    IHttpContextAccessor
    并从那里解析DbContext,您将始终拥有作用域的方法(除了在请求外部调用验证器时,即通过后台运行的任务调用验证器时,您应该在ASP.NET(Core)应用程序中避免这种情况)。但它非常粗暴,将验证器与基础结构(ASP.NET Core/HttpAbstration库)绑定。或者创建一个
    作用域
    类并将其注入(就像你对OwnedFirst Off所做的那样,这真的很聪明,很有效,所以谢谢,至少现在我没有被卡住!但是,这不是Digise中的服务定位器吗?它基本上可以解决任何问题。我希望有一种方法可以进行某种自定义注册。另一方面,也许我可以使用你的解决方案,但是约束
    T
    所以它是
    其中T:DbContext
    所以它只解析一个上下文。是和否。
    IScoped
    类是您的基础结构的一部分(与Autofac相同