C# 如何将连接字符串注入IDbContextFactory的实例<;T>;?
我使用EntityFramework5进行代码优先迁移。我有一个C# 如何将连接字符串注入IDbContextFactory的实例<;T>;?,c#,entity-framework,asp.net-mvc-4,dependency-injection,unity-container,C#,Entity Framework,Asp.net Mvc 4,Dependency Injection,Unity Container,我使用EntityFramework5进行代码优先迁移。我有一个数据存储类,它派生自DbContext: public class DataStore : DbContext, IDataStore { public int UserID { get; private set; } public DataStore(int userId, string connectionString) : base(connectionString) { UserID
数据存储
类,它派生自DbContext
:
public class DataStore : DbContext, IDataStore
{
public int UserID { get; private set; }
public DataStore(int userId, string connectionString) : base(connectionString)
{
UserID = userId;
}
public virtual IDbSet<User> Users { get; set; }
// Rest of code here
}
这些类的构造函数参数在运行时被注入。到目前为止,一切都很好
迁移时会出现问题:因为我的数据存储
上下文类没有默认构造函数,所以我需要提供IDbContextFactory
的实现,以便代码优先迁移可以实例化它:
public class MigrationDataStoreFactory : IDbContextFactory<DataStore>
{
public DataStore Create()
{
// Need to inject connection string so we can pass it to this constructor
return new DataStore(0, "CONNECTION_STRING_NEEDED_HERE");
}
}
除此之外,这个类无论如何都不是由Unity实例化的;它似乎只是在某种程度上通过代码优先迁移的约定进行调用,所以即使我可以这样做,它也不会真的有帮助
如果我用该方法硬编码连接字符串,一切都会正常工作,但出于明显的原因,我不想这样做
有人能帮忙吗?首先定义数据库设置界面,例如
IDBConnectionSettings
。
在app.config
中添加连接字符串:
<connectionStrings>
<add name=" ConnectionString "
connectionString="Integrated Security=SSPI; Persist Security Info=False; InitialCatalog=DB; Data Source=(local);"
providerName="System.Data.SqlClient" />
</connectionStrings>
public class MigrationDataStoreFactory : IDbContextFactory<DataStore>
{
public DataStore Create()
{
return new DataStore(0);
}
}
现在,在使用接口之前,必须在代码中的某个位置注册该接口
unityContainer.Register<IDBConnectionSettings>();
unityContainer.Register();
在您的案例中,您可以在任何地方使用它
public class MigrationDataStoreFactory : IDbContextFactory<DataStore>
{
public string _connectionString { get; set; }
public MigrationDataStoreFactory(UnityContainer unityContainer)
{
_connectionString = unityContainer.Resolve<IDBConnectionSettings>().ConnectionString;
}
public DataStore Create()
{
return new DataStore(0, new DateTimeProvider(() => DateTime.Now), _connectionString);
}
}
公共类迁移DataStoreFactory:IDbContextFactory
{
公共字符串_connectionString{get;set;}
公共迁移数据存储工厂(UnityContainer UnityContainer)
{
_connectionString=unityContainer.Resolve().connectionString;
}
公共数据存储创建()
{
返回新的数据存储(0,新的DateTimeProvider(()=>DateTime.Now),\u 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<IDBConnectionSettings>();
....
....
var fileMap=new ExeConfigurationFileMap{ExeConfigFilename=Application.StartupPath+Path.directoryseportorchar+@“app.config”};//应用程序名称必须为
使用(var unityContainer=new unityContainer())
{
var configuration=ConfigurationManager.OpenMappedExeConfiguration(fileMap,ConfigurationUserLevel.None);
var unitySection=(UnityConfigurationSection)configuration.GetSection(“unity”);
加载配置(unitySection,“ConnectionString”);
{
unityContainer.Resolve();
....
....
我希望这能解决您的问题!谢谢您,这是我最终使用的方法,使用自定义的
IDatabaseInitializer
code from,这对我帮助很大
首先,我们向数据存储
类(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<User> Users { get; set; }
// Rest of code here
}
这是自定义初始值设定项代码:
public class MigrateDatabaseToLatestVersionWithConnectionString<TContext, TMigrationsConfiguration> : IDatabaseInitializer<TContext>
where TContext : DbContext
where TMigrationsConfiguration : DbMigrationsConfiguration<TContext>, 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();
}
}
只要我们将数据库初始值设定项设置为自定义初始值设定项并传入连接字符串(在我的例子中,这是在Global.asax
中完成的),迁移将使用正确的连接:
Database.SetInitializer<DataStore>(new MigrateDatabaseToLatestVersionWithConnectionString<DataStore, MyMigrationsConfiguration>(INJECTED_CONNECTION_STRING_HERE));
Database.SetInitializer(新的MigrateDatabaseToLatestVersionWithConnectionString(此处注入了连接字符串));
希望所有这些都有意义,请在评论中要求澄清。对于那些升级到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<MyDbContext, MyMigrationConfiguration>(useSuppliedContext: true));
using (var context = kernel.Get<MyDbContext>())
context.Database.Initialize(false);
Database.SetInitializer(新的MigrateDatabaseToLatestVersion(useSuppliedContext:true));
使用(var context=kernel.Get())
context.Database.Initialize(false);
也许我完全误解了您的问题,但您不能创建一个包装类(例如IDbConnectionConfig/DbConnectionConfig)它从appconfig读取连接字符串并用Unity注册/inject?是的,我可以,但是我应该将它注入到什么?;)连接字符串已经使用Unity注入了。问题是我不知道如何将任何内容注入到MigrationDataStoreFactory
。这是哪里“上下文…默认构造函数"异常来自?是Unity引发了异常还是EF迁移?你能用stacktrace更新你的问题吗?为什么你不尝试使用IDbConnectionFactory,因为你不必到处给出连接字符串!嗨@Steven,是迁移引发了错误。我现在更新了问题,希望澄清了一点,但正如我在编辑中所说的,即使我可以创建一个新的构造函数,我也不确定它是否会有帮助。如果你不想在构造函数中添加unityContainer,你可以使用无参数构造函数,并在那里用静态解析器解析它!这种方法替换你类中的所有连接字符串。正如我所说的,它是在最后一个代码示例中,不可能执行您正在执行的操作,即向构造函数中注入某些内容。这会引发我在问题中提到的异常。但是,您提到了一个静态解析器:这将如何工作?在MigrationDataStoreFactory.C中从Unity静态解析connectionstring不是更简单吗reate()
method?那么你就不需要使用你自己的初始值设定项了。你不需要进行注入,但它会更简单,更易于维护。我目前看不到它是可以进行注入的。这目前正在一个实时web应用程序中工作,所以是的,这是可能的!至于维护:自从我写了这段代码之后,我再也没有接触过它,所以我不知道将其视为一个问题。我最初希望避免静态注入的主要原因是,它会在我的代码中引入对Unity本身的依赖;但事实证明,这将是唯一的实例,因此如果我想在某个时候交换DI容器,就不会太费力。可能是一个超过从我的角度来看,我会告诉你的。:@Nicodems13你会怎么做
public class MigrateDatabaseToLatestVersionWithConnectionString<TContext, TMigrationsConfiguration> : IDatabaseInitializer<TContext>
where TContext : DbContext
where TMigrationsConfiguration : DbMigrationsConfiguration<TContext>, 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();
}
}
public class MigrationDataStoreFactory : IDbContextFactory<DataStore>
{
public DataStore Create()
{
return new DataStore(0);
}
}
Database.SetInitializer<DataStore>(new MigrateDatabaseToLatestVersionWithConnectionString<DataStore, MyMigrationsConfiguration>(INJECTED_CONNECTION_STRING_HERE));
// 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);
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext, MyMigrationConfiguration>(useSuppliedContext: true));
using (var context = kernel.Get<MyDbContext>())
context.Database.Initialize(false);