前回は「C#からデータベースに接続する」をやりました。

今回は、業務系開発の設計でとても大事な考え方。データベースの「表」と、C#の「クラス」を対応させるという発想です。これが分かると、プログラムがぐっと整理されます。

表の1行を、クラスの1インスタンスに対応させる。これが、データを扱う基本の設計です。

バラバラのデータは、扱いにくい

前回、データベースから顧客を取り出すとき、こう書きました。

while (reader.Read())
{
    string name = reader["名前"].ToString();
    int age = (int)reader["年齢"];
    Console.WriteLine(name + ":" + age + "歳");
}

名前は name、年齢は age と、バラバラの変数で受け取っています。1人分なら、これでもいいです。

でも、この顧客データを、別のメソッドに渡したいときは? 計算に使いたいときは? 名前と年齢を、いちいち2つ別々に持ち回ることになります。第18回でやった「バラバラの変数は扱いにくい」という、あの問題が、また出てきます。

表の構造は、クラスとそっくり

ここで、前々回(第33回)の表を、もう一度見てみましょう。

顧客テーブル
┌──────┬────────┬─────┐
│  ID  │  名前  │ 年齢 │
├──────┼────────┼─────┤
│  1   │  山田  │  20  │
└──────┴────────┴─────┘

「ID・名前・年齢を持つ」。この構造、第18回で作ったクラスと、そっくりですよね。

だったら、表に対応するクラスを作ればいいんです。

class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

「顧客テーブル」に対応する「Customer クラス」。表のが、クラスのプロパティに対応します。

  • 表の ID 列 → クラスの Id プロパティ
  • 表の 名前 列 → クラスの Name プロパティ
  • 表の 年齢 列 → クラスの Age プロパティ

表の1行を、1インスタンスにする

そして、表の1行を、クラスの1インスタンスとして受け取ります。

List<Customer> customers = new List<Customer>();

while (reader.Read())
{
    Customer customer = new Customer();         // 1行 = 1インスタンス
    customer.Id = (int)reader["ID"];
    customer.Name = reader["名前"].ToString();
    customer.Age = (int)reader["年齢"];

    customers.Add(customer);                    // リストにためる
}

表の1行を読むたびに、Customer のインスタンスを1つ作って、データを詰める。そして、List にためていく。

これで、データベースの顧客データが、C#の「もの(オブジェクト)」の一覧になりました。バラバラの変数ではなく、「顧客」という意味のあるまとまりです。

まとまっていると、何が嬉しいか

クラスにまとめると、その後の扱いが、劇的にラクになります。

第29回でやったLINQが、そのまま使えます。

// 30歳以上の顧客の名前を取り出す
var names = customers
    .Where(c => c.Age >= 30)
    .Select(c => c.Name);

メソッドに渡すのも、一言です。

SendMail(customer);   // 顧客まるごと、1つで渡せる

バラバラの変数なら SendMail(name, age, id, ...) と全部渡す必要がありますが、クラスにまとめてあれば、customer 1つで済みます。

「データを、意味のあるまとまり(もの)として扱う」。これが、オブジェクト指向と、データベースが出会うところです。連載でやってきたクラス(第18回〜)と、データベース(第33回〜)が、ここで1つにつながります。

この対応づけには、名前がある

「表とクラスを対応させる」この作業は、実務でとても頻繁にやります。あまりに頻繁なので、それを自動でやってくれる道具まであります。

ORM(オーアールエム)」と呼ばれる仕組みです。C#では「Entity Framework」が有名です。ORMを使うと、前回書いたような接続やSQLの多くを、自分で書かずに済みます。

// ORM を使うと、こんなイメージで書ける
var customers = db.Customers.Where(c => c.Age >= 30).ToList();

SQLを書かずに、LINQのような書き方で、データベースを操作できる。表とクラスの対応も、自動でやってくれます。

ただ、最初は自動の道具に頼らず、前回のように手で書いてみるのがおすすめです。中で何が起きているかを知っていると、ORMを使うときも、問題が起きたときに対応できます。「裏で何が動いているか」を知る人は、強いです。

つまずきポイント

初心者が見落としがちなのが、「なぜ、わざわざクラスにするのか」という点です。

reader["名前"] で直接取れるのに、なぜCustomerクラスを作るの?」と。

理由は、プログラムが大きくなったときに効いてくるからです。小さなプログラムなら、バラバラの変数でも動きます。でも、顧客データをあちこちで使い回す本格的なシステムでは、「顧客」という意味のまとまりがあるかどうかで、コードの整理しやすさがまったく変わります。

「データに、意味のある形を与える」。これは、長く保守できるプログラムを書くための、大事な習慣です。

まとめ

データベースの表と、C#のクラスを対応させるのが、データ設計の基本。

class Customer            // 表に対応するクラス
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

// 表の1行を、1インスタンスにして、List にためる
  • 表の列 → クラスのプロパティ、表の1行 → 1インスタンス
  • まとめると、LINQやメソッドで扱いやすくなる
  • この対応を自動化する道具が「ORM」(Entity Framework など)
  • 最初は手で書いて、中の仕組みを知っておくとよい

クラス・継承・データベース——連載でやってきたことが、業務系開発の現場で、こうやって1つにつながっていきます。