2016年4月17日日曜日

EntityFrameWorkについて

最近、C#でEntityFrameWorkを使ってアプリを作ってみてよと言われました。研修みたいなものです。

でも、Web上にEntityFrameWorkの日本語の情報が少なすぎる。英語の情報を読んでなんとかアプリ自体は作りあげましたが、大変苦労しました。

そこで、このポストにC#. EntityFrameWorkを使って簡単なデータベースを使うアプリを作る方法を記しておきたいと思います。おなじようにEntityFrameWorkの日本語の情報を探す人達の役に立てばと思います。

さて、今回はWpfアプリで、データベースのデータを表示・更新・削除ができるような簡単なアプリを作ります。



EntityFrameWorkとプロジェクトの準備


まずはVisual studioを使ってプロジェクトを作ります。僕はVisual Studio 2015を使っています。
クリックで高画質版。以下同様。

今回はあえてWPFアプリとして作ります。名前は「WpfApplicationDBpractice」としました。
できたら「OK」を押してプロジェクトを作成します。

OKを押すとVisual studioくんががんばってプロジェクトを作成します。
ちょっと長いのでしばらく待ちます。

Visual studioくんの準備が終わるとWPFなどのコード編集画面に行くはずです。

ここでEntityFrameWorkを自分のVisual Studioにインストールします。
Visual studioの右側にソリューションエクスプロ―ラーというものがでているはずです。
ここに表示されている「WpfApplicationDBpractice」を右クリックします。



すると上のような選択画面がでるはずです。ここで「NuGetパッケージの管理(N)」を探してクリックしてください。



するとこういう画面がでます。上記のように検索窓に「EntityFrameWork」と入力して検索してください。するとEntityFrameWorkがヒットするはずなのでこれをインストールします。2016年現在の最新安定版はver6.1.3らしいのでこれをインストールします。

インストールできたら以下のようにソリューションエクスプロ―ラの「参照」をチェックします。
こんなふうにEntityFrameWorkとEntityFrameWork.SqlServerが追加されているはずです。
万が一、追加されていない場合は「参照」を右クリックして参照に追加してください。

次にデータベースを追加します。


データベースの追加

データベースを使うアプリなのですから .mdf ファイル(データベース)を用意しないといけません。
右側のソリューションエクスプローラーで、プロジェクト名「WpfApplicationDBpractice」を右クリックします。

すると以下のような画面になるので「追加」を選択、さらに「新しい項目(W)」を選択します。





すると以下のような画面になります。いっぱいありますが、「サービスペースのデータベース」を探してください。見つけたらクリックします。




名前はなんでもいいです。僕はDatabase1.mdfという名前にしました。

右下の「追加」を押すとこのデータベースが追加されます。


こんな感じ

(追加ボタンをクリック時にSQL server expressをインストールしていないと使えませんよという趣旨のメッセージが表示されるかもしれません。そのときは以下のようにToolからExtentions and Updates...を探してクリックします。ただし以下の画像は英語版であり、日本語版では違う表記になっているので注意。


そしたら以下の画面になるはずなのでUpdatesからMicrosoft SQL Server Update for database...というような名前のものを探してupdateをクリックします。
Updateが終わるとDatebaseが追加できるようになるはずです。


そもそもMicrosoft SQL Server Update for databaseうんぬんが見つからないよ!という場合はおよらくSQL server expressがインストールされていないのでしょう。ネットで探してインストールしてください。)


次にデータベースの中身を作ります。中身を作らないとデータベースは何の書き込みも読み込みもできません。

まず、以下のように上の「表示(V)」をクリックします。
そしたらサーバーエクスプローラーをクリックします。


そうすると画面左に以下のようなサーバーエクスプローラーが追加されるのでクリック、展開したら「データ接続、「Database1.mdf」、「テーブル」を順にクリックして展開してください。



そしたら次は「テーブル」を右クリックしてください。「新しいテーブルの追加(T)」という項目があるのでクリックします。



そうすると以下のような画面になります。

今はIdだけしかありませんね。これにName(text)、Age(int)、Sex(nchar10)の列を追加します。

ちなみにName(名前)、Age(年齢)、Sex(性別)というデータを入れようと思っています。

上のようにできたらid columnのpropertyを以下のように変更します。

これをしないとデータ追加時にIdがちゃんと1づつ増えない(すなわちデータ追加時にエラーが発生する)ことになりますので注意。

(今回は関係ないですが「すでに作ってあるデータベース」になんらかの変更をし保存した場合、updateをしたあとにdesigner上に表示されているedmxのほうも右クリックしてupdateする必要があります。。。まあ今は関係ないので聞き流してOK。事後的にデータベースを更新したけどプログラムがきちんと動かないというときに思い出してください)

ちなみにTableの名前を変えたい場合、すぐ下のスクリプトペインを見てください。そこにこういう一節があると思います。
CREATE TABLE [dbo].[Table]
現在のテーブル名「Table」です。
このテーブル名を「tekitoTable」に変えたいばあい、ここを次のように変更します。
CREATE TABLE [dbo].[tekitoTable]
これでテーブル名の変更ができます。。。が面倒なので今回はデフォルトの「Table」というテーブル名でいきます。

できたら左上の「更新(U)」を押します。

押したら次のような画面がでます。
「データベースの更新(U)」を押してください。

押したら、とりあえずこれでデータベースは完成です。

きちんとテーブルができたかどうか確認します。左のサーバーエクスプローラーで「データ接続」を右クリック、「最新の状態に更新(F)」をクリックしてください。するとテーブルが追加されたことが確認できるはずです。



さっきつくったテーブル「Table」が追加されています。



EntityFrameWork


さて次はEntityFrameWorkです。

さっきと同じように「新しい項目を追加(N)」を探してクリック。今度は「ADO .NET Entity Data Model」を追加します。名前はデフォルトの「Model1」でいいでしょう。



そうしたら以下のような画面になります。「データべースからEF Designer」を選んでください。
選んだら「次へ(N)」をクリック。

すると以下のような画面になるはずです。
ここはそのまま次へをクリック。
ここでもしさっき作った.mdfファイルが表示されていないなら何か設定が間違っている可能性があります。

すると以下の画面になります。
チェックを入れられるものと入れられないものとがありますが、
できるだけ全部にチェックをいれてください。
でもたぶん「ビュー」と「ストアドプロシージャと関数」はチェックいれられない。
この2つ以外はチェックしてね。
できたら「完了(F)」をクリックします。

するとVisual studioくんががんばって必要なものを作ります。
ちょっと待ってあげてください。
完了すると「Model1.edmx [Diagram1]」に次のように表示されるはず。
表示されていればOKです。


WPFの外見を作り、DBと接続する


次は、WPFの外見を作り、DBと接続します。





<Window x:Class="WpfApplicationDBpractice.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplicationDBpractice"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid x:Name="dataGrid" HorizontalAlignment="Left" Margin="10,88,0,0" VerticalAlignment="Top" Width="497" Height="222" AlternatingRowBackground="SkyBlue">
        </DataGrid>
        <Button x:Name="Loadbutton" Content="接続・更新" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75" Click="Loadbutton_Click"/>
        <Button x:Name="Delbutton" Content="削除" HorizontalAlignment="Left" Margin="116,10,0,0" VerticalAlignment="Top" Width="75" Click="Delbutton_Click"/>
        <Button x:Name="Addbutton" Content="追加" HorizontalAlignment="Left" Margin="228,10,0,0" VerticalAlignment="Top" Width="75" Click="Addbutton_Click"/>
        <TextBox x:Name="NameBox" HorizontalAlignment="Left" Height="23" Margin="10,60,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
        <TextBox x:Name="AgeBox" HorizontalAlignment="Left" Height="23" Margin="163,60,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
        <ComboBox x:Name="SexBox" HorizontalAlignment="Left" Margin="334,59,0,0" VerticalAlignment="Top" Width="120">
            <ComboBoxItem Content="男" />
            <ComboBoxItem Content="女" />
        </ComboBox>
    </Grid>
</Window>


とりあえずWPFは上のような感じに作りました。

つぎにC#のコードを書きます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApplicationDBpractice
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Loadbutton_Click(object sender, RoutedEventArgs e)
        {
            using (var context = new Database1Entities1())
            {
                //この<Table>っていうのはさっき作ったデータベースのテーブル「Table」のことです
                //まぎらわしくごめん
                IList<Table> dataList = new List<Table>();

                dataList = context.Tables.ToList<Table>();
                dataGrid.ItemsSource = dataList;
                dataGrid.Items.Refresh();
            }
        }

        private void Delbutton_Click(object sender, RoutedEventArgs e)
        {
            using (var context = new Database1Entities1())
            {
                var projectionQuery = from s in context.Tables
                                      select s;

                var dataList = projectionQuery.ToList<Table>();
                int r = 0;
                if (!int.TryParse(NameBox.Text, out r))
                {
                    MessageBox.Show("削除するときは最初のボックスに数字だけいれてね");
                    return;
                }

                if (dataList.Where(s => s.Id == r).FirstOrDefault<Table>() != null)
                    context.Tables.Remove(dataList.Where(s => s.Id == r).FirstOrDefault<Table>());

                if (context.SaveChanges() == 1)
                {
                    MessageBox.Show("ちゃんと消えました", "Successful");
                }
                else
                {
                    MessageBox.Show("消えませんできた。", "Error while deleting", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        private void Addbutton_Click(object sender, RoutedEventArgs e)
        {
            using (var ctx = new Database1Entities1())
            {
                List<Table> newdata = new List<Table>();
                int ageparsed;
                if(!int.TryParse(AgeBox.Text, out ageparsed))
                {
                    MessageBox.Show("年齢は数字だけ入力してね");
                    return;
                }
                newdata.Add(new Table() { Name = NameBox.Text, Age = ageparsed, Sex = SexBox.Text});
                using (Database1Entities1 context = new Database1Entities1())
                {
                    context.Tables.AddRange(newdata);
                    if (context.SaveChanges() == 1)
                    {
                        MessageBox.Show("きちんと保存されました", "Successful");
                    }
                    else
                    {
                        MessageBox.Show("保存できませんでした", "Error while saving", MessageBoxButton.OK, MessageBoxImage.Error);
                    }
                }

            }
        }
    }
}

こんな感じで書きます。

そしたら右のソリューションエクスプローラーのDatabase1.mdfをクリックします。

そしたらプロパティの「出力プロパティにコピー」を「新しい場合はコピーする」に変えてください。

できたら完成です。

さっき上でつくったプログラムのプロジェクトをダウンロードできるようにしておきました。
詳細が見たい場合はつかってください。
https://www.dropbox.com/s/vktae44x9zo85ug/WpfApplicationDBpractice.zip?dl=0


使い方
保存:「名前」「年齢」「性別」を入力してから「追加」ボタンを押してください。さらに「接続・更新」を押してください。データがDBに保存されます。
データ削除:「名前」の入力欄に消したいデータのIDを入力して「削除」ボタンを押してください。さらに「接続・更新」を押してください。そのIDのデータが消去されます。