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 中的参数调用数据库的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用 EF 的情况下连接到 ASP.NET Core Web API 中的数据库?

带有 EF Core 更新实体的 ASP.Net 核心 Web Api 如何

EF CORE 调用 Oracle11g 的输出参数上的“数字或值错误”

EF Core中通过Fluent API配置多对多关系

EF Core中通过Fluent API配置多对多关系

EF Core中通过Fluent API配置多对多关系