BEACHSIDE BLOG

Azure と GitHub と C# が好きなエンジニアの個人メモ ( ・ㅂ・)و ̑̑

ASP.NET5 MVC6 Entity Framework 7 を使って Database First する

ASP.NET Advent Calendar 2015 20日目です。二日酔いで原因不明で体調が悪くて遅刻でした。すいませんm(_ _)m。....次のチャック先生もまだ未公開のようですね....ふふふ...

個人的に Database First 派なので、EntityFramework 7で対応方法をここでお勉強♪。
「Dabase First って何?」をザックリいうと、既存のデータベースからリバース エンジニアリングでPOCOなエンティティ・クラスを作成することです。

> Environment

ASP.NET5は、現時点での上記ツールを入れたバージョンとなります。
ASP.NET and Web Tools 2015 (RC1 Update 1)入れてないとテンプレートがでない(んでしたっけ?)とかあるので、試される方は、ググっていただきインストールが必要です。
バージョンによって多少テンプレートの中身も異なる可能性が大きいし、直ぐにバージョンも上がると思うので、きっと数週間~数か月でここで書く方法とは古くなるんだろうなーと思ってます。

> 事前準備

DBは、Azure の SQLDatabase に接続しています。事前にデータベースとテーブルを作っておきます。
うむ、それくらいでしょうか。

>1. ASP.NET5 MVC6 のプロジェクト作成

VSでプロジェクトを作成します。新規プロジェクトで、[ASPNET Web Appication]を選び、targetのframeworkは、デフォルトで表示される4.6.1、テキトーにプロジェクト名とかつけ、[OK]します。
f:id:beachside:20151221173241p:plain

テンプレートの選択では、MVCのテンプレートとなる[Web Application]を選択して[OK]します。
f:id:beachside:20151221173631p:plain

MVC6のテンプレートということで、ControllersとかServiceとかModelsなどのフォルダができています。
bowerの設定変えたほうがいんじゃね的な内容が、ハンセルマン先生のブログにあったりなかったりしますが、
www.hanselman.com
今回の趣旨ではないので、放置プレイです。

>2. Nuget Packageで必要なもの導入

現時点でプロジェクトを作成すると、Entityframework関連は、以下が入ってます。

  • EntityFramework.MicrosoftSqlServer(7.0.0-rc1-final)
  • EntityFramework.Commands(7.0.0-rc1-final)
  • EntityFramework.AspNet.Identity.Framework(3.0.0-rc1-final)

Identityは、個人認証のプロジェクトを作ったから私のには入っているだけですが、その他は必須なはずです。
これに加え、DabaseFirstするために以下をインストールします。

  • EntityFramework.MicrosoftSqlServer.Design(7.0.0-rc1-final)


コマンド叩いてインストールしてもOKですが、今回は何となくNugetPackageManagerでやってます。
画面上部の[Tools] > [Nuget Package Manager] > [Manager Nuget Packages for Solution...]を開きます。
f:id:beachside:20151221180122p:plain

[Browse]を選択して、「EntityFramework.MicrosoftSqlServer.Design」と入力して検索します。一つヒットすると思いますので、その子をインストールします。
f:id:beachside:20151221175907p:plain

>3. コマンドで Database First する

「Database First する」とか日本語変じゃね?的なことはさておき、Database First するにはコマンド叩きますので、コマンドプロンプトとか開きます(画面キャプチャはConsole2です。)

まず、プロジェクトのディレクトリに移動します。今回だと、プロジェクト名が「mvc6ef7」なので、
「{プロジェクトを作ったディレクトリ}/mvc6ef7/src/mvc6ef7」
になります。ようは、「Models」とか「Controllers」とかあるディレクトリです。

ここまでの操作は、nugetで「productivity power tools 2015」を入れていると、
プロジェクト名(「mvc6ef7」)の上で右クリック > [Power Commands] > [Opne Command Prompt]でさっと開けます。

次に、dnvmコマンドを使う準備的なこととして、以下のコマンド打ちます。(バージョンが上がるとコマンドも変わるのでご注意を)

dnvm use 1.0.0-rc1-update1

f:id:beachside:20151221183757p:plain


次に、本題の Database First するわけですが、コマンドはこんな感じです。

dnx ef dbcontext scaffold "Server={接続文字列!}" EntityFramework.MicrosoftSqlServer --outputDir {出力先のディレクトリ}

今回はAzure SQL Databaseに接続するので、Azureのポータルから接続文字列を確認しつつておきます。出力先はご自由にということで、DAL/DbContextsというディレクトリに出力します。
具体的なコマンドのサンプルですが、

  • 接続先のサーバーが「tcp:beachside-dev-sv.database.windows.net,1433」
  • Database名が「sqldatabase」
  • SQL認証のユーザーは「Hogehoge」
  • パスワードは「passHogehoge」

だと、以下のコマンドになります。

dnx ef dbcontext scaffold "Server=tcp:beachside-dev-sv.database.windows.net,1433;Database=dev-sqldatabase;User ID=Hogehoge;Password=passHogehoge" EntityFramework.MicrosoftSqlServer --outputDir DAL/DbContexts

コマンドを実行すると、ダラララララーと動いて、問題なければ最後にDoneとでます。
ソリューションエクスプローラーを確認すると、テーブルの定義に添ったエンティティ・クラスが追加されていることが確認できます。

f:id:beachside:20151221185035p:plain

>4. 接続文字列の設定、動作検証

接続文字列は、デフォルトではDbContextのクラスに書かれます。今回、データベース名が「dev-sqldatabase」だったので、DbContextのクラスは、「dev_sqldatabaseContext」になっています。
クラスを開くと、DbContextクラスが継承されており、最初の方に接続文字列の設定が書かれています。

f:id:beachside:20151221191723p:plain

接続文字列は、「Startup.cs」でごねごねするのが一般的なようなので、[OnConfiguring]メソッドごと削除します。
あ、その前に、接続文字列自体は利用するので、どこかにコピーしておきましょう。

ごねごねする前に、ソリューションエクスプローラー直下にある「appsettings.json」を開いて接続文字列を書きます。こんな感じで。

{
  "Data": {
    "DefaultConnection": {
      "ConnectionString": "Server=tcp:beachside-dev-sv.database.windows.net,1433;Database=dev-sqldatabase;User ID={ひみつ};Password={ひみつ};MultipleActiveResultSets=true"
    }
  },
  "EntityFramework": {
    "dev_sqldatabaseContext": {
      "ConnectionStringKey": "Data:DefaultConnection:ConnectionString"
    }
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Verbose",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}

「"Logging"」はデフォルトのままです(今回のネタとは関係ないので)。

接続文字列となる「ConnectionString」の値はコピペした通り書きますが、最後に「MultipleActiveResultSets=true」つけておくと良いでしょう。「それ何?」という方は以下あたりを参照に...。
複数のアクティブな結果セット (MARS) の有効化

さらに小ネタとして、「EntityFramework」を追加し、DbContextのクラス名(今回だと「dev_sqldatabaseContext」)を指定して、「ConnectionStringKey」に上記の通り書きます。お察しの通りですが、さっき書いた接続文字列を指定しているだけです。

次に、「Startup.cs」を開きましょう。
クラス内に[public void ConfigureServices(IServiceCollection services)...]というメソッドがありますので、以下を追記します。

var connection = Configuration["Data:DefaultConnection:ConnectionString"];
services.AddEntityFramework()
    .AddSqlServer()
    .AddDbContext<dev_sqldatabaseContext>(options => options.UseSqlServer(connection));

これで、設定は完了です。
動作検証ということで、ざっくりなコードをHomeControllerに書いて試してみましょう。

public class HomeController : Controller
{
public async Task<IActionResult> Index()
{
    using (var context = new dev_sqldatabaseContext())
    {
        var customers = await context.Customer.Where(c => c.Title == "Mr.").ToListAsync();
        Debug.WriteLine(customers.Count);
    }
            
    return View();
}

雑にもHomeのIndexにコードを追加してしまったので、デバッグしてUrlを叩くと無事に動作することが確認できました。今回のサンプルでは、「Customer」テーブルが存在し、そこに「Title」列があって、その中で「Mr.」の行を取得した例になります。
(AzureのSQL Databaseでサンプルデータ付きで作れるのでそれを使ってます。中身は、みんな大好きAdventureWorksです。)


Startupクラスで接続文字列を取得してごねごねしてますが、別の書き方もできます。
「appsettings.json」で"EntityFramework"に関するコードを書いていますので、[public void ConfigureServices(IServiceCollection services)...]にて、こんな書き方でも問題なく接続できます。

services.AddEntityFramework()
    .AddSqlServer()
    .AddDbContext<dev_sqldatabaseContext>();

「AddDbContext」でoptionを書かないパターンです。ケースバイケースでどちらでもって感じですね。

>まとめ

現時点では、問題なくデータベース・ファーストで作れます。
以前から同様にpartialなクラスになってるので、必要に応じてコンストラクタを追加したり色々できますね。
一日半以上の遅刻となりましたが以上です。

Oh、次のチャック先生は、既にブログ公開済みになってますね...。