如何将连接字符串注入IDbContextFactory 的实例?

我正在使用Entity Framework 5和Code First Migrations。 我有一个从DbContext派生的DataStore类:

 public class DataStore : DbContext, IDataStore { public int UserID { get; private set; } public DataStore(int userId, string connectionString) : base(connectionString) { UserID = userId; } public virtual IDbSet Users { get; set; } // Rest of code here } 

还有一个工厂类,它创建DataStore类的DataStore

 public class DataStoreFactory : Disposable, IDataStoreFactory { private DataStore _database; private int _userId; private string _connectionString; public DataStoreFactory(int userId, string connectionString) { _userId = userId; _connectionString = connectionString; } public IDataStore Get() { _database = new DataStore(_userId, _connectionString); return _database; } protected override void DisposeCore() { if (_database != null) _database.Dispose(); } } 

这些类的构造函数参数在运行时使用Unity注入。 到目前为止一切都很好,一切都很棒!

当我们进行迁移时会出现问题:因为我的DataStore上下文类没有默认构造函数,所以我需要提供IDbContextFactory的实现,以便Code First Migrations可以实例化它:

 public class MigrationDataStoreFactory : IDbContextFactory { public DataStore Create() { // Need to inject connection string so we can pass it to this constructor return new DataStore(0, "CONNECTION_STRING_NEEDED_HERE"); } } 

问题是我无法弄清楚如何将连接字符串注入此类。 我不能用这样的连接字符串参数创建一个新的构造函数:

 public class MigrationDataStoreFactory : IDbContextFactory { public string _connectionString { get; set; } public MigrationDataStoreFactory(string connectionString) { _connectionString = connectionString; } public DataStore Create() { return new DataStore(0, new DateTimeProvider(() => DateTime.Now), _connectionString); } } 

如果我这样做,我会在运行时获得以下exception抛出的exception:

 [InvalidOperationException: The context factory type 'MigrationDataStoreFactory' must have a public default constructor.] System.Data.Entity.Infrastructure.DbContextInfo.CreateActivator() +326 System.Data.Entity.Infrastructure.DbContextInfo..ctor(Type contextType, DbProviderInfo modelProviderInfo, AppConfig config, DbConnectionInfo connectionInfo) +106 System.Data.Entity.Infrastructure.DbContextInfo..ctor(Type contextType) +52 System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration, DbContext usersContext) +202 System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration) +66 System.Data.Entity.MigrateDatabaseToLatestVersion`2.InitializeDatabase(TContext context) +50 // Truncated stack trace, but you get the idea 

除此之外,这个类还没有被Unity实例化; 似乎只是通过Code First Migrations以某种方式召集,所以即使我能做到这一点也不会真正帮助……

如果我在该方法中对连接字符串进行硬编码,一切正常,但出于显而易见的原因,我不想这样做。

有人可以帮忙吗?

对于那些升级到Entity Framework 6的人来说,迁移初始化的新重载使得这更容易:

  // Parameters: // useSuppliedContext: // If set to true the initializer is run using the connection information from the // context that triggered initialization. Otherwise, the connection information // will be taken from a context constructed using the default constructor or registered // factory if applicable. public MigrateDatabaseToLatestVersion(bool useSuppliedContext); 

使用此方法,您可以使用注入的DbContext运行迁移,如下所示:

 Database.SetInitializer(new MigrateDatabaseToLatestVersion(useSuppliedContext: true)); using (var context = kernel.Get()) context.Database.Initialize(false); 

这是我最终使用的方法,使用此答案中的自定义IDatabaseInitializer代码,这对我帮助很大。

首先,我们向DataStore类( DbContext )添加另一个构造函数,该类不需要连接字符串参数:

 public class DataStore : DbContext, IDataStore { public int UserID { get; private set; } // This is the constructor that will be called by the factory class // if it is initialised without a connection string parameter public DataStore(int userId) { UserID = userId; } public DataStore(int userId, string connectionString) : base(connectionString) { UserID = userId; } public virtual IDbSet Users { get; set; } // Rest of code here } 

然后我们对工厂类做同样的事情:

 public class DataStoreFactory : Disposable, IDataStoreFactory { private DataStore _database; private int _userId; private string _connectionString; // This is the constructor that will be called by the // MigrationDataStoreFactory class public DataStoreFactory(int userId) { _userId = userId; } public DataStoreFactory(int userId, string connectionString) { _userId = userId; _connectionString = connectionString; } public IDataStore Get() { // If we have a connection string, construct our context with it, // if not, use the new constructor if(_connectionString != null) _database = new DataStore(_userId, _dateTimeServices, _connectionString); else _database = new DataStore(_userId, _dateTimeServices); return _database; } protected override void DisposeCore() { if (_database != null) _database.Dispose(); } } 

这是自定义初始化代码:

 public class MigrateDatabaseToLatestVersionWithConnectionString : IDatabaseInitializer where TContext : DbContext where TMigrationsConfiguration : DbMigrationsConfiguration, new() { private readonly DbMigrationsConfiguration _config; public MigrateDatabaseToLatestVersionWithConnectionString() { _config = new TMigrationsConfiguration(); } public MigrateDatabaseToLatestVersionWithConnectionString(string connectionString) { // Set the TargetDatabase for migrations to use the supplied connection string _config = new TMigrationsConfiguration { TargetDatabase = new DbConnectionInfo(connectionString, "System.Data.SqlClient") }; } public void InitializeDatabase(TContext context) { // Update the migrator with the config containing the right connection string DbMigrator dbMigrator = new DbMigrator(_config); dbMigrator.Update(); } } 

我们的自定义上下文工厂(仅由Code First Migrations调用)现在可以继续使用不需要连接字符串的DataStore构造函数:

 public class MigrationDataStoreFactory : IDbContextFactory { public DataStore Create() { return new DataStore(0); } } 

只要我们将数据库初始化程序设置为我们的自定义初始化程序并传入连接字符串(在我的情况下在Global.asax完成),迁移将使用正确的连接:

 Database.SetInitializer(new MigrateDatabaseToLatestVersionWithConnectionString(INJECTED_CONNECTION_STRING_HERE)); 

希望所有有意义的 – 随意在评论中要求澄清。

首先定义数据库设置界面,例如IDBConnectionSettings 。 在app.config添加连接字符串:

     

要从Settings文件或app.config中检索连接字符串,您需要执行以下操作:

  public class DBConnectionSettings() { get ConnectionString { var connections = ConfigurationManager.ConnectionStrings; // From app.config you will get the connection string var connectionString = connections["ConnectionString"].ConnectionString; return connectionString; } } 

现在,您必须在使用前在代码中的某处注册接口。

 unityContainer.Register(); 

在您的情况下,您可以在任何地方使用它。

 public class MigrationDataStoreFactory : IDbContextFactory { public string _connectionString { get; set; } public MigrationDataStoreFactory(UnityContainer unityContainer) { _connectionString = unityContainer.Resolve().ConnectionString; } public DataStore Create() { return new DataStore(0, new DateTimeProvider(() => DateTime.Now), _connectionString); } } 

更新默认构造函数

创建一个静态方法或将此代码放在默认构造函数中,这样您就不必提供任何参数。

  var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = Application.StartupPath + Path.DirectorySeparatorChar + @"app.config" }; // application name must be using (var unityContainer = new UnityContainer()) { var configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); var unitySection = (UnityConfigurationSection)configuration.GetSection("unity"); unityContainer.LoadConfiguration(unitySection, "ConnectionString"); { unityContainer.Resolve(); .... .... 

我希望这能解决你的问题! 谢谢