实体框架在运行时更改连接

时间:2013-11-26 11:42:34

标签: c# entity-framework asp.net-web-api connection-string

我有一个Web API项目,它引用了我的模型和DAL程序集。向用户显示登录屏幕,他可以在其中选择不同的数据库。

我按如下方式构建连接字符串:

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

首先,我如何实际更改数据上下文的连接?

其次,由于这是一个Web API项目,连接字符串(在每个登录时设置)在整个用户的交互中是持久的还是每次都应该传递给我的数据上下文?

11 个答案:

答案 0 :(得分:101)

这个答案有点晚了,但我认为用一个简洁的小扩展方法有可能做到这一点。我们可以利用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
    );

我知道你已经有了基本的功能,但我认为这会增加一点点多样性。

答案 1 :(得分:56)

DbContext有一个构造函数重载,它接受连接字符串的名称或连接字符串本身。实现您自己的版本并将其传递给基础构造函数:

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

然后在实例化DbContext

时,只需传递已配置连接字符串的名称或连接字符串本身
var context = new MyDbContext( "..." );

答案 2 :(得分:12)

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

我真的很抱歉。我知道我不应该使用答案来回答其他答案,但我的答案太长了,无法发表评论:(

答案 3 :(得分: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的回答一样。 / p>

之后,您可以使用参数化构造函数对原始。 : - )

示例:

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

答案 4 :(得分:0)

在web.config或app.config中添加多个连接字符串。

然后你可以把它们作为一个字符串,如:

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

然后使用字符串设置:

Provider
Metadata
ProviderConnectionString

这里有更好的解释:

Read connection string from web.config

答案 5 :(得分: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;
        }
    }

答案 6 :(得分:0)

在我的情况下,我使用ObjectContext而不是DbContext,所以为了这个目的,我在接受的答案中调整了代码。

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

答案 7 :(得分:0)

我想在app配置中拥有多个数据源。因此,在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;

这让我有点想弄清楚。我希望它可以帮助别人。我让它变得太复杂了。在这之前。

答案 8 :(得分: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);

答案 9 :(得分:0)

好吧,如果您正在使用 EFCore,那么您可以执行类似创建新连接字符串的操作: 在你的上下文文件中(对于 Sqlite)

public biorevContext(string connectionString) : base(GetOptions(connectionString))
    {
       this.Database.EnsureCreated();
    }
    private static DbContextOptions GetOptions(string connectionString)
    {
        return SqliteDbContextOptionsBuilderExtensions.UseSqlite(new DbContextOptionsBuilder(), connectionString).Options;
    }

对于 MySql:

 public biorevContext(string connectionString) : base(GetOptions(connectionString))
    {
       this.Database.EnsureCreated();
    }
    private static DbContextOptions GetOptions(string connectionString)
    {
        return MySQLDbContextOptionsExtensions.UseMySQL(new DbContextOptionsBuilder(), connectionString).Options;
    }

对于 SQL:

    public biorevContext(string connectionString) : base(GetOptions(connectionString))
    {
       this.Database.EnsureCreated();
    } 
    private static DbContextOptions GetOptions(string connectionString)
    {
        return SqlServerDbContextOptionsExtensions.UseSqlServer(new DbContextOptionsBuilder(), connectionString).Options;
    }

然后你可以这样使用它:

        var context = new biorevContext("connectionString");

答案 10 :(得分:-4)

Linq2SQLDataClassesDataContext db = new Linq2SQLDataClassesDataContext();

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

试试这段代码......