読者です 読者をやめる 読者になる 読者になる

EntityFrameworkのMigration.exeを設定ファイルで参照するオプション

こんにちは。beaglesoftの真鍋です。

EntityFrameworkでMigrationを実行するためには、Migration.exeを利用すればいいのですがなかなかなれるまで上手くいきませんでした。何が上手くいかないかというと、このMigration.exeで参照する設定ファイルに以下のような相対パスを記述したところ、パスを取得できないせいかデータベースのMigrationが実行されないという状況になってしまったのです…。

<configuration>
...
  <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=..\aspnet-ef-test.mdf;Initial Catalog=aspnet-ef-test;Integrated Security=True;MultipleActiveResultSets=true;" providerName="System.Data.SqlClient" />
  </connectionStrings>
...
</configuration>

通常接続文字列には以下のように;AttachDbFilename=|DataDirectory|\aspnet-ef-test.mdf;を記述します。

<connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\aspnet-ef-test.mdf;Initial Catalog=aspnet-ef-test;Integrated Security=True;MultipleActiveResultSets=true;" providerName="System.Data.SqlClient" />
  </connectionStrings>

ただ、このままだと実行時には|DataDirectory|が設定されていないためデータベースを指定した場所に作成することができません。Unitテストを実行するコンテキスト内であれば設定することができるのですが、コマンド実行するため今回は利用できません。

ちなみに、絶対パスで記述すると正常にデータベースは作成されるのでAttachDbFilenameの記述方法次第と言うことになりそうでした。

困ったのでStackOverflowを読みあさったのですが、ConnectionStringを利用して接続する方法以外見つかりませんでした。もう少し頑張るかと言うことでMigration.exeのソースコードを確認したところ、引数のオプションに|DataDirectory|を指定するオプション(startUpDataDirectory)がありました!。

/// <summary>
/// Initializes a new instance of the ToolingFacade class.
/// </summary>
/// <param name="migrationsAssemblyName"> The name of the assembly that contains the migrations configuration to be used. </param>
/// <param name="contextAssemblyName"> The name of the assembly that contains the DbContext to be used. </param>
/// <param name="configurationTypeName"> The namespace qualified name of migrations configuration to be used. </param>
/// <param name="workingDirectory"> The working directory containing the compiled assemblies. </param>
/// <param name="configurationFilePath"> The path of the config file from the startup project. </param>
/// <param name="dataDirectory"> The path of the application data directory from the startup project. Typically the App_Data directory for web applications or the working directory for executables. </param>
/// <param name="connectionStringInfo"> The connection to the database to be migrated. If null is supplied, the default connection for the context will be used. </param>
[SuppressMessage("Microsoft.Security", "CA2140:TransparentMethodsMustNotReferenceCriticalCodeFxCopRule")]
public ToolingFacade(
    string migrationsAssemblyName,
    string contextAssemblyName,
    string configurationTypeName,
    string workingDirectory,
    string configurationFilePath,
    string dataDirectory,
    DbConnectionInfo connectionStringInfo)
{
    Check.NotEmpty(migrationsAssemblyName, "migrationsAssemblyName");

    _migrationsAssemblyName = migrationsAssemblyName;
    _contextAssemblyName = contextAssemblyName;
    _configurationTypeName = configurationTypeName;
    _connectionStringInfo = connectionStringInfo;

    var info = new AppDomainSetup
        {
            ShadowCopyFiles = "true"
        };

    if (!string.IsNullOrWhiteSpace(workingDirectory))
    {
        info.ApplicationBase = workingDirectory;
    }

    _configurationFile = new ConfigurationFileUpdater().Update(configurationFilePath);
    info.ConfigurationFile = _configurationFile;

    var friendlyName = "MigrationsToolingFacade" + Convert.ToBase64String(Guid.NewGuid().ToByteArray());

    _appDomain = AppDomain.CreateDomain(friendlyName, null, info);

    if (!string.IsNullOrWhiteSpace(dataDirectory))
    {
        _appDomain.SetData("DataDirectory", dataDirectory);    // ここでDataDirectoryを設定している!
    }
}
ソースコードはこちら

Entity Framework - Home

ということで、この引数を指定してMigration.exeを実行すれば任意のフォルダにデータベースファイルを作成してくれるようになりました。

最終的なバッチ処理内容

最終的なテストプロジェクトに設定したビルド後の処理は以下の通りとなります。

copy $(SolutionDir)packages\EntityFramework.6.1.3\tools\migrate.exe $(TargetDir)
$(TargetDir)migrate.exe EfDatabaseCreateSample.dll Configuration /startUpDirectory:$(TargetDir) /startUpConfigurationFile:$(TargetDir)EfDatabaseCreateSampleTests.dll.config /startUpDataDirectory:$(SolutionDir)$(TargetName)\App_Data /verbose

これでデータベースを利用したテストを実行しやすくなります。

まとめ

Migration.exeのオプションを簡単にまとめます。

migrate.exe [Migration対象のdll] Configuration /オプション

オプションには以下の内容を指定できます。

  • startUpDirectory:Migration.exeを実行ディレクトリ
  • startUpConfigurationFile:参照する設定ファイルのパス
  • startUpDataDirectory:|DataDirectory|に設定するパス
  • targetMigration:対象となるマイグレーション名
  • connectionStringName:接続文字列名
  • connectionString:接続文字列
  • connectionProviderName:データベースプロバイダー名
  • force:Migrationの強制実行
  • verbose:ログの表示

という感じでしょうか。

参考

上記手順を再現できるソリューションを作成したので添付します。Enjoy!

EfDatabaseCreateSample.zip (476.2 kB)