programing

Asp에서 연결 문자열을 동적으로 변경합니다.넷코어

lovejava 2023. 10. 6. 20:47

Asp에서 연결 문자열을 동적으로 변경합니다.넷코어

ApplicationDbContext가 아닌 컨트롤러에서 sql 연결 문자열을 변경하고 싶습니다.저는 Asp를 사용합니다.넷 코어 및 엔티티 프레임워크 코어.

예를 들어,

public class MyController : Controller {
    private readonly ApplicationDbContext _dbContext
    public MyController(ApplicationDbContext dbContext)
    {
        _dbContext = dbContext;
    }
    private void ChangeConnectionString()
    {
    // So, what should be here?
    } }

이거 어떻게 해요?

활성 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);
        });

갱신하다

1년 정도가 지난 후, 제 솔루션은 다른 답변에서 나온 것처럼 보입니다. 그러니 제가 마무리를 해드리겠습니다.

시작 파일에 HttpContextAccessor 단일 톤을 추가할 수 있습니다.

services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddDbContext<ERPContext>();

이렇게 하면 컨텍스트 생성기에 대한 주입이 해결됩니다.

public class ERPContext : DbContext
{
    private readonly HttpContext _httpContext;

    public ERPContext(DbContextOptions<ERPContext> options, IHttpContextAccessor httpContextAccessor = null)
        : base(options)
    {
        _httpContext = httpContextAccessor?.HttpContext;
    }

    //..

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            var clientClaim = _httpContext?.User.Claims.Where(c => c.Type == ClaimTypes.GroupSid).Select(c => c.Value).SingleOrDefault();
            if (clientClaim == null) clientClaim = "DEBUG"; // Let's say there is no http context, like when you update-database from PMC
            optionsBuilder.UseSqlServer(RetrieveYourBeautifulClientConnection(clientClaim));
        }
    }

    //..
}

이를 통해 클레임에 접근하여 추출하고 연관성을 결정할 수 있습니다.

애즈 @제임스Wilkins는 의견에 대해 OnConfiguring()이 생성되는 컨텍스트의 각 인스턴스에 대해 호출됩니다.

와 에 합니다.!optionsBuilder.IsConfigured에 맞는 할 수 있는 하게 하기 컨텍스트 구성을 우선시할 경우 테스트를 쉽게 수행하기 위해 이 옵션이 필요합니다.

당신과 비슷한 케이스가 있습니다.Startup 클래스의 ConfigureServices 메서드에서 ISServiceCollection구현 팩토리 오버로드를 사용하면 다음과 같습니다.

//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 Yury N은 AddTransient와 AddDbContext의 차이점이 무엇인지 물었고, 이는 유효한 질문입니다...그렇지 않습니다.제가 설명해 드릴게요.

이것은 원래 질문과 관련이 없습니다.

하지만... 그럼에도 불구하고, 이 경우에는 엔티티 프레임워크를 사용하여 '구현 공장'을 구현하는 것이 필요한 것보다 조금 더 까다로울 수 있습니다.

하지만, 이런 질문들로 요즘 운 좋게도 GitHub의 소스코드를 살펴볼 수 있기 때문에 AddDbContext가 정확히 무엇을 하는지 찾아 보았습니다.그리고 뭐...그것은 정말 어려운 일이 아닙니다.이러한 '추가'(및 '사용') 확장 방법은 편의적인 방법에 지나지 않습니다.따라서 AddDbContext에서 수행하는 모든 서비스와 옵션을 추가해야 합니다.AddDbContext 확장 방법을 재사용할 수도 있습니다. 구현 공장에서 자신의 오버로드를 추가하기만 하면 됩니다.

자, 다시 여러분의 질문으로 돌아오겠습니다.AddDbContext는 EF 특정 작업을 수행합니다.보다시피, 나중에 출시될 때(일시적, 싱글톤)에는 평생을 보낼 수 있습니다.AddTransient가 Asp입니다.필요한 모든 서비스를 추가할 수 있는 NetCore.그리고 구현 공장이 필요합니다.

이게 더 확실한 건가요?

을 로 할 수 .OnConfiguringDbContext가 메서드입니다.

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 요청에 querystring 파라미터를 선택적으로 제공하고 이것을 DbContext를 만드는 기본 연결 문자열로 대체하고 싶습니다.. 또는 경우 .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"
            });
        });
    }

늦었지만 EF Core에서 가장 간단한 방법은 nuget Microsoft를 사용하는 것입니다.엔티티 프레임워크 코어.관계:

_dbContext.Database.GetDbConnection().ConnectionString = "NEW_CONN_STRING";

이 기능은 어떤 이유로든 응용프로그램 구성/설정에 연결 문자열이 없거나 DbContext의 인스턴스 하나를 사용하여 동일한 구조의 여러 데이터베이스를 처리하려는 경우에 유용합니다.

영구적 또는 일시적으로 사용 여부는 DbContext에 대해 선택한 주입 수명 주기 유형에 따라 달라집니다.권장하지 않는 싱글톤 서비스로 주입하면 영구적입니다.

다른 답변은 모두 제게 맞지 않아 런타임에 DB 연결 문자열을 변경하는 작업자들을 위해 제 접근 방식을 공유하고자 합니다.

저의 애플리케이션은 Entity FrameworkMySql이 포함된 asp.net core 2.2로 제작되었습니다.

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 클래스

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을(를) 설정하는 컨트롤러가 필요합니다.

컨트롤러 클래스

[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");
}

여기저기서 뭔가 속임수를 써야 할 수도 있지만, 아이디어를 얻을 수 있을 겁니다.앱 시작 부분에 연결 내용이 없습니다.컨트롤러를 사용하여 명시적으로 설정해야 합니다.이것이 누군가에게 도움이 되길 바랍니다.

나를 위한 일:

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;
}

저는 이 해결책을 위해 노력했습니다.

대신에

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;

test1 데이터베이스와 test2 데이터베이스에 삽입하기 위해 .net6 콘솔 앱과 루프 1~10을 만들었습니다.Program.cs :

Console.WriteLine("Hello, World!");

for (int i = 1; i <= 10; i++)
{

    if (i % 2 == 0)
    {
        var _context = new AppDbContext("Data Source=.\\SQLEXPRESS;Initial Catalog=test2;Integrated Security=True"); // test2
        _context.Tbls.Add(new Tbl { Title = i.ToString() });
        _context.SaveChanges();
    }
    else
    {
        var _context = new AppDbContext("Data Source=.\\SQLEXPRESS;Initial Catalog=test1;Integrated Security=True"); // test1
        _context.Tbls.Add(new Tbl { Title = i.ToString() });
        _context.SaveChanges();
    }
}

AppDbContext.cs :

public partial class AppDbContext : DbContext
    {
        public AppDbContext(string connectionString) : base(GetOptions(connectionString))
        {
        }

        public virtual DbSet<Tbl> Tbls { get; set; }

        private static DbContextOptions GetOptions(string connectionString)
        {
            return SqlServerDbContextOptionsExtensions.UseSqlServer(new DbContextOptionsBuilder(), connectionString).Options;
        }
    }

정적 연결을 위한 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;
}

언급URL : https://stackoverflow.com/questions/36816215/dynamically-change-connection-string-in-asp-net-core