24

デフォルトの接続文字列 ( から読み取ったものapp.config) を使用してコンテキストを作成すると、データベースが作成され、移行が機能します。基本的にはすべて順調です。一方、接続文字列がプログラムで (を使用してSqlConnectionStringBuilder) 作成された場合:

  • データベースが存在しない場合、データベースは作成されません (シナリオA)。
  • CreateDbIfNotExists()データベース モデルの最新バージョンを作成しますが、移行メカニズムは呼び出されませんB(シナリオ)。

Aデータベースにアクセスしたいときに例外がスローされます-明らかに-そこにありません。データベースが適切に作成されている場合、標準の接続文字列の場合とは異なり、B移行メカニズムは呼び出されません。

app.config : " Data Source=localhost\\SQLEXPRESS;Initial Catalog=Db13;User ID=xxx;Password=xxx"

ビルダー:

sqlBuilder.DataSource = x.DbHost;
sqlBuilder.InitialCatalog = x.DbName;
sqlBuilder.UserID = x.DbUser;
sqlBuilder.Password = x.DbPassword;

初期化子:

Database.SetInitializer(
    new MigrateDatabaseToLatestVersion<
        MyContext,
        Migrations.Configuration
    >()
);

仕様: Entity Framework: 5.0、DB: SQL Server Express 2008

4

7 に答える 7

15

彼はソリューションであり、app.configに接続文字列はありません。自動移行と、同じコンテキストを使用する 2 つのデータベースを使用します。実際のランタイムが提供する接続。アプローチ。

APP.CONFIG (EF 6 を使用)

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework,     Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
 </configSections>
 <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
 <entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework">
  <parameters>
    <parameter value="Data Source=localhost; Integrated Security=True; MultipleActiveResultSets=True" />
  </parameters>
</defaultConnectionFactory>
 </entityFramework>
</configuration>

デモ用にできるだけ小さくするためにコードを書き直しました。

using System;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Migrations;

namespace Ef6Test {
    public class Program {
    public static void Main(string[] args) {
        Database.SetInitializer(new MigrateDatabaseToLatestVersion<Ef6Ctx, Ef6MigConf>());
        WhichDb.DbName = "HACKDB1";
        var sqlConn = GetSqlConn4DBName(WhichDb.DbName);
        var context = new Ef6Ctx(sqlConn);
        context.Database.Initialize(true);
        AddJunk(context);
        //sqlConn.Close();  //?? whatever other considerations, dispose of context etc...

        Database.SetInitializer(new MigrateDatabaseToLatestVersion<Ef6Ctx, Ef6MigConf>()); // yes its default again reset this !!!!
        WhichDb.DbName = "HACKDB2";
        var sqlConn2 = GetSqlConn4DBName(WhichDb.DbName);
        var context2 = new Ef6Ctx(sqlConn2);
        context2.Database.Initialize(true);
        AddJunk(context2);
    }
    public static class WhichDb { // used during migration to know which connection to build
        public static string DbName { get; set; }
    }
    private static void AddJunk(DbContext context) {
        var poco = new pocotest();
        poco.f1 = DateTime.Now.ToString();
      //  poco.f2 = "Did somebody step on a duck?";  //comment in for second run
        context.Set<pocotest>().Add(poco);
        context.SaveChanges();
    }
    public static DbConnection GetSqlConn4DBName(string dbName) {
        var sqlConnFact =
            new SqlConnectionFactory(
                "Data Source=localhost; Integrated Security=True; MultipleActiveResultSets=True");
        var sqlConn = sqlConnFact.CreateConnection(dbName);
        return sqlConn;
    }
}
public class MigrationsContextFactory : IDbContextFactory<Ef6Ctx> {
    public Ef6Ctx Create() {
        var sqlConn = Program.GetSqlConn4DBName(Program.WhichDb.DbName); // NASTY but it works
        return new Ef6Ctx(sqlConn);
    }
}
public class Ef6MigConf : DbMigrationsConfiguration<Ef6Ctx> {
    public Ef6MigConf() {
        AutomaticMigrationsEnabled = true;
        AutomaticMigrationDataLossAllowed = true;
    }
}
public class pocotest {
    public int Id { get; set; }
    public string f1 { get; set; }
 //   public string f2 { get; set; } // comment in for second run
}
public class Ef6Ctx : DbContext {
    public DbSet<pocotest> poco1s { get; set; }
    public Ef6Ctx(DbConnection dbConn) : base(dbConn, true) { }
}
}
于 2013-04-21T16:00:48.123 に答える
3

次の手法を使用して接続を切り替えることができました

1) app.config で複数の接続文字列名を定義します。

2) 接続文字列名を取るコンテキストにコンストラクターを用意する

public Context(string connStringName)
        : base(connStringName)
    {

    }

3) コンテキストの Create メソッドをセットアップし、接続名を受信できるようにします (ちょっとしたトリックを使用)。

  public class ContextFactory : IDbContextFactory<Context>
  {
    public Context Create()
    {
        var s = (string)AppDomain.CurrentDomain.GetData("ConnectionStringName");
        var context = new Context(s);
        return context;
    }
}

4) 私の移行設定 ....

 public sealed class Configuration : DbMigrationsConfiguration<SBD.Syrius.DataLayer.Context>
{
   etc
}

5) コンテキストを作成する関数を設定します。

 private static Context MyCreateContext(string connectionStringName )
    {
        // so that we can get the connection string name to the context create method 
       AppDomain.CurrentDomain.SetData("ConnectionStringName", connectionStringName);

        // hook up the Migrations configuration
        Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context, Configuration>());

        // force callback by accessing database
        var db = new Context(connectionStringName);
        var site = db.Sites.FirstOrDefault()  // something to access the database

        return db;
    }
于 2013-04-06T05:31:04.700 に答える
1

私も同様の結論に達しました。

私たちは昨日、それについて長い議論をしました。それを見てください。

接続が DbContext ctor を介して呼び出された場合 - 問題が発生する場所です (簡略化)。DbMigrator実際に「デフォルトの空の」コンストラクターを呼び出すので、さまざまなものが混在します。私はそれからいくつかの本当に奇妙な効果を得ました。私の結論は、通常の初期化子はCreateDb...機能するということでした-しかし、移行は機能しません(場合によっては失敗し、エラーをスローします)。

結論としては、@kirsten が使用した DbContext ファクトリを介して、または DbContext 内で静的接続を作成および変更するなど、何らかの方法で「シングルトン」接続を作成することです。これですべての問題が解決するかどうかはわかりませんが、役立つはずです。

于 2013-04-07T23:57:21.917 に答える
0

移行の場合は、(1)MigrateDatabaseToLatestVersionコンテキスト内のいずれかのエンティティの使用を開始したときに自動的に開始されるものを使用するか、(2) DbMigratorEF に移行を開始するように明示的に指示するために使用できます。AddJunk(2) の利点は、( @philsoady の例のように) ダミー操作を実行する必要がなくMigratorScriptingDecorator、移行 SQL を抽出する場合にも使用できることです (コードの例 2 を参照)。

DbMigrationsConfiguration(2) のトリックは、同じ接続文字列がクラスとクラスで一貫して使用されるようにすることにあるようですDbContext。処理中に複数のコンテキストがインスタンス化されることに注意してください。DbMigration.Updateこれらはすべて、コンテキストのデフォルト コンストラクターを呼び出します (複数のコンストラクターがある場合は注意してください)。ここには 2 つのオプションもありconnection string nameますconnection string。以下のコードのコメントを参照してください。

EF 6.0.1 & 6.0.2 でテスト済み

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Infrastructure;

namespace ConsoleApplication1
{
    // Models
    public class Foo
    {
        [Key]
        public int Id { get; set; }
        public string Column1 { get; set; }
        public string Column2 { get; set; }
    }

    // Configuration
    public class Configuration : DbMigrationsConfiguration<Context>
    {
        public static string StaticConnectionString; // use connection string

        public Configuration()
        {
            AutomaticMigrationsEnabled = true;
            AutomaticMigrationDataLossAllowed = true;
            TargetDatabase = new DbConnectionInfo(StaticConnectionString, "System.Data.SqlClient"); // use connection string
            //TargetDatabase = new DbConnectionInfo("ConnectionStringName"); // use connection string name in app.config
        }

        protected override void Seed(Context context)
        {
        }
    }

    // Context
    public class Context : DbContext
    {
        public Context()
            //: base("ConnectionStringName") // use connection string name in app.config
            : base(ConsoleApplication1.Configuration.StaticConnectionString) // use connection string
        {
        }

        public IDbSet<Foo> Foos { get; set; }
    }

    // App
    class Program
    {
        static void Main(string[] args)
        {
            // Example 1 - migrate to test1 DB
            Configuration.StaticConnectionString = "Data Source=localhost;Initial Catalog=test1;Integrated Security=True;MultipleActiveResultSets=True";
            var configuration = new Configuration();
            var migrator = new DbMigrator(configuration);
            migrator.Update();
            Console.WriteLine("Migration 1 complete");

            // Example 2 - create migrate SQL and migrate to test2 DB
            // NOTE: You can't do this if you use a connection string name in app.config
            // Generate migrate sql script for migration to test2 DB
            Configuration.StaticConnectionString = "Data Source=localhost;Initial Catalog=test2;Integrated Security=True;MultipleActiveResultSets=True";
            configuration = new Configuration();
            migrator = new DbMigrator(configuration);
            var scriptor = new MigratorScriptingDecorator(migrator);
            string sql = scriptor.ScriptUpdate(null, null);
            Console.WriteLine("Migration 2 SQL:\n" + sql);

            // Perform migration to test2 DB
            configuration = new Configuration();
            migrator = new DbMigrator(configuration);
            migrator.Update();
            Console.WriteLine("Migration 2 complete");
        }
    }
}
于 2014-02-20T22:48:23.633 に答える