如何在ASP.NET Core中将DbContext注入到存储库构造函数中

5
我正在编写我的第一个ASP.NET Core Web API,并试图弄清楚如何将我的DbContext对象注入到我的repository构造函数中。我按照这个教程中的EF部分进行操作,其中DbContext通过services.AddDbContext<DbContext>被注册到服务集合中,services.GetRequiredService<DbContext>()用于初始化数据库值。
    public class Startup
    {

        public IConfiguration Configuration { 

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
            services.AddSingleton<IItemRepository, ItemRepository>();
            services.AddSingleton<IUserRepository, UserRepository>();

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
            });

            services.AddDbContext<RAFYCContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

        }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            //CreateWebHostBuilder(args).Build().Run();

            IWebHost host = CreateWebHostBuilder(args).Build();

            using (IServiceScope scope = host.Services.CreateScope())
            {
                IServiceProvider services = scope.ServiceProvider;
                try
                {
                    RAFYCContext context = services.GetRequiredService<RAFYCContext>();
                    DbInitializer.Initialize(context);
                }
                catch (Exception ex)
                {
                    ILogger logger = services.GetRequiredService<ILogger<Program>>();
                    logger.LogError(ex, "An error occurred while seeding the database.");
                }
            }

            host.Run();
        }
    }

我能够将DbContext注入控制器并调用存储库方法将其分配给UserRepository。请注意保留HTML标记。
    public class UserController : ControllerBase
    {
        IUserRepository _userRepo;

        public UserController(IUserRepository userRepo, RAFYCContext context)
        {
            _userRepo = userRepo;
            _userRepo.SetDbContext(context);
        }
    }

    public class UserRepository : IUserRepository
    {
        public RAFYCContext _context;

        public UserRepository() { }

        public void SetDbContext(RAFYCContext context)
        {
            this._context = context;
        }
    }

这个方法可以正常工作,但是我想将DbContext注入到我的仓库构造函数中,而不是在实例化后在控制器构造函数中进行分配,如下所示:

    [Route("api/[controller]")]
    [ApiController]
    public class UserController : ControllerBase
    {
        IUserRepository _userRepo;

        public UserController(IUserRepository userRepo)
        {
            _userRepo = userRepo;
        }
    }

    public class UserRepository : IUserRepository
    {
        RAFYCContext _context;

        public UserRepository(RAFYCContext context)
        {
            _context = context;
        }
    }

使用该代码会出现以下错误:

InvalidOperationException: 无法从单例“RAFYC.MobileAppService.Repositories.IUserRepository”中消费作用域服务 'RAFYC.MobileAppService.Models.RAFYCContext'

请问在ASP.NET Core(2.2)中是否可能实现此操作?


错误是准确的,因为您正在尝试将作用域依赖项注入到单例中,这是不允许的。DbContexts被添加为作用域。 - Nkosi
@Nkosi 我并不是在暗示错误是不正确的,只是想知道是否有其他方法可以设计我的代码来实现我想要的目标(除了我在帖子中提出的那个方法)。如果没有,我会接受现状! - GDiff94
你可以考虑将存储库也作为范围添加。 - Nkosi
1
还应该快速查看文档中的内容 https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2#service-lifetimes,这将有助于解释问题。 - Nkosi
@NKosi 将其添加为作用域解决了问题,感谢提供文档,现在更有意义了。 - GDiff94
1个回答

6
AddDbContext 添加的 DbContext 默认是 scoped 的,这会导致单例仓储出现问题。

从单例中解析作用域服务是有风险的。这可能导致在处理后续请求时服务状态不正确。

参考文献:ASP.NET Core 中的依赖注入:服务生存期

如果可能的话,我建议将仓储也添加为 scoped。

services.AddScoped<IItemRepository, ItemRepository>();
services.AddScoped<IUserRepository, UserRepository>();

这是一个正确的答案。如果将您的存储库注册为单例,则会看到EF异常:在上一个异步操作完成之前,在此上下文上启动了第二个操作。使用“await”以确保在调用此上下文上的另一个方法之前已完成所有异步操作。任何实例成员都不能保证线程安全。这是因为您只创建它一次,因此您将重复使用db上下文来处理所有请求(它们可能是并发的)。 - Vočko
将存储库作为范围添加可以防止在其构造函数中预加载某些内容(例如,调用EF.CompileQuery),或者执行查询以加载应用程序生命周期内静态但在多个后续查询中重复使用的数据(重新构建对象而不调用Include()),这与我们以前使用单例存储库所做的方式不同。你会如何解决这个问题? - Dunge

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接