Help us understand the problem. What is going on with this article?

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

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)ロジックを別途実装する。

一覧で取得することがある場合は、全体でかなりのデータ量になることもありますので、この方式をお薦めします。

CodeOne
【品質と生産性にこだわるシステム開発】 .NET(C#/VB.NET)専門・リモート開発歴10年。即日・1時間から頼める常駐しないエンジニア。確かな技術で開発チームを手堅くサポートいたします。
https://codeone.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした