【问题标题】:Entity Framework Code First without app.config没有 app.config 的实体框架代码优先
【发布时间】:2013-04-19 02:42:20
【问题描述】:

我希望有人能够帮助我,因为我似乎完全陷入了困境。

对于我们公司即将开展的项目,我们希望将 Entity Framework 5 与代码优先方法一起使用。我玩了一会儿,每次尝试将 EF 与我们现有的库一起使用时,我都失败了,因为 EF 似乎严重依赖于现有的 app.config。

在我们公司,我们有一个内部数据库库,它允许我们连接到各种数据源和数据库技术,利用 MEF(托管扩展框架)为数据库提供者提供的优势。我只需传递一些数据库设置,例如主机(或文件)、目录、用户凭据和数据库提供程序名称,库会查找适当的插件并返回自定义连接字符串或 IDbConnection。 我们希望将此库与 EF 一起使用,因为它使我们能够灵活地选择使用哪个数据库,同时在运行时更改数据库。

所以。我看到一个典型的 DbContext 对象在构造函数中没有参数。它会自动在 app.config 中查找适当的连接字符串。我们不喜欢这样的事情,所以我更改了默认构造函数以获取传递给 DbContext 基类的 DbConnection 对象。没有交易。

代码优先模型更改时会出现问题。 EF 会自动注意到这一点并查找迁移类/配置。但是:典型的迁移类需要上下文的默认无参数构造函数!太可惜了!

所以我们使用 IDbContextFactory 接口构建自己的迁移类。但同样,这个 IDbContextFactory 似乎也需要一个无参数的构造函数,否则我无法添加迁移或更新数据库。

此外,我制作了自己的数据迁移配置器,用于传递上下文以及目标数据库。问题就在这里:无论我尝试什么,它都找不到任何迁移类。

我完全陷入困境,因为似乎使用 EF 的唯一方法是将连接字符串保存在 app.config 中。这很愚蠢,因为我们需要在运行时更改数据库连接,而 app.config 对默认用户来说是只读的!

如何解决?

【问题讨论】:

  • 如果你不传入连接字符串那么它必须从app.config中读取它?否则,它怎么会找到它?

标签: c# .net linq entity-framework app-config


【解决方案1】:

这里提供答案

https://stackoverflow.com/a/15919627/941240

诀窍是稍微修改默认的 MigrateDatabaseToLatestVersion 初始化器,以便:

  • 数据库总是被初始化...
  • ...使用当前上下文中的连接字符串

DbMigrator 仍将创建一个新的数据上下文,但会根据初始化程序从您的上下文中复制连接字符串。我什至可以缩短代码。

就这样:

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
}

客户端代码:

    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" ) )
        {
        }
    }

运行这个会导致根据迁移配置创建和迁移两个数据库。

【讨论】:

  • 完美解决我的问题的完美解决方案。我每次都必须为每个上下文都这样做太可怕了——微软似乎没有考虑动态数据库连接等。
  • 我记得我花了 3 或 4 个小时试图弄清楚这一点。然而,好的一点是,当您最终获得实际解决方案时,这似乎很简单。
  • 我知道这个答案已经存在了一段时间,但是当我实现它时我得到了一个 System.StackOverflowException。似乎在 InitializeDatabase 方法中构造新的 DbMigrator 时会调用 MasterDetailContext 的默认构造函数(根据 cmets),并且在默认构造函数中对 Database.Initialize( true ); 的调用会以某种方式再次调用 InitializeDatabase,因此它会陷入无限循环。我错过了什么吗?
  • 不要在构造函数中调用初始化。您只需要初始化一次,而不是每次创建新上下文时。既然我提供了这个答案,我会看看我是否在我身边进行了任何修改。
【解决方案2】:

它需要一个无参数的构造函数来调用它。您可以做的是在空构造函数中提供默认的DbConntectionFactory,例如:

public DbContext()
{
    IDbContextFactory defaultFactory; //initialize your default here
    DbContext(defaultFactory);
}

public DbContext(IDbContextFactory factory)
{
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-09-01
    • 2014-05-23
    • 1970-01-01
    • 1970-01-01
    • 2023-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多