我想在控制器中更改sql连接字符串,而不是在ApplicationDbContext中。我使用的是Asp.Net Core和Entity Framework Core。
例如:
public class MyController : Controller {
private readonly ApplicationDbContext _dbContext
public MyController(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
private void ChangeConnectionString()
{
// So, what should be here?
} }
我该怎么做?
答案 0 :(得分:10)
我可以通过将连接字符串逻辑移动到DbContext的OnConfiguring
方法来更改每个请求的连接字符串。
在Startup.cs#ConfigureServices
方法中:
services.AddDbContext<MyDbContext>();
在MyDbContext.cs中,我添加了我需要注入构造函数的服务。
private IConfigurationRoot _config;
private HttpContext _httpContext;
public MyDbContext(DbContextOptions options, IConfigurationRoot config, IHttpContextAccessor httpContextAccessor)
: base(options)
{
_config = config;
_httpContext = httpContextAccessor.HttpContext;
}
然后覆盖OnConfiguring:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connString = BuildConnectionString(); // Your connection string logic here
optionsBuilder.UseSqlServer(connString);
}
答案 1 :(得分:8)
如果您想根据活动的http请求参数为每个http请求选择一个连接字符串,这就足够了。
using Microsoft.AspNetCore.Http;
//..
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddDbContext<ERPContext>((serviceProvider, options) =>
{
var httpContext = serviceProvider.GetService<IHttpContextAccessor>().HttpContext;
var httpRequest = httpContext.Request;
var connection = GetConnection(httpRequest);
options.UseSqlServer(connection);
});
您无法轻易访问用户,也无法获得他的声明。除非你手动完成。
答案 2 :(得分:7)
我们有一个类似于你的案例。我们所做的是使用 Startup 的 ConfigureServices 方法中 IServiceCollection 的 implementationfactory 重载上课,像这样:
//First register a custom made db context provider
services.AddTransient<ApplicationDbContextFactory>();
//Then use implementation factory to get the one you need
services.AddTransient(provider => provider.GetService<ApplicationDbContextFactory>().CreateApplicationDbContext());
现在我很难为你实现CreateApplicationDbContext,因为它完全取决于你想要什么。但是,一旦你认为你要完全按照自己的意愿去做,那么该方法的基础知识应该是这样的:
public ApplicationDbContext CreateApplicationDbContext(){
//TODO Something clever to create correct ApplicationDbContext with ConnectionString you need.
}
实现此功能后,您可以像在构造函数中一样在控制器中注入正确的ApplicationDbContext:
public MyController(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
或控制器中的操作方法:
public IActionResult([FromServices] ApplicationDbContext dbContext){
}
然而,您实现了详细信息,诀窍是实现工厂将在每次注入时构建ApplicationDbContext。
告诉我您是否需要更多帮助来实施此解决方案。
更新#1 Yuriy N.询问了AddTransient和AddDbContext之间的区别,这是一个有效的问题......而且它不是。让我解释一下。
这与原始问题无关。
但是......话虽如此,实施自己的实施工厂&#39; (关于我的答案,这是最重要的注意事项)在这种情况下,实体框架可能比我们需要的更棘手。
但是,有了这些问题,我们现在可以幸运地查看GitHub中的源代码,所以我查看了AddDbContext的确切内容。好吧......这并不难。这些&#39;添加&#39; (和&#39;使用&#39;)扩展方法只不过是方便的方法,请记住。因此,您需要添加AddDbContext所做的所有服务以及选项。也许你甚至可以重用AddDbContext扩展方法,只需用实现工厂添加你自己的重载。
所以,回到你的问题。 AddDbContext执行一些EF特定的东西。正如您所看到的,它们将允许您在以后的版本中使用一生(瞬态,单例)。 AddTransient是Asp.Net Core,允许您添加所需的任何服务。而且你需要一个实施工厂。
这是否更清晰?
答案 3 :(得分:2)
那对我有用:
public void ConfigureServices(IServiceCollection services)
{
// .....
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<School360DbContext>(provider =>
{
return ResolveDbContext(provider, hostingEnv);
});
// ..
}
private MyDbContext ResolveDbContext(IServiceProvider provider, IHostingEnvironment hostingEnv)
{
string connectionString = Configuration.GetConnectionString("DefaultConnection");
string SOME_DB_IDENTIFYER = httpContextAccessor.HttpContext.User.Claims
.Where(c => c.Type == "[SOME_DB_IDENTIFYER]").Select(c => c.Value).FirstOrDefault();
if (!string.IsNullOrWhiteSpace(SOME_DB_IDENTIFYER))
{
connectionString = connectionString.Replace("[DB_NAME]", $"{SOME_DB_IDENTIFYER}Db");
}
var dbContext = new DefaultDbContextFactory().CreateDbContext(connectionString);
// ....
return dbContext;
}
答案 4 :(得分:2)
@ginalx和@jcmordan的答案非常适合我的用例。我喜欢这些答案的地方是,我可以在Startup.cs
中完成所有操作,并保持所有其他类的构造代码清洁。我想为Web Api请求提供一个可选的querystring参数,并将其替换为创建DbContext的基本连接字符串。我将基本字符串保留在appsettings.json中,并根据传入的参数或如果未提供默认值的默认值对其进行格式化,即:
"IbmDb2Formatted": "DATABASE={0};SERVER=servername;UID=userId;PWD=password"
对我来说,最终ConfigureServices
方法看起来像(观察。我正在连接到DB2而不是SQL,但这是偶然的):
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();
services.AddDbContext<Db2Context>(((serviceProvider, options) =>
{
var httpContext = serviceProvider.GetService<IHttpContextAccessor>().HttpContext;
var httpRequest = httpContext.Request;
// Get the 'database' querystring parameter from the request (if supplied - default is empty).
// TODO: Swap this out for an enum.
var databaseQuerystringParameter = httpRequest.Query["database"].ToString();
// Get the base, formatted connection string with the 'DATABASE' paramter missing.
var db2ConnectionString = Configuration.GetConnectionString("IbmDb2Formatted");
if (!databaseQuerystringParameter.IsNullOrEmpty())
{
// We have a 'database' param, stick it in.
db2ConnectionString = string.Format(db2ConnectionString, databaseQuerystringParameter);
}
else
{
// We havent been given a 'database' param, use the default.
var db2DefaultDatabaseValue = Configuration.GetConnectionString("IbmDb2DefaultDatabaseValue");
db2ConnectionString = string.Format(db2ConnectionString, db2DefaultDatabaseValue);
}
// Build the EF DbContext using the built conn string.
options.UseDb2(db2ConnectionString, p => p.SetServerInfo(IBMDBServerType.OS390));
}));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info
{
Title = "DB2 API",
Version = "v1"
});
});
}
答案 5 :(得分:1)
虽然晚了,但是EF Core中最简单的技巧是使用nuget Microsoft.EntityFrameworkCore.Relational:
_dbContext.Database.GetDbConnection().ConnectionString = "NEW_CONN_STRING";
当由于任何原因在您的应用程序配置/设置中不存在连接字符串,或者您想使用一个DbContext实例(同样由于任何原因)处理具有相同结构的多个数据库时,这很有用。
永久或临时取决于您为DbContext选择的注入生命周期的类型。如果您将其作为 Singleton 服务注入,则将是永久,建议不。
答案 6 :(得分:1)
我寻求这种解决方案:
代替
services.AddScoped<IMyDbContext, MyDbContext>();
我去了
services.AddTransient<IMyDbContext, MyDbContext>(resolver =>
{
var context= resolver.GetService<MyDbContext>();
var config = resolver.GetService<IConfiguration>();
var connectionString = config.GetConnectionString("MyDb");
context.GetDbConnection().ConnectionString = connectionString;
return context;
});
在运行时覆盖设置:
Configuration["ConnectionStrings:MyDb"] = newConnectionString;
答案 7 :(得分:0)
我知道其他人已经回答了。但是,我想与那些想在运行时更改数据库连接字符串的人分享我的方法。
我的应用程序是使用 asp.net core 2.2 和 Entity Framework 和 MySql 。
StartUp.cs
public void ConfigureServices(IServiceCollection services)
{
...
services.AddDbContext<MyDbContext>();
...
MyDbContext类
public partial class MyDbContext : DbContext
{
public MyDbContext()
{
}
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (DbManager.DbName != null && !optionsBuilder.IsConfigured)
{
var dbName = DbManager.DbName;
var dbConnectionString = DbManager.GetDbConnectionString(dbName);
optionsBuilder.UseMySql(dbConnectionString);
}
}
...
Json -具有连接信息的文件
[
{
"name": "DB1",
"dbconnection": "server=localhost;port=3306;user=username;password=password;database=dbname1"
},
{
"name": "DB2",
"dbconnection": "server=localhost;port=3306;user=username;password=password;database=dbname2"
}
]
DbConnection类
public class DbConnection
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("dbconnection")]
public string Dbconnection { get; set; }
public static List<DbConnection> FromJson(string json) => JsonConvert.DeserializeObject<List<DbConnection>>(json, Converter.Settings);
}
DbConnectionManager类
public static class DbConnectionManager
{
public static List<DbConnection> GetAllConnections()
{
List<DbConnection> result;
using (StreamReader r = new StreamReader("myjsonfile.json"))
{
string json = r.ReadToEnd();
result = DbConnection.FromJson(json);
}
return result;
}
public static string GetConnectionString(string dbName)
{
return GetAllConnections().FirstOrDefault(c => c.Name == dbName)?.Dbconnection;
}
}
DbManager类
public static class DbManager
{
public static string DbName;
public static string GetDbConnectionString(string dbName)
{
return DbConnectionManager.GetConnectionString()dbName;
}
}
然后,您将需要一些控制器来设置 dbName 。
控制器类
[Route("dbselect/{dbName}")]
public IActionResult DbSelect(string dbName)
{
// Set DbName for DbManager.
DbManager.DbName = dbName;
dynamic myDynamic = new System.Dynamic.ExpandoObject();
myDynamic.DbName = dbName;
var json = JsonConvert.SerializeObject(myDynamic);
return Content(json, "application/json");
}
您可能需要在这里和那里做一些技巧。但您会得到想法。在应用程序的开头,它没有连接详细信息。因此您必须使用Controller对其进行显式设置。希望这会帮助某人。
答案 8 :(得分:0)
用于静态连接的Startup.cs
customElements.define('user-card', class extends HTMLElement {
connectedCallback() {
this.attachShadow({mode: 'open'})
.innerHTML = `
<style>
div.name {
font-size: 24px;
width: 200px;
background-color: yellow;
display: inline-block;
}
::slotted(span[slot="username"]) {
display: inline-block;
width: 100%;
}
</style>
<div class="name">Name :
<slot name="username"></slot>
</div>
<div class="dob">Birthday:
<slot name="birthday"></slot>
</div>
`
}
})
function abc(event) {
console.log('Expected: ', this.document.getElementsByTagName('user-card')[0].shadowRoot.querySelector('.name').offsetWidth)
console.log('Result: ', event.target.parentElement.offsetWidth)
}
用于动态连接的Repository.cs
<user-card>
<span slot="username">
<div onclick="abc(event)">Ritesh Rajput</div>
</span>
<span slot="birthday">01.01.2001</span>
</user-card>
Table1MyContext.cs
services.AddScoped<MyContext>(_ => new MyContext(Configuration.GetConnectionString("myDB")));