【问题标题】:How to inject DbContext into repository constructor in ASP.NET core如何将 DbContext 注入 ASP.NET 核心中的存储库构造函数
【发布时间】:2019-05-30 18:58:14
【问题描述】:

我正在编写我的第一个 ASP.NET Core Web API,并试图弄清楚如何将我的 DbContext 对象注入到我的存储库构造函数中。我遵循tutorial 的 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 注入到 Controller 中,然后调用存储库方法将其分配给 UserRepository:

    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 注入到我的存储库的构造函数中,而不是在实例化后在 Controller 构造函数中分配它,如下所示:

    [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.Models.RAFYCContext” 'RAFYC.MobileAppService.Repositories.IUserRepository'

有谁知道这是否可以通过 ASP.NET Core (2.2) 实现?

【问题讨论】:

  • 错误是准确的,因为您试图将作用域依赖注入到单例中,这是不允许的。 DbContexts 作为作用域添加。
  • @Nkosi 我并不是要暗示错误是不正确的,只要有一种方法可以设计我的代码(除了我在帖子中建议的)来实现我的目标后。如果没有,我会用我所拥有的!
  • 您也可以考虑将存储库添加为作用域
  • 还应该在文档docs.microsoft.com/en-us/aspnet/core/fundamentals/… 中快速回顾一下这应该有助于解释问题
  • @NKosi 将其添加为作用域就可以了,感谢您的文档,现在更有意义

标签: c# asp.net-core dependency-injection repository dbcontext


【解决方案1】:

AddDbContext 默认添加DbContext 作为作用域,这会导致单例存储库出现问题。

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

参考Dependency injection in ASP.NET Core: Service lifetimes

如果可能的话,我建议将存储库也添加到范围内。

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

【讨论】:

  • 这是一个正确的答案。如果您将存储库注册为单例,您将看到 EF 异常:A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe. 这是因为您只创建一次,因此您将为所有请求重用 db 上下文(它们可能是并发的)。
  • 将存储库添加为作用域可防止在其构造函数中预加载某些内容(例如,调用 EF.CompileQuery),或执行查询以加载在应用程序生命周期内为静态但重新加载的数据- 用于多个后续查询(重建对象而不调用 Include()),就像我们之前对 Singleton 存储库所做的那样。你将如何解决这个问题?
猜你喜欢
  • 2019-03-29
  • 1970-01-01
  • 1970-01-01
  • 2015-10-28
  • 1970-01-01
  • 2021-05-03
  • 2020-12-27
  • 2020-07-28
  • 1970-01-01
相关资源
最近更新 更多