フォームアプリ備忘録＿C#で簡易CRUD機能を実装！

### FormApplication CRUD機能
C#とSQLite3を用いて、簡単なCRUD機能を実装しています。
記述形式や冗長なコードだらけですが、スキルアップに伴って修正していきます。

今回は各機能ごと分解して説明していきます。（初）
以下コードになります。

## Form 骨格
```C#:Form2.cs
using System;
using System.Data;
using System.Data.SQLite;
using System.Windows.Forms;

namespace LoginCRUD
{
    public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();
        }

        private void sarchButton_Click(object sender, EventArgs e)
        {
            searchSqlChk();
            gridDisplay();
        }

        private void addButton_Click(object sender, EventArgs e)
        {
            if (addSql())
            {
                gridDisplay();
                txtBoxAndChkBoxClear();
            }
        }

        private void updateButton_Click(object sender, EventArgs e)
        {
            if (updateSql())
            {
                gridDisplay();
                txtBoxAndChkBoxClear();
            }
        }

        private void deleteButton_Click(object sender, EventArgs e)
        {
            if (delSql())
            {
                gridDisplay();
            }
        }

        private void clearButton_Click(object sender, EventArgs e)
        {
            txtBoxAndChkBoxClear();
        }
        private void logOutButton_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Logout...");
            var form1 = new Form1();
            form1.Show();
            this.Hide();
        }
```
まずはそれぞれのボタンをクリックした際のイベントをわかりやすく？書いています。
基本となるのは __gridDisplay()__ で、全テーブル情報を抽出しDataGridViewに格納します。

__searchSqlChk()__ ではselect文を文字列連結で構成します。
__addSql()__ , __updateSql()__ そして __delSql()__ については、処理に成功した場合のみ、
全テーブル情報を表示します。これによって追加・更新・削除後の結果がすぐに表示されます。

### gridDisplay()
```C#
private bool gridDisplay()
        {
            Person p = new Person();

            p.Firstname = fstNameTextBox.Text;
            p.Lastname = lstNameTextBox.Text;
            p.Address = addressTextBox.Text;

            Database d = new Database();
            string cmdTxt = d.Selectcmdtxt;
            if (fstNameCheckBox.Checked)
            {
                cmdTxt += " AND fstname =" + "'" + p.Firstname + "'";
            }
            if (lstNameCheckBox.Checked)
            {
                cmdTxt += " AND lstname =" + "'" + p.Lastname + "'";
            }
            if (addressCheckBox.Checked)
            {
                cmdTxt += " AND address =" + "'" + p.Address + "'";
            }

            using (SQLiteConnection con = new SQLiteConnection(d.Datasrctxt))
            {
                con.Open();
                SQLiteDataAdapter adapter = new SQLiteDataAdapter(cmdTxt, con);
                DataTable dt = new DataTable();
                adapter.Fill(dt);
                dataGridView1.DataSource = dt;
            }
            return true;
        }

```
...承知しております。このコードは考え中なので、 __真似しないでください ( 'ω' )__ 
難しいと感じたのが条件式で、チェックボックスの状態による条件変化につまずきました。
つまづいたというかこけてそのまま突き進んでしまったのでこんなコードになっています。

これまではselect文の結果をlistBoxへの格納しか行わなかったため、
また再度SQLiteDataAdapter等の理解を深めてから修正したいと思います。

### searchSqlChk()
```C#
        private bool searchSqlChk()
        {
            Person p = new Person();
            p.Firstname = fstNameTextBox.Text;
            p.Lastname = lstNameTextBox.Text;
            p.Address = addressTextBox.Text;
            if (fstNameCheckBox.Checked)
            {
                if (p.Firstname == "")
                {
                    MessageBox.Show("Firstname is empty.");
                    return false;
                }
                return true;
            }
            if (lstNameCheckBox.Checked)
            {
                if (p.Lastname == "")
                {
                    MessageBox.Show("Lastname is empty.");
                    return false;
                }
                return true;
            }
            if (addressCheckBox.Checked)
            {
                if (p.Address == "")
                {
                    MessageBox.Show("Address is empty.");
                    return false;
                }
                return true;
            }
            return true;
        }
```
条件式を繋げて ```if (p.Firstname == "" || p.LastName == "" )``` のように
記述しないのは、条件式を繋げまくる癖を予防するためです。
Udemyとインターネットで学習したのでやってみました。

### addSql()
```C#
        private bool addSql()
        {
            Person p = new Person();
            p.Firstname = fstNameTextBox.Text;
            p.Lastname = lstNameTextBox.Text;
            p.Age = (int)ageNumericUpDown.Value;
            p.Address = addressTextBox.Text;
            p.Phonenum = phoneNumberTextBox.Text;

            Database d = new Database();
            if (!d.paramCheckOk(p.Firstname, p.Lastname, p.Age, p.Address, p.Phonenum))
            {
                return false;
            }

            try
            {
                using (SQLiteConnection con = new SQLiteConnection(d.Datasrctxt))
                {
                    con.Open();
                    var tran = con.BeginTransaction();
                    string cmdTxt = d.Insertcmdtxt;
                    SQLiteCommand cmd = new SQLiteCommand(cmdTxt, con);

                    cmd.Parameters.Add(new SQLiteParameter(":fstname", p.Firstname));
                    cmd.Parameters.Add(new SQLiteParameter(":lstname", p.Lastname));
                    cmd.Parameters.Add(new SQLiteParameter(":age", p.Age));
                    cmd.Parameters.Add(new SQLiteParameter(":address", p.Address));
                    cmd.Parameters.Add(new SQLiteParameter(":phonenum", p.Phonenum));

                    if (cmd.ExecuteNonQuery() != 1)
                    {
                        MessageBox.Show("<Query Error> rollback..");
                        tran.Rollback();
                        return false;
                    }
                    tran.Commit();

                    MessageBox.Show("USER：" + p.Firstname + " " + p.Lastname + " added!");
                    return true;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return false;
            }
        }
```
using()とtarnsaction()を初めて使ってみました。
using()を使う前はtry catch fainallyを使っていましたが、
fainallyでclose()やdispose()をする必要がなくなり、管理性・可読性が向上しました。
（難しそうな言葉を使いたかった。(o^―^o)
addされたユーザのIDはautoincrementで自動的に設定されます。(かつprimaryの値)


### delSql()
```C#
private bool delSql()
        {
            if (dataGridView1.CurrentRow == null)
            {
                MessageBox.Show("No columns are selected.");
                return false;
            }

            try
            {
                Database d = new Database();
                using (SQLiteConnection con = new SQLiteConnection(d.Datasrctxt))
                {
                    con.Open();
                    var tran = con.BeginTransaction();
                    string cmdTxt = d.Deletecmdtxt;
                    string delIndex = dataGridView1.CurrentRow.Cells[0].Value.ToString();
                    SQLiteCommand cmd = new SQLiteCommand(cmdTxt, con);
                    cmd.Parameters.Add(new SQLiteParameter(":id", delIndex));

                    if (cmd.ExecuteNonQuery() != 1)
                    {
                        MessageBox.Show("<Query Error> rollback..");
                        tran.Rollback();
                        return false;
                    }
                    tran.Commit();
                    MessageBox.Show("deleted!");
                    return true;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return false;
            }
        }
```
確認画面を設けていないので、成功したら即消えます。
insert文と構成が似ているので理解しやすいと思います。

注意した点は削除の条件式に用いる値で、今回はprimaryなIDを条件式に使用しました。
選択された行の番号のCell[0]がユーザIDにあたるので、それをdelIndexに格納しました。

### updateSql()
```C#
private bool updateSql()
        {
            if (dataGridView1.CurrentRow == null)
            {
                MessageBox.Show("No columns are selected.");
                return false;
            }

            Person p = new Person();
            p.Firstname = fstNameTextBox.Text;
            p.Lastname = lstNameTextBox.Text;
            p.Age = (int)ageNumericUpDown.Value;
            p.Address = addressTextBox.Text;
            p.Phonenum = phoneNumberTextBox.Text;
            string updateIndex = dataGridView1.CurrentRow.Cells[0].Value.ToString();

            Database d = new Database();
            if (!d.paramCheckOk(p.Firstname, p.Lastname, p.Age, p.Address, p.Phonenum))
            {
                return false;
            }

            try
            {
                using (SQLiteConnection con = new SQLiteConnection(d.Datasrctxt))
                {
                    con.Open();
                    var tran = con.BeginTransaction();
                    string cmdTxt = d.Updatecmdtxt;
                    SQLiteCommand cmd = new SQLiteCommand(cmdTxt, con);
                    cmd.Parameters.Add(new SQLiteParameter(":id", updateIndex));
                    cmd.Parameters.Add(new SQLiteParameter(":fstname", p.Firstname));
                    cmd.Parameters.Add(new SQLiteParameter(":lstname", p.Lastname));
                    cmd.Parameters.Add(new SQLiteParameter(":age", p.Age));
                    cmd.Parameters.Add(new SQLiteParameter(":address", p.Address));
                    cmd.Parameters.Add(new SQLiteParameter(":phonenum", p.Phonenum));

                    if (cmd.ExecuteNonQuery() != 1)
                    {
                        MessageBox.Show("<Query Error> rollback..");
                        tran.Rollback();
                        return false;
                    }
                    tran.Commit();
                    MessageBox.Show("updated!");
                    return true;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return false;
            }
        }
```
空白については制限しています。
ID以外はすべて更新できてしまいます。

### txtBoxAndChkBoxClear()
```C#
        public void txtBoxAndChkBoxClear()
        {
            fstNameTextBox.Text = string.Empty;
            lstNameTextBox.Text = string.Empty;
            ageNumericUpDown.Value = 30;
            addressTextBox.Text = string.Empty;
            phoneNumberTextBox.Text = string.Empty;
            fstNameCheckBox.Checked = false;
            lstNameCheckBox.Checked = false;
            addressCheckBox.Checked = false;
        }


        public string assembleSql(string cmdtext)
        {
            if (fstNameCheckBox.Checked)
            {
                cmdtext += " AND fstname = :fstname";
            }
            if (lstNameCheckBox.Checked)
            {
                cmdtext += " AND lstname = :lstname";
            }
            if (addressCheckBox.Checked)
            {
                cmdtext += " AND address = :address";
            }
            return cmdtext;
        }
    }
}
```
textBoxとcheckBoxの値をきれいにします。
ボタン以外にもadd,updateの後に実行しています。


最後にクラスです。
### ClassPerson
```C#
namespace LoginCRUD
{
    internal class Person
    {
        //プロパティ　属性
        public int Id { get; set; }
        public string Firstname { get; set; }
        public string Lastname { get; set; }
        public int Age { get; set; }
        public string Address { get; set; }
        public string Phonenum { get; set; }


        //コンストラクター
        public Person()
        {
            this.Id = 0;
            this.Firstname = "";
            this.Lastname = "";
            this.Age = 0;
            this.Address = "";
            this.Phonenum = "";
        }
    }
}
```

### ClassDatabase
```C#
using System.Windows.Forms;

namespace LoginCRUD
{
    internal class Database
    {

        public string Datasrctxt { get; set; }
        public string Selectcmdtxt { get; set; }
        public string Insertcmdtxt { get; set; }
        public string Deletecmdtxt { get; set; }
        public string Updatecmdtxt { get; set; }


        public Database()
        {
            this.Datasrctxt = @"Data source=C:\Users\OOO\source\repos\WindowsFormsApp1\DB\RDBMS_DB";
            this.Selectcmdtxt = @"select * from personal where '1'='1'";
            this.Insertcmdtxt = @"INSERT INTO personal(fstname,lstname,age,address,phonenum) values(:fstname,:lstname,:age,:address,:phonenum)";
            this.Deletecmdtxt = @"DELETE FROM personal WHERE id =:id";
            this.Updatecmdtxt = @"UPDATE personal SET fstname=:fstname,lstname=:lstname,age=:age,address=:address,phonenum=:phonenum WHERE id=:id";
        }

        public bool paramCheckOk(string fst, string lst, int age, string addr, string num)
        {
            if (fst == "" || lst == "" || addr == "" || num == "")
            {
                MessageBox.Show("Empty value exists.");
                return false;
            }
            return true;
        }

    }
}
```
あ、条件式４つ繋げてるところありましたわね。（管理性可読性etc...

### まとめ
動作したので良かったですが、
データベース処理において冗長なコード、脆弱なコードが散見されるのでまた修正していきたいと思います。
あわせてC#のコーディング規則、英語のつづりについても学習していきたいと思います。

見ていただいてありがとうございました。