1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Azure App Service に ASP.NET MVC をデプロイして PostgreSQL を使って簡易 TODO リストを作ってみた

Posted at

Azure の初回登録時の無料プランで試した内容です、Azure App Service は無料で使えたようですが、Azure Database for PostgreSQL は有料(初回登録時にもらえる30000円のクレジットで余裕の範囲内)のようでした。

初心者で調べながらやっただけですのでご了承ください。

Azure にリソースグループを作成

「リソースグループ」をクリック

image.png

「作成」をクリック

image-1.png

リソースグループに「SimpleTodoList」と入力して、リージョンを「Japan East」にして「確認および作成」をクリック

image-2.png

「作成」をクリック

image-3.png

表示されるまで数十秒ラグがあったが、「SimpleTodoList」ができていることを確認

image-4.png

PostgreSQL の設定

「Azure Database for PostgreSQL - フレキシブル サーバー」を選択

image-5.png

「作成」をクリック

image-6.png

「基本」タブは以下のように設定しました、パスワードは任意のものを設定しました

image-7.png

image-8.png

「ネットワーク」タブで「Azure 内の任意の Azure サービスにこのサーバーへのパブリック アクセスを許可する」のチェックを入れて、「確認及び作成」を押しました

image-9.png

「作成」をクリック

image-10.png

「デプロイが進行中です」という表示になり、10分ちょっと待つと完了の表示になりました。

image-11.png

「Azure Database for PostgreSQL - フレキシブル サーバー」の画面から、先ほど作った「posgres-todolist」をクリック

image-12.png

「接続」から「postgres」のデータベース選択状態で「はい」をクリック

image-13.png

コンソールが開いてパスワード入力状態となるとなるので、PostgreSQLのリソース作成時に自分で設定したパスワードを入力する、ちなみにコンソールへのコピペは Shift+Ctrl+V でできる

image-14.png

コンソールから sql 実行してテーブルを作っておく

CREATE TABLE todos (
	id SERIAL PRIMARY KEY,
	title varchar(100) NOT NULL,
	created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
	updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
);

image-16.png

「接続」をクリックすると「アプリからの接続」の欄に「ADO.NET」用の接続文字列が見れるので、後述のC#の接続処理の時に使う。

image-15.png

C# で ASP.NET MVC のプロジェクトをデプロイするまで

基本的に↓この通りにするとできました

VisualStudio で MVC のアプリを作成

image-17.png

ソリューションエクスプローラーでプロジェクト名を右クリックして「発行」をクリック

image-18.png

Azure が選択されている状態で「次へ」をクリック

image-19.png

「Azure App Service(Windows)」を選択して「次へ」をクリック

image-20.png

Azure にログインしているマイクロソフトアカウントが選択されていない場合は設定する

「+新規作成」を押す

06.png

「ホスティングプラン」の「新規作成...」を押す

07.png

「場所」を「Japan West」、「サイズ」を「Free」に設定して「OK」ボタンを押しました

08.png

「作成」ボタンを押しました

image-21.png

作成されたようなので「完了」を押しました

image-22.png

右上の「発行」を押すと自動でブラウザが開いて Azure App Service にデプロイされている画面を見れました

image-23.png

image-24.png

TodoList を作成する

NuGet のパッケージ管理から Npgsql をインストールする

image-25.png

DB操作をやりやすくするために Npgsql をラップしたクラスを作る

Db.cs
using Npgsql;

namespace WebApplication1
{
    internal class Db
    {
        private static NpgsqlConnection? _connection;

        private static NpgsqlConnection GetConnection()
        {
            if (_connection == null)
            {
                // Azure の画面から確認できる、接続文字列を設定する
                // パスワードはPostgreSQLのリソース作成時に自分で設定したパスワードにする
                _connection = new NpgsqlConnection("Server=postgres-todolist.postgres.database.azure.com;Database=postgres;Port=5432;User Id=postgres;Password=XXXXX;Ssl Mode=Require;");
                _connection.Open();
            }
            return _connection;
        }

        public static void Execute(string sql, object?[]? param = null)
        {
            var command = new NpgsqlCommand(sql, GetConnection());
            Array.ForEach(param ?? [], p => command.Parameters.Add(new NpgsqlParameter("", p)));
            command.ExecuteNonQuery();
        }

        public static Dictionary<string, object>[] ExecuteAndFetchAll(string sql, object?[]? param = null)
        {
            var lines = new List<Dictionary<string, object>>();

            var command = new NpgsqlCommand(sql, GetConnection());
            Array.ForEach(param ?? [], p => command.Parameters.Add(new NpgsqlParameter("", p)));

            using var reader = command.ExecuteReader();
            while (reader.Read())
            {
                var row = new Dictionary<string, object>() {};
                for (int i = 0; i < reader.FieldCount; i++)
                {
                    row.Add(reader.GetName(i), reader.GetValue(i));
                }
                lines.Add(row);
            }

            return lines.ToArray();
        }
    }
}

デフォルトで作られている HomeController.cs を修正する

Controllers/HomeController.cs
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using WebApplication1.Models;

namespace WebApplication1.Controllers
{
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;

        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }

        public IActionResult Index()
        {
            ViewData["Messege"] = TempData["Messege"];

            try
            {
                ViewData["TodoList"] = Db.ExecuteAndFetchAll("select * from todos order by id desc");
            }
            catch (Exception e)
            {
                ViewData["Messege"] = $"エラー : {e.Message}";
            }


            return View();
        }

        [HttpPost]
        public ActionResult Add(string title)
        {
            try
            {
                Db.Execute("insert into todos (title, created_at, updated_at) values ($1, $2, $3)", [title, DateTime.Now, DateTime.Now]);
                TempData["Messege"] = "追加しました";
            }
            catch (Exception e)
            {
                TempData["Messege"] = $"エラー : {e.Message}";
            }
            return RedirectToAction("Index", "Home");
        }

        [HttpPost]
        public ActionResult Edit(int id, string title)
        {
            try
            {
                Db.Execute("update todos set title = $1, updated_at = $2 where id = $3", [title, DateTime.Now, id]);
                TempData["Messege"] = $"id:{id} を更新しました";
            }
            catch (Exception e)
            {
                TempData["Messege"] = $"エラー : {e.Message}";
            }
            return RedirectToAction("Index", "Home");
        }

        [HttpPost]
        public ActionResult Del(int id)
        {
            try
            {
                Db.Execute("delete from todos where id = $1", [ id ]);
                TempData["Messege"] = $"id:{id} を更新しました";
            }
            catch (Exception e)
            {
                TempData["Messege"] = $"エラー : {e.Message}";
            }
            return RedirectToAction("Index", "Home");
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
}

デフォルトで作成されている Index.cshtml を修正する

Views/Home/Index.cshtml
@{
    ViewData["Title"] = "Home Page";
}

<h2>Todo List</h2>

<div>Messege: @ViewData["Messege"]</div>

<table class="table">
    <thead>
        <tr>
            <th>id</th>
            <th>名前</th>
            <th>作成日</th>
            <th>更新日</th>
            <th></th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td></td>
            <td>
                <input type="text" class="form-control" name="title" value="" form="add" placeholder="名前を入力してください" />
            </td>
            <td></td>
            <td></td>
            <td>
                <form method="post" action="/Home/Add" id="add" onsubmit="return confirm('追加してよろしいですか?')">
                    <input type="submit" class="btn btn-success" value="追加" />
                </form>
            </td>
            <td></td>
        </tr>
        @foreach (var row in (IEnumerable<dynamic>)ViewData["TodoList"])
        {
            <tr>
                <td>@row["id"]</td>
                <td>
                    <input type="text" class="form-control" name="title" value="@row["title"]" form="edit_@row["id"]" />
                </td>
                <td>@row["created_at"]</td>
                <td>@row["updated_at"]</td>
                <td>
                <form method="post" action="/Home/Edit" id="edit_@row["id"]" onsubmit="return confirm('変更してよろしいですか?')">
                        <input type="hidden" name="id" value="@row["id"]" />
                        <input type="submit" class="btn btn-primary" value="変更" />
                    </form>
                </td>
                <td>
                    <form method="post" action="/Home/Del" onsubmit="return confirm('削除してよろしいですか?')">
                        <input type="hidden" name="id" value="@row["id"]" />
                        <input type="submit" class="btn btn-danger" value="削除" />
                    </form>
                </td>
            </tr>
        }
    </tbody>
</table>

再度VisualStudioから「発行」を押してデプロイすると Todo List が動きました!

image-26.png

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?