EF Core 根据 API 中的参数调用数据库

Posted

技术标签:

【中文标题】EF Core 根据 API 中的参数调用数据库【英文标题】:EF Core to call database based on parameter in API 【发布时间】:2021-07-30 09:31:33 【问题描述】:

我有一个在 .NET Core 中使用 EF Core 开发的 API。我必须为多个客户提供不同的数据(但相同的模式)。这是一个学校申请,由于竞争等原因,每所学校都希望单独保存他们的数据。所以我们为每所学校都有一个数据库。现在我的挑战是,基于一些参数,我想更改我的dbContext 对象的连接字符串。

例如,如果我调用 api/students/1 它应该得到所有来自学校 1 的学生,依此类推。我不确定在配置服务本身中是否有更好的方法来做到这一点。但我应该能够从我的客户端应用程序传递SchoolId

public void ConfigureServices(IServiceCollection services)

    services.AddDbContext<SchoolDataContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("APIConnectionString")));
    services.AddScoped<IUnitOfWorkLearn, UnitOfWorkLearn>();

2021 年 5 月 11 日


namespace LearnNew

    public class Startup
    
        public Startup(IConfiguration configuration)
        
            Configuration = configuration;
        

        public IConfiguration Configuration  get; 

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        
            //Comenting to implement Mr Brownes Solution
            //services.AddDbContext<SchoolDataContext>(options =>
            //   options.UseSqlServer(
            //       Configuration.GetConnectionString("APIConnectionString")));



            services.AddScoped<IUnitOfWorkLearn, UnitOfWorkLearn>();


            services.AddControllers();


            services.AddHttpContextAccessor();

            services.AddDbContext<SchoolDataContext>((sp, options) =>
            
                var requestContext = sp.GetRequiredService<HttpContext>();
                var constr = GetConnectionStringFromRequestContext(requestContext);
                options.UseSqlServer(constr, o => o.UseRelationalNulls());

            );

            ConfigureSharedKernelServices(services);

            services.AddSwaggerGen(c =>
            
                c.SwaggerDoc("v1", new OpenApiInfo  Title = "LearnNew", Version = "v1" );
            );
        

        private string GetConnectionStringFromRequestContext(HttpContext requestContext)
        
            //Trying to implement Mr Brownes Solution
            var host = requestContext.Request.Host;
            // Since I don't know how to get the connection string, I want to  
            //debug the host variable and see the possible way to get details of 
            //the host. Below line is temporary until the right method is identified
            return Configuration.GetConnectionString("APIConnectionString");
        

        private void ConfigureSharedKernelServices(IServiceCollection services)
        
            ServiceProvider serviceProvider = services.BuildServiceProvider();
            SchoolDataContext appDbContext = serviceProvider.GetService<SchoolDataContext>();

            services.RegisterSharedKernel(appDbContext);
        

            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        
            if (env.IsDevelopment())
            
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "LearnNew v1"));
            

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            
                endpoints.MapControllers();
            );
        



    

【问题讨论】:

你看过this answer吗? 【参考方案1】:

您可以在配置 DbContext 时访问 HttpContext,如下所示:

        services.AddControllers();

        services.AddHttpContextAccessor();

        services.AddDbContext<SchoolDataContext>((sp, options) =>
        
            var requestContext = sp.GetRequiredService<IHttpContextAccessor>().HttpContext;
            var constr = GetConnectionStringFromRequestContext(requestContext);
            options.UseSqlServer(constr, o => o.UseRelationalNulls());

        );

这段代码:

            var requestContext = sp.GetRequiredService<IHttpContextAccessor>().HttpContext;                var constr = GetConnectionStringFromRequestContext(requestContext);
            options.UseSqlServer(constr, o => o.UseRelationalNulls());

将为每个请求运行,根据来自 HttpRequestContext 的详细信息配置连接字符串。

如果您需要在启动时使用您的 DbContext,请不要通过 DI 解决它。只需像这样配置一个连接:

        var ob  = new DbContextOptionsBuilder<SchoolDataContext>();
        var constr = "...";
        ob.UseSqlServer(constr);
        using (var db = new Db(ob.Options))
        
            db.Database.EnsureCreated();
        

但在生产环境中,您通常会提前创建所有租户数据库。

【讨论】:

大卫您好,感谢您的回复。由于我是新手,如果我的问题太基本,请多多包涵。假设我有两所学校订阅了这个 API,第一所学校将尝试从 schoolapis.azurewebsites.net/api/students/1 访问学生的控制器,第二所学校从 schoolapis.azurewebsites.net/api/students/2 访问。由于我假设 DbContext 处于启动状态,.net 核心将在这两种情况下运行配置服务?由于任何原因,我不想在学校 1 中获得学校 2 的学生名单。 这也意味着我必须将学校 ID 作为参数传递给所有控制器操作,对吗?有没有更好的办法? 对学校 ID 使用 URL 参数可能不是最好的主意。相反,您通常使用用户的身份。身份验证数据、请求标头和 URL 都可以通过 HttpContext 获得。 谢谢你,大卫。我将进行相同的测试并确认答案。再次感谢您的宝贵时间。我从 DbContextFactory 开始,但您的解决方案看起来更直接。 嗨大卫,它给了我以下错误,你能帮帮我吗? System.InvalidOperationException:'没有为类型'Microsoft.AspNetCore.Http.HttpContext'注册服务。'我知道当你从你的水平看我时,我只是一个绝对的初学者,但我相信在你的帮助下我可以让它发挥作用。下面是完整的代码。

以上是关于EF Core 根据 API 中的参数调用数据库的主要内容,如果未能解决你的问题,请参考以下文章