.NET Coreで幾つかのデータベースアクセスを試してみる（SQL Server編）


私は普段Macで開発しているのでC#を利用した開発には可能な限り.NET Coreを利用したいのです。が、一部互換性のない部分も有るようなので自分が利用する範囲での動作確認をしてみます。またpackage.jsonファイルの記述方法等は明示的に紹介されたソースが無いためメモしておきます。

試す接続方法は、

* ADO.NET
* Entity Framework
* Dapper

の３つ。性質は、

* ADO.NETはいちおう最も低レベルなAPIですが、高速。
* Entity FrameworkはいわゆるO/Rマッパで、高レベルなAPIですが、相対的に遅い。
* DapperはMicro ORMと言われる部類のもので、中レベルなAPIですが、比較的高速（ADOに近い）

と言われています。

##前提条件

* Mac OS El Capitan（Sierraでも動きます）
* .NET Coreインストール済（dotnetコマンドが利用できるようにしておく）
* Sql-cli（Macで動くnpmでインストールできるSQL ServerのCLIツール）
* Visual Studio Code（別になんでもいい）
* SQL Server（Docker使うかWindows上に構築済）

##データベースの準備

SQL Server上にテスト用のデータベースとテーブルを作成して、データを追加しておきます。

* サーバ名: 192.168.77.127（各自の環境に合わせてどうぞ）
* ログイン: sa
* パスワード: P@ssw0rd!（仮置き）
* データベース名: sample
* テーブル名: members

操作はSql-cliのmssqlコマンドを利用して行います。
なおMac上にDocker for Macを利用してSQL Serberを立てる方法については[こちら](http://qiita.com/zaburo/items/eca05a959b8157f960f5)をご覧ください。

なお外部にSQL Serverを立てた場合は外部からのアクセスを許可して、事前にアクセス可能か確認してください。

###ログイン

まずはログインしてみます。Dockerを利用してローカルにSQL Serverがある場合はlocalhostとなりますが、他のWindows上にある場合はサーバ名（IP）を別途指定します。

```bash
mssql -s 192.168.77.129 -u sa -p P@ssw0rd!
```

###CREATE DATABASE

ログインできたらテスト用のデータベースを作成します。ここではsampleという名前にしました。
無事データベースが作成できたらuseにて利用するデータベースをsampleに切り替えます。

```sql
create database sample;

use sample
```

###CREATE TABLE

データベースが出来たので、それ上にテーブルを作成します。ここではmembersという名前にしました。
なお、EntiryFrameworkで利用する場合はprimary keyが必要となります。

```sql
create table members \
( \
    id int identity(1,1) not null, \
    name nvarchar(128), \
    email nvarchar(128), \
    constraint pk_members primary key (id) \
);
```

>mssqlコマンドでは改行がある場合は明示的に\で示す必要があるようです。

###INSERT

テーブルができたら、簡単なデータを挿入しておきましょう。

insert into members(name,email) values('hoge','hoge@hoge.com');\
insert into members(name,email) values('foo','foo@foo.com');

###SELECT

正しく挿入されているか確認します。

```sql
select * from members;
id  name  email
--  ----  -------------
1   hoge  hoge@hoge.com
2   foo   foo@foo.com

2 row(s) returned
```

問題ないようです。

##実装する

ASP.NETプロジェクト等を利用したいところですが、データベースコネクト以外のコードが複雑になるため、ここではコンソールアプリケーションの雛形を利用してみます。

作業用のディレクトリを作成し、dotnet newで雛形を生成します。
問題なければpackage.jsonとProgram.csが生成されるはずです。

```bash
mkdir test
cd test
dotnet new
```

>dotnet new -t web とすることでASP.NET MVCの雛形を生成することもできます。

##ADO.NET

では、ADO.NETを利用した接続を見てみます。

なお、執筆時点で.NET Framework上で利用できる全ての機能が利用できるわけではないようです（現時点でDataSet等が移植されていない）。

まずはpackage.jsonに依存しているライブラリを追加し、dotnet restoreします。

###package.json

```diff
{
  "version": "1.0.0-*",
  "buildOptions": {
    "debugType": "portable",
    "emitEntryPoint": true
  },
  "dependencies": {},
  "frameworks": {
    "netcoreapp1.0": {
      "dependencies": {
        "Microsoft.NETCore.App": {
          "type": "platform",
          "version": "1.1.0"
        },
+       "System.Data.SqlClient": "*"
      },
      "imports": "dnxcore50"
    }
  }
}
```

###Program.cs

では実装してみます。

ここではステップを確認するためにusingステートメントを利用していません。
また、接続文字列などは実際の実装ではappSetting.json等から取得するべきですが、ここでは静的に記述します。
さらにtry{}catch{}等も行いません。

```csharp
using System;
using System.Data.SqlClient;

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            //connection
            SqlConnection connection = new SqlConnection();
            connection.ConnectionString = @"Data Source=192.168.77.129;Initial Catalog=sample;User Id=sa;Password=P@ssw0rd!;";

            //open
            connection.Open();

            //commmand
            var command = connection.CreateCommand();
            command.CommandText = @"select * from members";

            //reader
            var reader = command.ExecuteReader();

            //loop & write
            while(reader.Read() == true)
            {
                Console.WriteLine(reader["id"]+" "+reader["name"]+" "+reader["email"]);
            }

            //dispose reader
            reader.Dispose();
            //dispose command
            command.Dispose();
            //close connection
            connection.Close();
            
        }
    }
}
```

とりあえずは動くはず。

###usingステートメントとtry catchを入れてみる

こだわればきりがないですが、下記のような感じでしょうか。

```csharp
using System;
using System.Data.SqlClient;

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            string constr = @"Data Source=192.168.77.129;Initial Catalog=sample;User Id=sa;Password=P@ssw0rd!;";

            //connection
            using(var connection = new SqlConnection(constr))
            {
                //command
                using(var command = connection.CreateCommand())
                {

                    try{
                        connection.Open();

                        command.CommandText = @"select * from members";

                        //reader
                        using(var reader = command.ExecuteReader())
                        {
                            //loop
                            while(reader.Read() == true)
                            {
                                Console.WriteLine(reader["id"]+" "+reader["name"]+" "+reader["email"]);
                            }
                        }
                    }
                    catch(Exception e)
                    {
                        Console.WriteLine(e.Message);
                        throw;
                    }
                    finally
                    {
                        connection.Close();
                    }
                }

            }
        }
    }
}
```

##Entiry Framework

つづいてEntiry Frameworkを利用してみます。
まずはpackage.jsonに依存関係を追加します。

なお、最新バージョンは常に変動するのでNuGet等のページで最新バージョン番号を確認して見てください。
書き換えたら忘れずにdonet restoreを実行します。

###package.json

```diff
{
  "version": "1.0.0-*",
  "buildOptions": {
    "debugType": "portable",
    "emitEntryPoint": true
  },
  "dependencies": {},
  "frameworks": {
    "netcoreapp1.0": {
      "dependencies": {
        "Microsoft.NETCore.App": {
          "type": "platform",
          "version": "1.1.0"
        },
+       "Microsoft.EntityFrameworkCore": "1.0.0",
+       "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0"
      },
      "imports": "dnxcore50"
    }
  }
}
```

###Program.cs

続いて実装です。特に難しいところはありません。
Entiry FrameworkではModelクラスとContextクラスの定義が必要となります。

```csharp
using System;
using System.Linq;
using Microsoft.EntityFrameworkCore;

namespace ConsoleApplication
{
    //data
    public class Member
    {
        public int id {get;set;}
        public string name {get;set;}
        public string email {get;set;}
    }

    //context
    public class MyContext:DbContext
    {
        public DbSet<Member> Members{get;set;}

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
                => optionsBuilder.UseSqlServer(@"server=192.168.77.129;database=sample;uid=sa;pwd=P@ssw0rd!;");
    }

    //main
    public class Program
    {
        public static void Main(string[] args)
        {
            using(var context = new MyContext())
            {
                //select
                var ret = context.Members
                    .ToList();

                foreach(var x in ret)
                {
                    Console.WriteLine($"{x.name} {x.email}");
                }
            }
        }
    }
}
```

##Dapper

最後にDapperです。DapperはSQLは自分で書かないとダメですがそれ以外はよしなにやってくれます。
まずはpackage.json。変更したら忘れずにdotnet restoreを実行します。

###package.json

```diff
{
  "version": "1.0.0-*",
  "buildOptions": {
    "debugType": "portable",
    "emitEntryPoint": true
  },
  "dependencies": {},
  "frameworks": {
    "netcoreapp1.1": {
      "dependencies": {
        "Microsoft.NETCore.App": {
          "type": "platform",
          "version": "1.1.0"
        },
+       "Dapper": "1.50.2",
+       "System.Data.SqlClient": "*"
      },
      "imports": "dnxcore50"
    }
  }
}
```

###Program.cs

実装です。これも特に気にするところはありません。Contextクラスは必要ありませんが、Modelクラスは必要です（必要ないパターンもあります）。

```csharp
using System;
using Dapper;
using System.Data.SqlClient;

namespace ConsoleApplication
{
    public class Program
    {
        //model
        public class Member
        {
            public int id {get;set;}
            public string name {get;set;}
            public string email {get;set;}
        }

        //main
        public static void Main(string[] args)
        {
            using(var connection = new SqlConnection(@"Data Source=192.168.77.129;Initial Catalog=sample;User Id=sa;Password=P@ssw0rd!;"))
            {
                connection.Open();

                var members = connection.Query<Member>("select * from members");

                foreach(var member in members)
                {
                    Console.WriteLine(member.name + " " + member.email);
                }
            }
        }
    }
}
```

以上簡単ですが.NET Core上でのデータベースアクセス方法を見てみました。

##その他

.NET CoerからMySQLを利用したEntiryFrameworkおよびDapperの情報は下記にあります。

* [MySQL + EntiryFramework](http://qiita.com/zaburo/items/a167c9ad94fcea625c56)
* [MySQL + Dapper](http://qiita.com/zaburo/items/239612ab632bcb5ab18c)