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

Iva*_*ono 75 c# entity-framework connection-string asp.net-web-api

我有一个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()
        };
    }
Run Code Online (Sandbox Code Playgroud)

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

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

jim*_*lan 103

这个答案有点晚了,但我认为用一个简洁的小扩展方法有可能做到这一点.我们可以利用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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

基本用法:

// 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
    );
Run Code Online (Sandbox Code Playgroud)

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

  • 这很好,谢谢!我可以在多租户项目中使用它以及扩展的"Controller",它总是将控制器的"db"设置为其客户特定的db.这也使我(或任何未来的管理员/开发人员)不必为每个添加的客户端创建新的连接字符串. (6认同)
  • 是的,我一直在努力寻找一个可行的强大解决方案来解决这个问题,这个简单的扩展方法解决了我的问题.自从去年开始创建它以来,我不需要对它进行任何改动,所以我认为它已经过路测试了:).无论如何,很高兴它能打几个盒子......很高兴能说话. (3认同)
  • 我收到此错误System.ArgumentException:不支持关键字:EF 4中的"数据源" (3认同)
  • @ user1234我也得到错误:关键字不支持'数据源'.为了解决这个问题,我不得不改变他的代码的这一部分:`//添加对System.Configuration的引用var entityCnxStringBuilder = new EntityConnectionStringBuilder {ProviderConnectionString = new SqlConnectionStringBuilder(System.Configuration.ConfigurationManager .ConnectionStrings [configNameEf] .ConnectionString).ConnectionString };` (2认同)
  • @jimtollan 每次我创建新实例时,它都是从 app.config 中保存的旧连接字符串创建的!! (2认同)

Moh*_*oho 57

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

public class MyDbContext : DbContext
{
    public MyDbContext( string nameOrConnectionString ) 
        : base( nameOrConnectionString )
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,只需在实例化时传递已配置连接字符串的名称或连接字符串本身 DbContext

var context = new MyDbContext( "..." );
Run Code Online (Sandbox Code Playgroud)

  • @JuanCarlosOropeza:EF巧妙地将生成的类(bot hcontext和实体)标记为部分,因此您可以创建自己的文件,在其中重新声明您的DbContext(作为部分)并在其中添加自定义函数. (3认同)
  • 我认为这个答案应该标记为已批准的答案。 (2认同)
  • 这个答案很棒,但正如@eMeL解释的那样.此类是自动生成的,因此您应该基于此类创建另一个类,因此如果更新模型,则不会覆盖. (2认同)

A.I*_*Ima 12

Jim Tollan的回答非常好,但我得到了错误:关键字不支持'数据源'.为了解决这个问题,我不得不改变他的代码的这一部分:

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

对此:

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

我真的很抱歉.我知道我不应该使用答案回答其他答案,但我的答案太长了评论:(


小智 6

创建的类是'部分'!

public partial class Database1Entities1 : DbContext
{
    public Database1Entities1()
        : base("name=Database1Entities1")
    {
    }
Run Code Online (Sandbox Code Playgroud)

......你这样称呼它:

using (var ctx = new Database1Entities1())
      {
        #if DEBUG
        ctx.Database.Log = Console.Write;
        #endif
Run Code Online (Sandbox Code Playgroud)

所以,你只需要为原始自动生成的类(具有相同的类名!)创建一个部分自己的类文件,并添加一个带有连接字符串参数的新构造函数,就像之前Moho的回答一样.

在它之后你可以使用参数化构造函数对原始.:-)

例:

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