ApplicationDbContextではなく、コントローラーでSQL接続文字列を変更したい。 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?
} }
これどうやってするの?
似たようなケースがあります。行ったのは、 ConfigureServices メソッドの IServiceCollection のimplementationfactoryオーバーロードを使用することです。 Startup クラス、次のように:
//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を構築することです。
このソリューションの実装にさらにサポートが必要な場合は教えてください。
Update#1Yuriy N.は、AddTransientとAddDbContextの違いを尋ねました。これは有効な質問です...そして、そうではありません。説明させてください。
これは元の質問には関係ありません。
しかし...とはいえ、この場合、エンティティフレームワークを使用して独自の「実装ファクトリ」(これは最も重要なことです)を実装することは、必要なものよりもややトリッキーになる可能性があります。
しかし、このような質問では、最近幸運にもGitHubのソースコードを見ることができるので、 AddDbContext が正確に行うことを調べました。そしてまあ...それは本当に難しいことではありません。これらの「追加」(および「使用」)拡張メソッドは、便利なメソッドに過ぎません、覚えておいてください。そのため、AddDbContextが行うすべてのサービスとオプションを追加する必要があります。実装ファクトリに独自のオーバーロードを追加するだけで、AddDbContext拡張メソッドを再利用することもできます。
それで、あなたの質問に戻りましょう。 AddDbContextは、EF固有の機能をいくつか実行します。ご覧のとおり、後のリリース(一時的、シングルトン)でライフタイムを渡すことができます。 AddTransientはAsp.Net Coreであり、必要なサービスを追加できます。そして、実装ファクトリーが必要です。
これにより、より明確になりますか?
アクティブな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);
});
ただし、ユーザーに簡単にアクセスすることも、ユーザーの主張もありません。手動で行わない限り。
接続文字列ロジックを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);
}
_
@ginalxと@jcmordanの答えは、私のユースケースに完全に適合しています。これらの答えについて私が気に入っているのは、Startup.cs
そして、他のすべてのクラスを構築コードから削除します。 Web Apiリクエストにオプションのクエリ文字列パラメーターを指定し、これをDbContextを作成する基本接続文字列に置き換えたいと思います。私はappsettings.jsonに基本文字列を保持し、渡されたパラメーターまたは何も指定されていない場合のデフォルトに基づいてフォーマットします。
"IbmDb2Formatted": "DATABASE={0};SERVER=servername;UID=userId;PWD=password"
私の最終的なConfigureServices
メソッドは次のようになります(obvs。SQLではなくDB2に接続していますが、それは偶然です)
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"
});
});
}
私のために働く:
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;
}
他の人がすでに答えていることを知っています。しかし、実行時にDB接続文字列を変更したい人々のために私のアプローチを共有したいと思います。
私のアプリケーションはasp.net core 2.2Entity FrameworkおよびMySql.
StartUp.cs
public void ConfigureServices(IServiceCollection services)
{
...
services.AddDbContext<MyDbContext>();
...
MyDbContext Class
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 Class
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
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);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
}
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 upを設定するコントローラーが必要になります。
Controller Class
[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を使用して明示的に設定する必要があります。これが誰かを助けることを願っています。
静的接続のStartup.cs
services.AddScoped<MyContext>(_ => new MyContext(Configuration.GetConnectionString("myDB")));
動的接続用のRepository.cs
using (var _context = new MyContext(@"server=....){
context.Table1....
}
Table1MyContext.cs
public MyContext(string connectionString) : base(GetOptions(connectionString))
{
}
private static DbContextOptions GetOptions(string connectionString)
{
return SqlServerDbContextOptionsExtensions.UseSqlServer(new DbContextOptionsBuilder(), connectionString).Options;
}