C#.Net にてCRUDを実装してみた

# 初めに

c#を触り始めて、とりあえずCRUDを実装することが出来たので公開します。

Railsとは違ってフォルダ構成など自由度が高いし

データの取ってきかたとかよくわかっていない部分が多いので

もっとこうしたら良いなどあれば指摘してほしいです。


# github

実装したフォルダはgithubに投稿しています。

https://github.com/Sibakeny/C-Crud

# フォルダ構成

フォルダ構成に関しては

```
├── Domain
│   ├── Entities
│   └── Repositories
├── Infrastructure
├── Winform
│   ├── ViewModel
│   └── Views

```

DomainのEntitiesでPostクラスを定義している。
このPostクラスは投稿、表示する際のひな型として使う。

DomainのRepositoriesではIPostRepositoryというインターフェイスを実装していて

Save Update GetAllなどのメソッドを定義している


InfrastructureではSQLServerへの接続関係の処理を書いてる

Winformのviewsではフォームを設定し、ここでは基本的に処理は書かないようにしている。

具体的な処理はviewModelに書いており、viewで使う値はviewModelのインスタンス変数として設定し、viewModelから取り出して使う形にしている。


# 実装内容


Homeのコード

```c#

using MyCRUD.Winform.ViewModel;
using MyCRUD.Winform.Views;
using System;
using System.Windows.Forms;

namespace MyCRUD
{
    public partial class Form1 : Form
    {

        private static Form1 _form1Instance;

        public static Form1 Form1Instance
        {
            get
            {
                return _form1Instance;
            }
            set
            {
                _form1Instance = value;
            }
        }

        public Panel PanelContainer
        {
            get
            {
                return this.panelContainer;
            }
        }

        private HomeViewModel _viewModel
            = new HomeViewModel();
        public Form1()
        {
            InitializeComponent();

            Form1.Form1Instance = this;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            NewPost f = new NewPost();
            changeForm(f);
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            
        }

        

        

        public void changeForm(object form)
        {
            if (this.panelContainer.Controls.Count > 0)
            {
                this.panelContainer.Controls.RemoveAt(0);
            }
            Form fh = form as Form;
            fh.TopLevel = false;
            fh.Dock = DockStyle.Fill;
            this.panelContainer.Controls.Add(fh);
            this.panelContainer.Tag = fh;
            fh.Show();
        }

        private void btnPosts_Click(object sender, EventArgs e)
        {
            Posts f = new Posts();
            changeForm(f);
        }
    }
}
```

上から順番に public static でForm1Instanceを定義して

コンストラクタにて

```c#

Form1.Form1Instance = this;
```

とすることで外部からフォームにアクセスできるようにしている。


メイン画面のパネルに投稿画面、投稿表示画面に表示するためにchangeFormメソッドを作っています。


外部のフォームからchangeFormを実行する場合はForm.Form1Instance.changeFormのように呼びだします。


# 投稿作成

```c#
using MyCRUD.Winform.ViewModel;
using System;
using System.Windows.Forms;

namespace MyCRUD.Winform.Views
{
    public partial class NewPost : Form
    {
        private NewPostViewModel _viewModel
            = new NewPostViewModel();
        public NewPost()
        {
            InitializeComponent();

            titleTextBox.DataBindings.Add("Text", _viewModel, nameof(_viewModel.TitleText));
            bodyTextBox.DataBindings.Add("Text", _viewModel, nameof(_viewModel.BodyText));


        }

        private void postButton_Click(object sender, EventArgs e)
        {
            try
            {
                _viewModel.Save();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

            MessageBox.Show("保存しました。");

            this.Close();
        }
    }
}
```

保存処理はviewModelで行います。

title,bodyをviewModelにて使えるようにviewModelのプロパティのTitleText,BodyTextを設定します。

NewPostViewModelでは

```c#
using MyCRUD.Domain.Entities;
using MyCRUD.Domain.Repositories;
using MyCRUD.Infrastructure;

namespace MyCRUD.Winform.ViewModel
{
    class NewPostViewModel
    {
        private IPostRepository _post;

        public NewPostViewModel()
            :this(new PostSQLite())
        {

        }

        public NewPostViewModel(
            IPostRepository post)
        {
            _post = post;
        }

        public string TitleText { get; set; }
        public string BodyText { get; set; }

        public void Save()
        {
            PostEntity entity = new PostEntity(
                TitleText,
                BodyText
            );

            _post.Save(entity);

        }
    }
}

```

引数なしでインスタンスが作成されたときは

new PostSQLiteが引数に渡されます。


IPostRepositoryではSaveメソッドが定義されているので


_post.Saveメソッドの実行が出来ます。

Saveメソッド自体はPostSQLiteクラスで定義されていて

PostEntityをもとにデータの作成を行います。


# SQL

sql関係に関してはInfrastructureフォルダで書いていて

SQLHeperにてexecuteメソッドを実装

```c#
internal static void Execute(
            string sql,
            SqlParameter[] parameters)
        {
            using (var connection = new SqlConnection(ConnectionString))
            using (var command = new SqlCommand(sql, connection))
            {
                connection.Open();

                if (parameters != null)
                {
                    command.Parameters.AddRange(parameters);
                }

                command.ExecuteNonQuery();
            }
            
        }
```

このexecuteメソッドをもとにsave処理を

```c#
public void Save(PostEntity post)
        {
            string insert = @"
INSERT INTO Posts
(title,body)
VALUES
(@title,@body)
";

            var args = new List<SqlParameter>
            {
                new SqlParameter("@title", post.Title),
                new SqlParameter("@body", post.Body)
            };

            SQLHelper.Execute(insert, args.ToArray());
        }
```

このように書いています。

これでPostEntityをもとに保存処理が行われます。



# 終わりに

大体こんな感じでCRUDを実装してみました。


実装の仕方に関してはUdemyの

C#でドメイン駆動開発とテスト駆動開発を使って保守性の高いプログラミングをする方法
https://www.udemy.com/course/domain-1/


を参考にしました。

ドメイン駆動自体全然理解出来てないのでおそらく実装の仕方がだいぶ変だと思います。


ドメイン駆動に関して少し調べてみたのですが結構情報少ないんですよね、

それにドメイン駆動以外ではどんな感じに実装するんでしょうか.....


終わり:sunny: