Entity Frameworkはデータベースへのアクセスを容易にしてくれるライブラリです。
.NET Coreを利用している場合はそれに対応した Entity Framework Core を使います。
ー 開発環境 ー
Visual Studio 2019
.NET Core 3.1
NuGetからパッケージをインストールする
NuGetから以下のパッケージをインストールしましょう。
- Microsoft.EntityFrameworkCore
- Microsoft.EntityFrameworkCore.Tools
また、今回はデータベースとしてSQL Serverを利用するので以下のパッケージもインストールします。
- Microsoft.EntityFrameworkCore.SqlServer
Visual Studioの NuGetパッケージマネージャーからインストールすることができます。
メニューから「プロジェクト(P)」→「NuGet パッケージの管理(N)...」を選択します。
「Microsoft.EntityFrameworkCore」を検索し必要なパッケージをインストールします。
バージョンに注意!
Entity Framework Core の最新バージョンは 5.0.x ですが、Azure Functions の対象フレームワークが .NET Core 3.1 の場合は Entity Framework Core も 3.1.x を使用する必要があります。
誤って、最新バージョンをインストールすると実行時に以下のようなエラーが発生してしまいます。
A host error has occurred during startup operation 'xxxxxxxxxxxxxxxxxx'. System.Private.CoreLib: Could not load file or assembly 'Microsoft.Extensions.Logging.Abstractions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=xxxxx'. 指定されたファイルが見つかりません。.
データベースからモデルを作成する
Entity Frameworkではデータベースのテーブル構造をモデルにしたクラスを作成する必要があります。
既にデータベースがある場合にはデータベースから自動的にクラスを生成することが可能です。
Visual Studioのパッケージマネージャーコンソールから自動生成を実行できます。
メニューから「ツール(T)」→「NuGet パッケージ マネージャー(N)」→「パッケージ マネージャー コンソール(O)」を選択します。
下記の例のようにコンソールからScaffold-DbContextコマンドを実行します。
※自身の環境に合わせて書き換えてください
Scaffold-DbContext -Connection "xxxx" -Provider Microsoft.EntityFrameworkCore.SqlServer -OutputDir "Models\TestDatabase" -Context "TestDbContext" -DataAnnotations -UseDatabaseNames -Force
Scaffold-DbContextコマンドのパラメータは以下のような意味になります。
https://docs.microsoft.com/ja-jp/ef/core/cli/powershell#scaffold-dbcontext
-Connection
データベースの接続文字列を指定します。
SQL Serverの場合は
Data Source=[サーバー名];Database=[データベース名];user id=[ユーザー名];password=[パスワード]
のようになります。
-Provider
利用するデータベースのプロバイダを指定します。
SQL Server の場合は Microsoft.EntityFrameworkCore.SqlServer です。
-OutputDir
ファイルを格納するディレクトリを指定します。
プロジェクトディレクトリの相対パスを指定します。
-Context
DbContextを継承したクラスが生成されるので、そのクラス名を指定します。
-DataAnnotations
属性を使用してモデルを構成します (可能な場合)。
このパラメーターを省略した場合は、fluent API のみが使用されます。
-UseDatabaseNames
テーブル名と列名はデータベースに表示されるとおりに使用します。
このパラメーターを省略した場合、C# の名前のスタイル規則により厳密に準拠するように変更されます。
-Force
既存のファイルを上書きします。
完了すると、DbContextクラスを継承するクラスと、データベースのテーブルに対応するクラスが生成されます。
ソースコードに書かれたデータベース接続文字列を消す
DbContextクラスを継承したクラス(例ではTestDbContextクラス)が自動生成されますが、このままではビルド時に次のような警告が出てしまいます。
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings.
「データベース接続文字列のような機密性の高い情報はソースコードには書かないで!」という警告です。
TestDbContextクラスの中を見てみると、以下のようにデータベース接続文字列がソースコードの中に書かれてしまっています。 とりあえずテスト的に動作させることが出来るようにソースコードに書かれていますが危険なので消しましょう。
namespace AzureFunctionTest.Models.TestDatabase { public partial class TestDbContext : DbContext { public TestDbContext() { } public TestDbContext(DbContextOptions<TestDbContext> options) : base(options) { } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { if (!optionsBuilder.IsConfigured) { #warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings. optionsBuilder.UseSqlServer("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); } } } }
このオーバーライドされた OnConfiguringメソッドごと消してしまいましょう。
データベース接続文字列を環境変数に定義
ローカルテスト環境用の環境変数
ローカル環境では「local.settings.json」というJSON形式のファイルを使って環境変数を定義します。
「local.settings.json」を開いてValuesに接続文字列を定義します。
以下の例では「TestDBContextConnectionString」という名前の定義を追加しています。
{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "FUNCTIONS_WORKER_RUNTIME": "dotnet", "TestDBContextConnectionString": "xxxxxxxxxxxxxxxx" } }
Azureサーバー環境用の環境変数
Azureポータルサイトからでも設定できますが、ここではVisual Studioから設定してみます。
メニューから「ビルド(B)」→「発行(H)」を選択して公開画面を表示します。
ホスティング欄にある「…」ボタンをクリックして「Azure App Serviceの設定を管理する」を選択しましょう。
↓
local.settings.jsonに記述した内容がリストアップされます。
ローカル欄にはlocal.settings.jsonに指定した値が入っているはずです。
リモート欄にも接続文字列を入力して「OK」をクリックしましょう。
環境変数からデータベース接続文字列を取得する
先ほどTestDbContextクラスからデータベース接続文字列を消してしまったので、改めて接続文字列をセットするように改良します。
環境変数からデータベース接続文字列を取得し、TestDbContextクラスへセットするにはDI(Dependency injection)という設計モデルを使って行うのが良いようです。
参考: C# - DI(Dependency injection)依存性の注入とは
C# - DI(Dependency injection)コンテナを使う
C# - Azure FunctionsでDI(Dependency injection)を利用する
FunctionsStartupクラスを継承したクラスを作成する
Startupクラスを作成してTestDbContextクラスをDIコンテナへ登録します。
using Microsoft.Azure.Functions.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using Microsoft.EntityFrameworkCore; using AzureFunctionTest.Models.TestDatabase; [assembly: FunctionsStartup((typeof(AzureFunctionTest.Startup)))] namespace AzureFunctionTest { public class Startup : FunctionsStartup { public override void Configure(IFunctionsHostBuilder builder) { var connectionString = System.Environment.GetEnvironmentVariable("TestDBContextConnectionString"); builder.Services.AddDbContext<TestDbContext>(options => options.UseSqlServer(connectionString)); } } }
環境変数から値を取得するには Environment.GetEnvironmentVariableメソッドを使います。
(13行目)
DIコンテナへDbContextクラスを登録するにはAddDbContextメソッドを使います。(14行目)
データベースにSQL Serverを利用する場合にはAddDbContextメソッドの引数となる options のUseSqlServerメソッドから接続文字列をセットします。
※Microsoft.EntityFrameworkCoreをusingする必要があります
DIコンテナからDbContextインスタンスを取得する
FunctionsクラスのコンストラクタでTestDbContextのインスタンスを取得します。
using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using AzureFunctionTest.Models.TestDatabase; namespace AzureFunctionTest { public class TestFunction { private readonly TestDbContext TestDB; public TestFunction(TestDbContext db) { TestDB = db; } [FunctionName("TestFunction")] public async TaskGlt;IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, ILogger log) { return new OkResult(); } } }
コメントをお書きください