アレアレ

お役立ち情報、お悩み解決情報を発信!

visual studio code

macでASP.NET MVC CoreとDapperを使ったウェブアプリを作る手順

macでASP.NET MVC CoreとDapperを使ったウェブアプリを作る手順

今回は、ASP.NET MVC Coreを使ってデータベースを利用したウェブアプリを作るのが初めての人に伝えたい情報です。

具体的には、データベースにSQLiteを使った、ASP.NET MVC Coreでのウェブアプリの作り方の手順をご紹介します。また、今回SQLiteにアクセスするにあたり、Dapperという、ライブラリを使って行います。

今回の手順から、ASP.NET MVC Coreの基本、またASP.NET MVC CoreならではのDI(Dependency Injection)の基本、Dapperを使ったデータアクセス処理の書き方が学べます。

また、今回作成するサンプルのソースコードは、こちらからダウンロードできます。

開発環境はmacが前提、一方WindowsでもLinuxでもできる

今回の開発環境としては、macを前提にします。ですが、クロスプラットフォームのASP.NET Coreアプリですので、実際には、WindowsやLinuxでも同じことができます。

また、開発ツールとしては、Visual Studio Code.NET Core コマンドライン インターフェイス (CLI) ツールを使う前提です。

ですが、これも普通のVisual StudioやVisual Studio For Macでも、同じことができます。ただ、Visual Studioを使う場合は、Visual Studio Codeとは、プロジェクトの新規作成方法やパッケージの追加方法が異なります。なので、そこは読み替える必要があります。

開発環境の構築方法

私が、この記事で解説する内容の動作確認が取れている開発環境は以下の通りです。

  • OS: macOS Sierra バージョン10.12.5
  • Visual Studio Code: バージョン1.13.1
  • .NET Core コマンドライン インターフェイス (CLI) ツール: 1.0.3

この開発環境の構築にあたり、Visual Studio Codeをインストールしていない人は、公式サイトの「Download Visual Studio Code」のページから、それをダウンロードし、インストールしてください。

次に、.NET Core コマンドライン インターフェイス (CLI) ツールを使えるようにするため、次のページから、利用するOSに合わせて、セットアップを行ってください。

Get Started with .NET Core

そのインストールに何GBも必要とする普通のVisual Studioと比べて、Visual Studio Coreと.NET Core コマンドライン インターフェイス (CLI) ツールの開発環境はとても軽いです。つまり、気軽にインストールできます。

プロジェクトを新規作成する

以上の開発環境が整った前提で始めます。

まず、今回のサンプルアプリのためのプロジェクトを新規に作成します。好きなところに、「MVCDapper」というフォルダを作ってください。ここでは、とりあえず、デスクトップにそのフォルダを作ったこととします。

そのフォルダができたら、ターミナルを起動し、カレントディレクトリをそのフォルダに移動します。

cd MVCDapper

その後、ASP.NET MVC Coreのウェブアプリの雛形を生成するために、次のコマンドを実行します。

dotnet new mvc

このコマンドを実行すると、そのフォルダ内に次のファイルとフォルダが生成されます。

  • Controllers
  • MVCDapper.csproj
  • Program.cs
  • Startup.cs
  • Views
  • appsettings.json
  • appsettings.Development.json
  • bower.json
  • bundleconfig.json
  • wwwroot

以上で、ASP.NET MVC Coreのプロジェクトを新規作成できました。

ちなみに、普通のVisual Studioを使っている場合は、この作業をGUIから行えます。ASP.NET MVC Coreプロジェクトを新規に作ると、同じ構成のプロジェクトが生成されます。

必要なライブラリのパッケージを追加する

新規プロジェクトを作成したら、今回のサンプルに必要なライブラリのパッケージを追加するために次のコマンドを実行します。

dotnet add package System.Data.SqlClient
dotnet add package Microsoft.Data.Sqlite
dotnet add package Dapper

ここでは、それぞれ、次の目的で、これらのライブラリを追加しました。

  • System.Data.SqlClient: データベースに接続するための基本のライブラリ
  • Microsoft.Data.Sqlite: データベースにSQLiteを使うためのライブラリ
  • Dapper: データベースへのアクセスを簡単に記述するためのライブラリ

上記のコマンドを実行後に、「MVCDapper.csproj」の中身を見てみると、次のようになっています。

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp1.1</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Dapper" Version="1.50.2" />
    <PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
    <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.1.1" />
    <PackageReference Include="Microsoft.Data.Sqlite" Version="1.1.1" />
    <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" />
    <PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="1.1.0" />
    <PackageReference Include="System.Data.SqlClient" Version="4.3.1" />
  </ItemGroup>
</Project>

見ると、上で追加した3つのライブラリが、書き込まれていることがわかりますよね。逆に言うと、MVCDapper.csprojをこの記述にすることでも、ライブラリの追加ができます。

つまり、上で利用した「dotnet add package」コマンドを使わなくても、テキストエディタでMVCDapper.csprojを編集し、ライブラリを追加することもできます。また、普通のVisual Studioを利用している場合は、nugetでパッケージを追加すれば良いです。

ライブラリの追加が出来たタイミングで、一度、プロジェクトをビルドしてみます。そのために、次のコマンドを打ちます。

dotnet restore
dotnet build

「dotnet build」コマンドの実行後に、「Build succeeded.」とターミナル上に表示されれば問題ありません。

dotnet build成功画面

SQLiteのデータベースを作る

続いて、今回のサンプルで利用するSQLiteのデータベースを作ります。「MVCDapper.db」というファイル名で、次の定義のテーブルを持つSQLiteのデータベースを作成してください。

CREATE TABLE `Dog` (
    `Id`    TEXT NOT NULL UNIQUE,
    `Name`  TEXT NOT NULL,
    `Age`   INTEGER,
    `Weight`    INTEGER,
    PRIMARY KEY(`Id`)
);

DB Browser for SQLiteというアプリを使うと、GUIから簡単に行えます。次の図は、そのMac版でデータベースやテーブルを作成している様子です。Windows版でも同じことができます。

DB Browser for SQLのMac版でデータベースを作る様子

DB Browser for SQLのMac版でテーブルを作る様子

そして、作成したデータベース「MVCDapper.db」は、「MVCDapper/bin/Debug/netcoreapp1.1」の配下に、置いてください。

データベースファイルの配置場所

データベースファーストがポイント

このように、Dapperを利用した開発では、データベースファーストとなるのが、ポイントです。

よく、ASP.NET MVC Coreのチュートリアルでは、Entity Framework Coreを利用した、コードファースト、つまり、プログラムからデータベースを生成する方法での開発手順が解説されるものが多いです。

このアプローチは、全くゼロからシステムを作る場合、かつ、テーブル数が少なく複雑なリレーションがない場合は、有効です。

一方で、現実のプロジェクトでは、既存のデータベースを利用することはよくあります。また、既存のシステムで利用していたSQLを再利用した方が、効率よく開発できることもあります。

そのような時に、Dapperが役立ちます。生のSQLを簡単に実行できるようなっているからです。

Dapperでは、ORMにSQLの生成を任せるのではなく、基本SQL文は自分で書きます。そして、実行した結果をいかにアプリ側(コード側)で楽に使えるようにするか?」という点が、工夫されています。

また、Entity Framework Coreを利用する場合、「SQLでは簡単にできるこんなことも、楽にできないの?」ということがあります。例えば、「2つのテーブルを結合して、その親のID毎に子の数を数える」のようなことです。

例として、欲しいイメージのSQL文を挙げると次のような感じです。

SELECT o.ID, o.Name, COUNT(d.Id) AS PETCOUNT 
FROM OWNER o 
LEFT OUTER JOIN DOG d on o.Id = d.OwnerId 
GROUP BY d.OwnerId

Onwnerテーブルを作っていないので、このSQLは実行できません。が、それがある前提でこのSQLを実行できたとすると、オーナー毎のペット数の数の一覧が取れます。

これと同じ結果を、Entity Framework Coreを使っても取れなくはありません。ですが、そのためには、「データベースから全データを引っ張ってきて、LINQを使いプログラム側でのメモリ上で集計する」ということが、現在のバージョンのEntity Framework Coreでは、必要になります。たかだか2テーブルを結合した集計をするのに、これは避けたいですよね。

とりあえずDBに接続してみる

では、いよいよ、Dapperを利用したウェブアプリを作っていきます。まず、次のコマンドを実行し、Visual Studio Codeで、作成したプロジェクトを開きます。

code .

コマンドが正しく実行できると、次の画像のように、MVCDapperフォルダをルートにして、そのプロジェクトを開けます。

Visual Studio Codeの起動画面

続いて、左のメニューから、HomeController.csを選択し、次のように編集します。

HomeController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

using Microsoft.Data.Sqlite;
using Dapper;

namespace MVCDapper.Controllers
{
    public class HomeController : Controller
    {
        public string CountDogs()
        {
            using(var connection = new SqliteConnection("Data Source=MVCDapper.db"))
            {
                var count = connection.Query<int>("SELECT COUNT(*) FROM Dog").First();

                return $"{count}";
            }
        }

        //以下、簡単のために省略

    }
}

このコードでは、HomeControllerに、CountDogsアクションメソッドを追加しています。そのアクションの中で、先ほど作成したSQLiteのデータベースに接続し、Dogテーブルのレコード数を数えています。

HomeControllerの編集が終わったら、このプログラムを実行し、その動作を確認します。次のコマンドを実行し、このウェブアプリを実行してください。

dotnet run

このコマンドにより、ウェブアプリが起動します。macの場合は、ポート5000で、ローカルのウェブサーバーが立ち上がり、そこでウェブアプリが動いている状況となります。

つまり、ブラウザで次のアドレスにアクセスすると、先ほど作成したHomeControllerのCountDogsアクションを呼び出せます。

http://localhost:5000/Home/CountDogs
※ポート番号(この例の5000)は、その開発環境によって異なる場合があります

そして、アクセスすると、ブラウザ上に「0」と表示されます。現段階では、Dogテーブルは空なので、こうなって当然ですよね。

試しに、DB Browser for SQLiteを使って、Dogテーブルにいくつか、データを作ってみてください。その際、Write Changesボタンで変更を反映するのをお忘れなく。

Database Browser for SQLiteのマック版でのデータ編集画面の様子

データを作った後、再度、ブラウザでCountDogsアクションを呼び出すと、そのレコード数が結果として表示されます。

データベースの接続定義をベタ書きしないように変更する

さて、以上の手順で、とりあえずは、ASP.NET MVC Coreのウェブアプリから、Dapperを利用してデータベースにアクセスすることができました。

ですが、このままでは、あまりにサンプル的というか、やっつけ的なプログラミングとなっています。そこで、ここから形を整えていきます。

まず、改めて、先ほど作成したDapperを利用したCountDogsアクションのプログラムを見てみましょう。

public string CountDogs()
{
    using(var connection = new SqliteConnection("Data Source=MVCDapper.db"))
    {
        var count = connection.Query<int>("SELECT COUNT(*) FROM Dog").First();

        return $"{count}";
    }
}

このプログラムでは、CountDogsアクションの中で、データベースの接続文字列が、「”Data Source=MVCDapper.db”」とベタ書きされています。そのため、今後、データベースの接続定義に変化があった時、修正する手間が発生します。

加えて、アクションの中でデータベースへの接続を定義しているため、また別のアクションでDB接続が必要になると、より手間です。そのアクション毎に、接続文字列がベタ書きされるからです。

この問題を解決するために、HomeControllerを次のように変更します。

HomeController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

using Microsoft.Data.Sqlite;
using Dapper;
using System.Data; //追加

namespace MVCDapper.Controllers
{
    public class HomeController : Controller
    {
        private IDbConnection connection;

        public HomeController(IDbConnection connection)
        {
            this.connection = connection;
        }

        public string CountDogs()
        {
            var count = connection.Query<int>("SELECT COUNT(*) FROM Dog").First();

            return $"{count}";
        }

        //以下、簡単のために省略

    }
}

このコードで、何を変えたかというと、HomeControllerクラスのメンバ変数として、IDbConnectionを定義したことです。そして、HomeControllerのコンストラクタで、それを引数にもらい設定するように変更しています。

これによって、HomeController内で「”Data Source=MVCDapper.db”」とベタ書きする必要がなくなりました。

データベースの接続情報の生成をStartup.csに定義する

一方で、このように変更したということは、どこかでデータベースの接続文字列をちゃんと設定した接続情報のインスタンスを作成し、それをHomeControllerのコンストラクタに渡す必要が出てきます。

ASP.NET MVC Coreでは、その定義を、Startup.csファイルの中で行えます。具体的には、次のようにコードを変更します。

Startup.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

using System.Data; //追加
using Microsoft.Data.Sqlite; //追加

namespace MVCDapper
{
    public class Startup
    {
        //簡単のための省略

        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddMvc();

            // コンフィグから接続文字列を読み込み、IDbConnectionのインスタンスとして
            // SqliteConnectionを生成するように定義
            services.AddScoped<IDbConnection>(
               provider=> new SqliteConnection(
                   Configuration.GetConnectionString("DefaultConnection")));
        }

        //簡単のための省略
    }
}

合わせて、appsettings.jsonに、次のようにデータベースの接続文字列を定義してください。

appsettings.json

{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=MVCDapper.db"
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  }
}

以上のように変更したことで、プログラムの中に、接続文字列「Data Source=MVCDapper.db」をベタ書きせずに済むようになりました。その定義は、設定ファイルのappsettings.jsonに書かれ、そこから読み込まれるようになりました。

Startup.csファイルのConfigureServicesメソッド内では、次のコードがポイントです。

services.AddScoped<IDbConnection>(
    provider=> new SqliteConnection(
    Configuration.GetConnectionString("DefaultConnection")));

この定義をおこなったことにより、HomeControllerのコンストラクタの引数に、SqliteConnectionのインスタンスが渡されるようになります。

つまり、この時点で、この変更したプログラムは、変更前と同じように動作します。試しに、プログラムを起動して、ブラウザから、CountDogsアクションを呼び出してみてください。

具体的には、次のコマンドを実行し、

dotnet run

ウェブアプリを起動してください。すでにウェブアプリが起動中の場合は、「Ctrl + C」キーを押して、一度プログラムを停止してから、再度、「dotnet run」をすると良いです。

そして、http://localhost:5000/Home/CountDogsにブラウザでアクセスして見ると、変更前と同じ結果が得られます。

HomeControllerからデータベースへの依存を排除する

いよいよ大詰めです。以上のように、データベースへの接続情報を利用するだけでも、だいぶ汎用性のあるプログラムになってきました。ですが、このように変更しても、HomeControllerには、まだ問題があります。

それは、HomeControllerの中で、データベースに接続する処理を書いてしまっていることです。具体的には、IDbConnectionを利用したり、connection.QueryのようにDapperのメソッドを利用したり、している点が、よくないわけです。

なぜ良くないかというと、そうすると、コントローラーのクラスを利用した、独立したテストを行うのが難しくなるからです。

今回のサンプルでは、ローカルで動作するSQLiteを利用しているため、その難しさがわかりにくいはずです。ですが、これがOracleやSQL Serverのようなサーバー型のデータベースを利用していた場合はどうでしょうか。

その場合は、サーバー側のデータベースを用意できない限り、HomeControllerが単体でテストできなくなってしまうわけです。なので、HomeController上では、データベースを一切意識しないように、プログラムを修正する必要があります。

IDogService.csを作成する

そのように修正するために、プロジェクトの直下にModelsというフォルダを作り、IDogService.csというファイルを作ります。そして、そのファイルに、次のコードを書きます。

IDogService.cs

namespace MVCDapper.Models
{
    public interface IDogService{
        int CountDogs();
    }
}

HomeControllerのCountDogsメソッドで欲しい情報は、突き詰めて考えると、「Dogテーブルのレコードの数」ということがわかりますよね。なので、その数を返すだけの非常にシンプルなインターフェースとして、IDogServiceを定義します。

そして、HomeControllerは、このIDogServiceを利用する形に変更します。

HomeController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

//using Microsoft.Data.Sqlite; //削除
//using Dapper; //削除
//using System.Data; //削除

using MVCDapper.Models; //追加

namespace MVCDapper.Controllers
{
    public class HomeController : Controller
    {

        private IDogService service;

        public HomeController(IDogService service)
        {
            this.service = service;
        }

        public string CountDogs()
        {
            var count = service.CountDogs();

            return $"{count}";
        }

        //以下、簡単のための省略

    }
}

ご覧の通り、HomeControllerのCountDogsアクションでは、IDogServiceのCountDogsメソッドを利用するように変更されました。その結果、IDbConnectionやDapperを利用したデータベースへの接続処理が、HomeControllerから取り除かれました。

また、HomeControllerでは、IDogServiceをメンバ変数に持ち、その初期化は、コンストラクタから貰う形になっています。つまり、これから、IDogServiceを実装した具体的なクラスを作成し、そのインスタンスが渡されるように、Startup.csに定義を追加することになります。

DogServiceを実装する

そこで、まずは、IDogServiceを実装した具体的なクラスとして、DogServiceを作成します。Modelsファイルの直下にDogService.csファイルを作成し、次のコードを書いてください。

DogService.cs

using System.Data;
using System.Linq;
using System.Collections.Generic;
using Dapper;

namespace MVCDapper.Models
{
    public class DogService : IDogService{

        private IDbConnection connection;

        public DogService(IDbConnection connection)
        {
            this.connection = connection;
        }

        public int CountDogs(){
            return connection.Query<int>("SELECT COUNT(*) FROM Dog").First();
        }
    }
}

このコードでは、IDogServiceインターフェースを実装したクラスとして、DogServiceを定義しています。

DogServiceは、具体的にデータにアクセスする処理を書く目的のクラスなので、IDbConnectionやDapperのメソッドを利用した、具体的なデータアクセス処理を書いて問題ありません。

ただし、DogServiceで利用するIDbConnectionのインスタンスは、上でHomeControllerに行ったように、DogServiceのコンストラクタで貰う形にしておいた方が、独立性が高まります。

DogServiceのCountDogsでは、IDbConnectionとDapperを利用して、データベースに問い合わせ、その結果intで返す処理を行っています。

最後に、HomeControllerのコンストラクで渡されるIDogServiceのインスタンスの中身に、先ほど実装したDogServiceが作成されるように、Startup.csを編集します。次の通りです。

Startup.cs

//簡単のための省略

using System.Data; //追加
using Microsoft.Data.Sqlite; //追加

using MVCDapper.Models; //追加

namespace MVCDapper
{
    public class Startup
    {
        //簡単のための省略

        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddMvc();

            // コンフィグから接続文字列を読み込み、IDbConnectionのインスタンスとして
            // SqliteConnectionを生成するように定義
            services.AddScoped<IDbConnection>(
               provider=> new SqliteConnection(
                   Configuration.GetConnectionString("DefaultConnection")));

            // IDogServiceのインスタンスとして、DogServiceを生成するように定義
            services.AddScoped<IDogService, DogService>();
        }

        //簡単のための省略
    }
}

以上の変更を終えたら、再度、「dotnet run」して、ウェブプログラムを起動し、http://localhost:5000/Home/CountDogsにアクセスしてください。これまでと同じように動作することがわかるはずです。

以上を行うことで、HomeControllerからデータベースへの接続処理を切り離し、独立してテストしやすいような構成に変更することができました。

Return Top