I hope somebody is able to help me, because it seems I'm totally stuck.
我希望有人能帮助我,因为我好像完全被困住了。
For upcoming projects in our company we'd like to use Entity Framework 5 with an code first approach. I played around a little while and everytime I try to use EF with our existing libraries, I fail because it seems EF heavily relies on an existing app.config.
对于即将在我们公司进行的项目,我们希望使用Entity Framework 5,并采用代码优先的方法。我花了一些时间,每次我尝试在现有库中使用EF时,我都失败了,因为EF似乎严重依赖于现有的app.config。
In our company, we have an inhouse database library that allows us to connect to various data sources and database technologies taking the advantages of MEF (managed extensibility framework) for database providers. I just have to pass some database settings, such as host (or file), catalog, user credentials and a database provider name, the library looks for the appropriate plugin and returns me a custom connection string or IDbConnection. We'd like to use this library together with EF because it allows us to be flexible about which database we use also change the database at runtime.
在我们的公司中,我们有一个内部数据库库,它允许我们利用数据库提供者的MEF(托管可扩展性框架)的优势连接各种数据源和数据库技术。我只需传递一些数据库设置,如主机(或文件)、目录、用户凭证和数据库提供程序名,库将查找适当的插件并返回自定义连接字符串或IDbConnection。我们希望与EF一起使用这个库,因为它允许我们灵活地使用哪个数据库,并且在运行时更改数据库。
So. I saw that a typical DbContext object takes no parameters in the constructor. It automatically looks for the appropriate connection string in app.config. We don't like such things so I changed the default constructor to take a DbConnection object that get's passed to the DbContext base class. No deal.
所以。我看到一个典型的DbContext对象在构造函数中没有参数。它会在app.config中自动查找适当的连接字符串。我们不喜欢这样的东西,所以我更改了默认构造函数,以获取传递给DbContext基类的DbConnection对象。没有达成任何协议。
Problems occur when the code first model changes. EF automatically notices this and looks for migration classes / configuration. But: A typical migration class requires a default parameterless constructor for the context! What a pity!
当代码第一模型发生变化时,就会出现问题。EF会自动注意到这一点,并查找迁移类/配置。但是:典型的迁移类需要上下文的默认无参数构造函数!真遗憾!
So we build our own migration class using the IDbContextFactory interface. But again, it seems that also this IDbContextFactory needs a parameterless constructor, otherwise I'm not able to add migrations or update the database.
因此,我们使用IDbContextFactory接口构建自己的迁移类。但是,似乎这个IDbContextFactory也需要一个无参数的构造函数,否则我无法添加迁移或更新数据库。
Further, I made my own data migration configurator where I pass the context, also the target database. Problem is here: It doesn't find any migration classes, no matter what I try.
此外,我还制作了自己的数据迁移配置程序,在其中传递上下文和目标数据库。这里的问题是:无论我尝试什么,它都没有找到任何迁移类。
I'm completely stuck because it seems the only way to use EF is when connection strings are saved in app.config. And this is stupid because we need to change database connections at runtime, and app.config is read-only for default users!
我完全被困住了,因为似乎使用EF的唯一方法是将连接字符串保存在app.config中。这很愚蠢,因为我们需要在运行时更改数据库连接,而app.config对默认用户是只读的!
How to solve this?
如何解决呢?
2 个解决方案
#1
3
The answer is provided here
答案在这里
https://*.com/a/15919627/941240
https://*.com/a/15919627/941240
The trick is to slightly modify the default MigrateDatabaseToLatestVersion
initializer so that:
诀窍是稍微修改默认的MigrateDatabaseToLatestVersion初始化器,以便:
- the database is always initialized ...
- 数据库总是被初始化……
- ... using the connection string from current context
- …使用当前上下文中的连接字符串。
The DbMigrator
will still create a new data context but will copy the connection string from yours context according to the initializer. I was even able to shorten the code.
DbMigrator仍将创建一个新的数据上下文,但将根据初始化器从您的上下文复制连接字符串。我甚至可以缩短代码。
And here it goes:
这里是:
public class MasterDetailContext : DbContext
{
public DbSet<Detail> Detail { get; set; }
public DbSet<Master> Master { get; set; }
// this one is used by DbMigrator - I am NOT going to use it in my code
public MasterDetailContext()
{
Database.Initialize( true );
}
// rather - I am going to use this, I want dynamic connection strings
public MasterDetailContext( string ConnectionString ) : base( ConnectionString )
{
Database.SetInitializer( new CustomInitializer() );
Database.Initialize( true );
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
public class CustomInitializer : IDatabaseInitializer<MasterDetailContext>
{
#region IDatabaseInitializer<MasterDetailContext> Members
// fix the problem with MigrateDatabaseToLatestVersion
// by copying the connection string FROM the context
public void InitializeDatabase( MasterDetailContext context )
{
Configuration cfg = new Configuration(); // migration configuration class
cfg.TargetDatabase = new DbConnectionInfo( context.Database.Connection.ConnectionString, "System.Data.SqlClient" );
DbMigrator dbMigrator = new DbMigrator( cfg );
// this will call the parameterless constructor of the datacontext
// but the connection string from above will be then set on in
dbMigrator.Update();
}
#endregion
}
Client code:
客户机代码:
static void Main( string[] args )
{
using ( MasterDetailContext ctx = new MasterDetailContext( @"Database=ConsoleApplication801;Server=.\SQL2012;Integrated Security=true" ) )
{
}
using ( MasterDetailContext ctx = new MasterDetailContext( @"Database=ConsoleApplication802;Server=.\SQL2012;Integrated Security=true" ) )
{
}
}
Running this will cause the two databases to be created and migrated according to the migration configuration.
运行这将导致创建两个数据库并根据迁移配置迁移。
#2
0
It needs a parameterless constructor in order to invoke it. What you could do is provide your default DbConntectionFactory
in the empty constructor, something like:
它需要一个无参数的构造函数来调用它。您可以在空的构造函数中提供默认的DbConntectionFactory,内容如下:
public DbContext()
{
IDbContextFactory defaultFactory; //initialize your default here
DbContext(defaultFactory);
}
public DbContext(IDbContextFactory factory)
{
}
#1
3
The answer is provided here
答案在这里
https://*.com/a/15919627/941240
https://*.com/a/15919627/941240
The trick is to slightly modify the default MigrateDatabaseToLatestVersion
initializer so that:
诀窍是稍微修改默认的MigrateDatabaseToLatestVersion初始化器,以便:
- the database is always initialized ...
- 数据库总是被初始化……
- ... using the connection string from current context
- …使用当前上下文中的连接字符串。
The DbMigrator
will still create a new data context but will copy the connection string from yours context according to the initializer. I was even able to shorten the code.
DbMigrator仍将创建一个新的数据上下文,但将根据初始化器从您的上下文复制连接字符串。我甚至可以缩短代码。
And here it goes:
这里是:
public class MasterDetailContext : DbContext
{
public DbSet<Detail> Detail { get; set; }
public DbSet<Master> Master { get; set; }
// this one is used by DbMigrator - I am NOT going to use it in my code
public MasterDetailContext()
{
Database.Initialize( true );
}
// rather - I am going to use this, I want dynamic connection strings
public MasterDetailContext( string ConnectionString ) : base( ConnectionString )
{
Database.SetInitializer( new CustomInitializer() );
Database.Initialize( true );
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
public class CustomInitializer : IDatabaseInitializer<MasterDetailContext>
{
#region IDatabaseInitializer<MasterDetailContext> Members
// fix the problem with MigrateDatabaseToLatestVersion
// by copying the connection string FROM the context
public void InitializeDatabase( MasterDetailContext context )
{
Configuration cfg = new Configuration(); // migration configuration class
cfg.TargetDatabase = new DbConnectionInfo( context.Database.Connection.ConnectionString, "System.Data.SqlClient" );
DbMigrator dbMigrator = new DbMigrator( cfg );
// this will call the parameterless constructor of the datacontext
// but the connection string from above will be then set on in
dbMigrator.Update();
}
#endregion
}
Client code:
客户机代码:
static void Main( string[] args )
{
using ( MasterDetailContext ctx = new MasterDetailContext( @"Database=ConsoleApplication801;Server=.\SQL2012;Integrated Security=true" ) )
{
}
using ( MasterDetailContext ctx = new MasterDetailContext( @"Database=ConsoleApplication802;Server=.\SQL2012;Integrated Security=true" ) )
{
}
}
Running this will cause the two databases to be created and migrated according to the migration configuration.
运行这将导致创建两个数据库并根据迁移配置迁移。
#2
0
It needs a parameterless constructor in order to invoke it. What you could do is provide your default DbConntectionFactory
in the empty constructor, something like:
它需要一个无参数的构造函数来调用它。您可以在空的构造函数中提供默认的DbConntectionFactory,内容如下:
public DbContext()
{
IDbContextFactory defaultFactory; //initialize your default here
DbContext(defaultFactory);
}
public DbContext(IDbContextFactory factory)
{
}