2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Entity Framework] Code First で SQL Server の FILESTREAM(バイナリファイル連携)アクセス

Last updated at Posted at 2019-03-03

SQL Server の FILESTREAM はバイナリデータを管理するのに便利な仕組みですが、Entity Framework の Code First ではまだサポートされていません(2019年2月現在)。

たとえば Entity Framework Core では、次の手順で FILESTREAM データにアクセスできるようになります。

1. インスタンスレベルで FILESTREAM を有効化します。

FILESTREAM の有効化と構成 | Microsoft Docs

2. FILESTREAM を有効化したデータベースを作成します。

FILESTREAM が有効なデータベースを作成する方法 | Microsoft Docs

3. モデルクラスに byte 配列型でプロパティを定義します。

public class FooTable
{
    // :
    public byte[] FileDataColumn { get; set; }
    // :
}

4. 初期マイグレーションを作成します。

[パッケージマネージャーコンソールの場合]
Add-Migration <migration name> -Context SampleContext
[CLI コマンドの場合]
dotnet ef migrations add <migration name> --context SampleContext

5. Migrations フォルダ内にヘルパークラスを作成します。

UpTableAfter メソッドでは、CREATE TABLE の後処理として、FILESTREAM に必要な Id 列と制約を定義し、バイナリ列を再作成します。
DownTableBefore メソッドでは、DROP TABLE の前処理として、バイナリ列と制約を削除します。

public static class FileStreamMigration
{
    public static void UpTableAfter(MigrationBuilder migrationBuilder, string tableName, string fileDataColumnName)
    {
        migrationBuilder.Sql($"ALTER TABLE [{tableName}] ADD [FsId] uniqueidentifier rowguidcol NOT NULL");
        migrationBuilder.Sql($"ALTER TABLE [{tableName}] ADD CONSTRAINT [UQ_{tableName}_FsId] UNIQUE NONCLUSTERED ([FsId])");
        migrationBuilder.Sql($"ALTER TABLE [{tableName}] ADD CONSTRAINT [DF_{tableName}_FsId] DEFAULT (NewID()) FOR [FsId]");
        migrationBuilder.Sql($"ALTER TABLE [{tableName}] DROP COLUMN [{fileDataColumnName}]");
        migrationBuilder.Sql($"ALTER TABLE [{tableName}] ADD [{fileDataColumnName}] varbinary(max) FILESTREAM NULL");
    }
 
    public static void DownTableBefore(MigrationBuilder migrationBuilder, string tableName, string fileDataColumnName)
    {
        migrationBuilder.Sql($"ALTER TABLE [{tableName}] DROP COLUMN [{fileDataColumnName}]");
        migrationBuilder.Sql($"ALTER TABLE [{tableName}] DROP CONSTRAINT [DF_{tableName}_FsId]");
        migrationBuilder.Sql($"ALTER TABLE [{tableName}] DROP CONSTRAINT [UQ_{tableName}_FsId]");
        migrationBuilder.Sql($"ALTER TABLE [{tableName}] DROP COLUMN [FsId]");
    }
}

6. 初期マイグレーションクラスの Up メソッド末尾にヘルパーメソッド呼び出しを追記します。

public partial class Initial : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
         :
         migrationBuilder.CreateTable(
                name: "FooTable", ...
         :
        FileStreamMigration.UpTableAfter(migrationBuilder, nameof(FooTable), nameof(FooTable.FileDataColumn));
    }

7. 初期マイグレーションクラスの Down メソッド先頭にヘルパーメソッドを呼び出しを追記します。

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        FileStreamMigration.DownTableBefore(migrationBuilder, nameof(FooTable), nameof(FooTable.FileDataColumn));
         :
        migrationBuilder.DropTable(
                name: "FooTable");
         :

これで byte 配列を介した Transact-SQL による FILESTREAM アクセス が実現できます。

Ignore 方式(検討を推奨)

ファイルサイズに対してよりスケーラブルな 「ファイル システム ストリーミング」(Win32 API)方式 でアクセスする場合や、モデルとは別個にバイナリデータを取得したい場合は、以下のようにします。

  • DbContext クラスの OnModelCreating メソッドでファイルデータ列を Ignore する。
  • FileStreamMigration.UpTableAfter メソッドから DROP COLUMN のステートメントを除去する。
  • バイナリデータ列値の取得(SELECT)、保存(UPDATE)ロジックを別途実装する。
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?