런타임시 Entity Framework 변경 연결


80

내 모델 및 DAL 어셈블리를 참조하는 웹 API 프로젝트가 있습니다. 사용자에게 다른 데이터베이스를 선택할 수있는 로그인 화면이 표시됩니다.

다음과 같이 연결 문자열을 작성합니다.

    public void Connect(Database database)
    {
        //Build an SQL connection string
        SqlConnectionStringBuilder sqlString = new SqlConnectionStringBuilder()
        {
            DataSource = database.Server,
            InitialCatalog = database.Catalog,
            UserID = database.Username,
            Password = database.Password,
        };

        //Build an entity framework connection string
        EntityConnectionStringBuilder entityString = new EntityConnectionStringBuilder()
        {
            Provider = database.Provider,
            Metadata = Settings.Default.Metadata,
            ProviderConnectionString = sqlString.ToString()
        };
    }

우선 데이터 컨텍스트의 연결을 실제로 어떻게 변경합니까?

둘째, 이것은 웹 API 프로젝트이므로 연결 문자열 (위의 로그인에서 설정 됨)이 사용자의 상호 작용 동안 지속됩니까? 아니면 매번 내 데이터 컨텍스트에 전달되어야합니까?


나는 그것이 당신의 사고 방식 / 도구 상자 요구 사항에 맞는 경우를 대비하여 약간의 대안을 추가했습니다.
jim tollan 2013

@ Ivan-Mark이 부분을 어떻게 해결 했습니까? 그리고 두 번째로, 이것은 웹 API 프로젝트이기 때문에 사용자 상호 작용 전체에 걸쳐 연결 문자열 (위에 로그인 할 때 설정 됨)이 지속되거나 내 데이터 컨텍스트에 매번 전달되어야합니다
Narendra Singh Rathore

@NarendraSinghRathore 연결 문자열은 데이터베이스 이름 (또는 다른 이름)이 키인 구성 파일에 저장됩니다. 사용자는 로그인시 데이터베이스를 선택하고 키가 사용자 이름 일 수있는 캐시에 저장됩니다. 사용자는 자신의 사용자 이름을 헤더로 전달하는 요청을하고 연결 문자열을 검색하여 데이터 컨텍스트에 전달합니다.
Ivan-Mark Debono 2017

@ Ivan-MarkDebono이 캐시 를 설명 할 수 있습니까 ? 백엔드에서 메모리 캐시 또는 세션을 사용하거나 프런트 엔드에서 쿠키로 저장하고 있습니까? 감사!
Narendra Singh Rathore 2017

1
싱글에서 @NarendraSinghRathore MemoryCache
이반 마크 Debono

답변:


110

이 답변에 조금 늦었지만 깔끔한 작은 확장 방법으로 이것을 수행하는 잠재적 인 방법이 있다고 생각합니다. 구성에 대한 EF 규칙과 약간의 프레임 워크 호출을 활용할 수 있습니다.

어쨌든, 주석이 달린 코드와 예제 사용법 :

확장 메서드 클래스 :

public static class ConnectionTools
{
    // all params are optional
    public static void ChangeDatabase(
        this DbContext source,
        string initialCatalog = "",
        string dataSource = "",
        string userId = "",
        string password = "",
        bool integratedSecuity = true,
        string configConnectionStringName = "") 
        /* this would be used if the
        *  connectionString name varied from 
        *  the base EF class name */
    {
        try
        {
            // use the const name if it's not null, otherwise
            // using the convention of connection string = EF contextname
            // grab the type name and we're done
            var configNameEf = string.IsNullOrEmpty(configConnectionStringName)
                ? source.GetType().Name 
                : configConnectionStringName;

            // add a reference to System.Configuration
            var entityCnxStringBuilder = new EntityConnectionStringBuilder
                (System.Configuration.ConfigurationManager
                    .ConnectionStrings[configNameEf].ConnectionString);

            // init the sqlbuilder with the full EF connectionstring cargo
            var sqlCnxStringBuilder = new SqlConnectionStringBuilder
                (entityCnxStringBuilder.ProviderConnectionString);

            // only populate parameters with values if added
            if (!string.IsNullOrEmpty(initialCatalog))
                sqlCnxStringBuilder.InitialCatalog = initialCatalog;
            if (!string.IsNullOrEmpty(dataSource))
                sqlCnxStringBuilder.DataSource = dataSource;
            if (!string.IsNullOrEmpty(userId))
                sqlCnxStringBuilder.UserID = userId;
            if (!string.IsNullOrEmpty(password))
                sqlCnxStringBuilder.Password = password;

            // set the integrated security status
            sqlCnxStringBuilder.IntegratedSecurity = integratedSecuity;

            // now flip the properties that were changed
            source.Database.Connection.ConnectionString 
                = sqlCnxStringBuilder.ConnectionString;
        }
        catch (Exception ex)
        {
            // set log item if required
        }
    }
}

기본 사용법 :

// assumes a connectionString name in .config of MyDbEntities
var selectedDb = new MyDbEntities();
// so only reference the changed properties
// using the object parameters by name
selectedDb.ChangeDatabase
    (
        initialCatalog: "name-of-another-initialcatalog",
        userId: "jackthelady",
        password: "nomoresecrets",
        dataSource: @".\sqlexpress" // could be ip address 120.273.435.167 etc
    );

이미 기본 기능을 갖추고 있지만 이것이 약간의 다양성을 추가 할 것이라고 생각했습니다.


6
대단합니다, 감사합니다! 나는 Controller항상 컨트롤러의 'db'를 고객 특정 db로 설정 하는 확장과 함께 멀티 테넌트 프로젝트에서 이것을 사용할 수 있습니다 . 이렇게하면 추가되는 모든 클라이언트에 대해 새 연결 문자열을 만들지 않아도됩니다 (또는 향후 관리자 / 개발자).
LukeP 2014 년

3
그래, 나는이 문제에 대한 실행 가능한 강력한 솔루션을 찾기 위해 문자 그대로 며칠 동안 고생 했으며이 간단한 확장 방법이 내 문제에 답했습니다. 작년 11 월에 만든 이래로 변경할 필요가 없었기 때문에 그대로 도로 테스트를 거쳤습니다. :). 어쨌든, 몇 개의 상자를 틱하는 것이 기쁩니다.
jim tollan 2014 년

5
이 오류 System.ArgumentException : 키워드가 지원되지 않습니다 : EF 4의 '데이터 소스'
sheshadri

2
@ user1234 또한 오류 : 키워드가 '데이터 소스'를 지원하지 않습니다. 이 문제를 해결하기 위해 나는 그의 코드의이 부분을 변경해야했다 : // add a reference to System.Configuration var entityCnxStringBuilder = new EntityConnectionStringBuilder { ProviderConnectionString = new SqlConnectionStringBuilder(System.Configuration.ConfigurationManager .ConnectionStrings[configNameEf].ConnectionString).ConnectionString };
A.Ima

2
@jimtollan 내가 새 인스턴스를 만들 때마다 app.config에 저장된 이전 연결 문자열에서 생성되었습니다!
Abdulsalam Elsharif

62

DbContext연결 문자열 또는 연결 문자열 자체의 이름을 허용하는 생성자 오버로드가 있습니다. 고유 한 버전을 구현하고 기본 생성자에 전달합니다.

public class MyDbContext : DbContext
{
    public MyDbContext( string nameOrConnectionString ) 
        : base( nameOrConnectionString )
    {
    }
}

그런 다음 구성된 연결 문자열의 이름이나 연결 문자열 자체를 인스턴스화 할 때 전달하기 만하면됩니다. DbContext

var context = new MyDbContext( "..." );

내 DbContext 파생 클래스에 함수가 이미 존재한다는 것을 몰랐으므로 방금 사용했습니다.
Brian Leeming

2
이 답변은 승인 된 답변으로 표시되어야한다고 생각합니다.
수포

2
이 대답은 훌륭하지만 @eMeL이 설명합니다. 이 클래스는 자동 생성되므로 대신이 클래스를 기반으로 다른 클래스를 만들어야하므로 모델을 업데이트해도 덮어 쓰지 않습니다.
Juan Carlos Oropeza

4
@JuanCarlosOropeza : EF는 생성 된 클래스 (bot hcontext 및 엔터티)를 부분적으로 표시하므로 사용자 고유의 파일을 만들고 DbContext를 부분적으로 다시 선언하고 여기에 사용자 지정 함수를 추가 할 수 있습니다.
dotNET

14

Jim Tollan의 답변은 훌륭하게 작동하지만 오류 : 키워드가 '데이터 소스'를 지원하지 않습니다. 이 문제를 해결하기 위해 그의 코드에서이 부분을 변경해야했습니다.

// add a reference to System.Configuration
var entityCnxStringBuilder = new EntityConnectionStringBuilder
    (System.Configuration.ConfigurationManager
            .ConnectionStrings[configNameEf].ConnectionString);

이에:

// add a reference to System.Configuration
var entityCnxStringBuilder = new EntityConnectionStringBuilder
{
    ProviderConnectionString = new  SqlConnectionStringBuilder(System.Configuration.ConfigurationManager
               .ConnectionStrings[configNameEf].ConnectionString).ConnectionString
};

정말 죄송합니다. 다른 답변에 답변하기 위해 답변을 사용해서는 안된다는 것을 알고 있지만 답변이 너무 길어서 댓글을 작성할 수 없습니다.


6

생성 된 클래스는 '부분'입니다!

public partial class Database1Entities1 : DbContext
{
    public Database1Entities1()
        : base("name=Database1Entities1")
    {
    }

... 그리고 다음과 같이 부릅니다.

using (var ctx = new Database1Entities1())
      {
        #if DEBUG
        ctx.Database.Log = Console.Write;
        #endif

따라서 원래 자동 생성 된 클래스 (동일한 클래스 이름!)에 대한 부분 자체 클래스 파일을 만들고 이전에 Moho의 답변과 같이 연결 문자열 매개 변수가있는 새 생성자를 추가하면됩니다.

그 후에 원본에 대해 매개 변수화 된 생성자를 사용할 수 있습니다. :-)

예:

using (var ctx = new Database1Entities1(myOwnConnectionString))
      {
        #if DEBUG
        ctx.Database.Log = Console.Write;
        #endif

위의 솔루션이 저에게 효과적입니다. 당신은에서 더 많은 정보를 얻을 수있는 링크
Kartik 고얄

0

web.config 또는 app.config에 여러 연결 문자열을 추가합니다.

그런 다음 다음과 같은 문자열로 가져올 수 있습니다.

System.Configuration.ConfigurationManager.
    ConnectionStrings["entityFrameworkConnection"].ConnectionString;

그런 다음 문자열을 사용하여 설정합니다.

Provider
Metadata
ProviderConnectionString

여기에 더 잘 설명되어 있습니다.

web.config에서 연결 문자열 읽기


연결 문자열은 별도의 SQL Server 데이터베이스에 저장되고 목록이 사용자에게 제공됩니다.
Ivan-Mark Debono 2013

0
string _connString = "metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl;provider=System.Data.SqlClient;provider connection string="data source=localhost;initial catalog=DATABASE;persist security info=True;user id=sa;password=YourPassword;multipleactiveresultsets=True;App=EntityFramework"";

EntityConnectionStringBuilder ecsb = new EntityConnectionStringBuilder(_connString);
ctx = new Entities(_connString);

web.config에서 연결 문자열을 가져 와서 EntityConnectionStringBuilder 생성자에서 설정하고 컨텍스트의 생성자에서 EntityConnectionStringBuilder를 인수로 사용할 수 있습니다.

사용자 이름으로 연결 문자열을 캐시합니다. 몇 가지 일반적인 방법을 사용하여 캐시에서 추가 / 검색을 처리하는 간단한 예제입니다.

private static readonly ObjectCache cache = MemoryCache.Default;

// add to cache
AddToCache<string>(username, value);

// get from cache

 string value = GetFromCache<string>(username);
 if (value != null)
 {
     // got item, do something with it.
 }
 else
 {
    // item does not exist in cache.
 }


public void AddToCache<T>(string token, T item)
    {
        cache.Add(token, item, DateTime.Now.AddMinutes(1));
    }

public T GetFromCache<T>(string cacheKey) where T : class
    {
        try
        {
            return (T)cache[cacheKey];
        }
        catch
        {
            return null;
        }
    }

예,하지만 사용자가 컨트롤러의 작업을 호출 할 때마다 새 연결 문자열을 dbcontext에 전달해야합니까?
Ivan-Mark Debono 2013

아마도 각 호출 후에 컨텍스트를 삭제할 것이므로 그렇습니다. 컨텍스트는 하나의 요청 (작업 단위)에 대해서만 존재해야합니다. 설명
scheien 2013

그렇다면 세션 기간 동안 사용자의 연결 문자열을 어떻게 그리고 어디에 저장할까요? (많은 사용자가 웹 API 프로젝트에 연결할 수 있으며 다른 연결
문자열을

그것을 캐싱하고 사용자 이름이나 다른 키로 검색하는 것은 어떻습니까?
scheien 2013

0

제 경우에는 DbContext와 반대로 ObjectContext를 사용하고 있으므로 해당 목적으로 허용되는 답변에서 코드를 수정했습니다.

public static class ConnectionTools
{
    public static void ChangeDatabase(
        this ObjectContext source,
        string initialCatalog = "",
        string dataSource = "",
        string userId = "",
        string password = "",
        bool integratedSecuity = true,
        string configConnectionStringName = "")
    {
        try
        {
            // use the const name if it's not null, otherwise
            // using the convention of connection string = EF contextname
            // grab the type name and we're done
            var configNameEf = string.IsNullOrEmpty(configConnectionStringName)
                ? Source.GetType().Name
                : configConnectionStringName;

            // add a reference to System.Configuration
            var entityCnxStringBuilder = new EntityConnectionStringBuilder
                (System.Configuration.ConfigurationManager
                    .ConnectionStrings[configNameEf].ConnectionString);

            // init the sqlbuilder with the full EF connectionstring cargo
            var sqlCnxStringBuilder = new SqlConnectionStringBuilder
                (entityCnxStringBuilder.ProviderConnectionString);

            // only populate parameters with values if added
            if (!string.IsNullOrEmpty(initialCatalog))
                sqlCnxStringBuilder.InitialCatalog = initialCatalog;
            if (!string.IsNullOrEmpty(dataSource))
                sqlCnxStringBuilder.DataSource = dataSource;
            if (!string.IsNullOrEmpty(userId))
                sqlCnxStringBuilder.UserID = userId;
            if (!string.IsNullOrEmpty(password))
                sqlCnxStringBuilder.Password = password;

            // set the integrated security status
            sqlCnxStringBuilder.IntegratedSecurity = integratedSecuity;

            // now flip the properties that were changed
            source.Connection.ConnectionString
                = sqlCnxStringBuilder.ConnectionString;
        }
        catch (Exception ex)
        {
            // set log item if required
        }
    }
}

이 오류 키워드가 지원되지 않습니다 : '데이터 소스'. 저는 EF 4
sheshadri를

0

앱 구성에 여러 데이터 소스를 갖고 싶었습니다. 따라서 app.config에서 섹션을 설정 한 후 데이터 소스를 교체 한 다음 연결 문자열로 dbcontext에 전달합니다.

//Get the key/value connection string from app config  
var sect = (NameValueCollection)ConfigurationManager.GetSection("section");  
var val = sect["New DataSource"].ToString();

//Get the original connection string with the full payload  
var entityCnxStringBuilder = new EntityConnectionStringBuilder(ConfigurationManager.ConnectionStrings["OriginalStringBuiltByADO.Net"].ConnectionString);     

//Swap out the provider specific connection string  
entityCnxStringBuilder.ProviderConnectionString = val;

//Return the payload with the change in connection string.   
return entityCnxStringBuilder.ConnectionString;

알아내는 데 약간의 시간이 걸렸습니다. 누군가에게 도움이되기를 바랍니다. 나는 그것을 너무 복잡하게 만들고 있었다. 이 전에.


0

일반 연결 문자열을 Entity Framework 형식으로 변환하는 두 가지 확장 메서드가 있습니다. 이 버전은 app.config 파일에서 기본 프로젝트로 연결 문자열을 복사하지 않고 클래스 라이브러리 프로젝트에서 잘 작동합니다. 이것은 VB.Net이지만 C #으로 변환하기 쉽습니다.

Public Module Extensions

    <Extension>
    Public Function ToEntityConnectionString(ByRef sqlClientConnStr As String, ByVal modelFileName As String, Optional ByVal multipleActiceResultSet As Boolean = True)
        Dim sqlb As New SqlConnectionStringBuilder(sqlClientConnStr)
        Return ToEntityConnectionString(sqlb, modelFileName, multipleActiceResultSet)
    End Function

    <Extension>
    Public Function ToEntityConnectionString(ByRef sqlClientConnStrBldr As SqlConnectionStringBuilder, ByVal modelFileName As String, Optional ByVal multipleActiceResultSet As Boolean = True)
        sqlClientConnStrBldr.MultipleActiveResultSets = multipleActiceResultSet
        sqlClientConnStrBldr.ApplicationName = "EntityFramework"

        Dim metaData As String = "metadata=res://*/{0}.csdl|res://*/{0}.ssdl|res://*/{0}.msl;provider=System.Data.SqlClient;provider connection string='{1}'"
        Return String.Format(metaData, modelFileName, sqlClientConnStrBldr.ConnectionString)
    End Function

End Module

그 후 DbContext에 대한 부분 클래스를 만듭니다.

Partial Public Class DlmsDataContext

    Public Shared Property ModelFileName As String = "AvrEntities" ' (AvrEntities.edmx)

    Public Sub New(ByVal avrConnectionString As String)
        MyBase.New(CStr(avrConnectionString.ToEntityConnectionString(ModelFileName, True)))
    End Sub

End Class

쿼리 생성 :

Dim newConnectionString As String = "Data Source=.\SQLEXPRESS;Initial Catalog=DB;Persist Security Info=True;User ID=sa;Password=pass"

Using ctx As New DlmsDataContext(newConnectionString)
    ' ...
    ctx.SaveChanges()
End Using

0

SQL Server 및 SQLite 데이터베이스 모두에 대해 다음을 사용하십시오.

_sqlServerDBsContext = new SqlServerDBsContext(new DbContextOptionsBuilder<SqlServerDBsContext>().UseSqlServer("Connection String to SQL DB").Options);

SQLite의 경우 Microsoft.EntityFrameworkCore.Sqlite설치되어 있는지 확인 하고 연결 문자열은 단순히 " 'DataSource ='+ 파일 이름"입니다.

_sqliteDBsContext = new SqliteDBsContext(new DbContextOptionsBuilder<SqliteDBsContext>().UseSqlite("Connection String to SQLite DB").Options);

-6
Linq2SQLDataClassesDataContext db = new Linq2SQLDataClassesDataContext();

var query = from p in db.SyncAudits orderby p.SyncTime descending select p;
Console.WriteLine(query.ToString());

이 코드를 사용해보십시오 ...

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.