0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【C#】エラー発生時のASP.NET CoreでSQLServerからデータ更新する際のロールバック実行

Posted at

データをDBに更新する際に失敗した場合の処置としてRollbackを実装する方法を記載します。

フロントエンドの実装

Views/AdminUser/AdminEmployeeMember.cshtml
@removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.OptionTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers

@model List<ASPNETSQLServer.Models.AdminEmployeeViewModel>
@{
    ViewData["Title"] = "従業員名簿";
}

<form asp-action="SubmitEmployeeList" method="post">
    <div class="text-center">
        <h2>従業員名簿</h2>
        <table class="table table-bordered">
            <thead>
                <tr>
                    <th>従業員ID</th>
                    <th>部署</th>
                    <th>課</th>
                    <th>役職</th>
                    <th>従業員名</th>
                    <th>性別</th>
                    <th>IPアドレス</th>
                    <th>割り当て</th>
                </tr>
            </thead>
            <tbody>
                @for (int i = 0; i < Model.Count; i++)
                {
                    <tr>
                        <!-- hidden ID -->
                        <td>
                            @Model[i].employeeId
                            <input type="hidden" name="Employees[@i].employeeId" value="@Model[i].employeeId" />
                        </td>

                        <td>
                            <select class="form-select" name="Employees[@i].Department">
                                <option value="人事部" selected="@(Model[i].Department == "人事部")">人事部</option>
                                <option value="経理部" selected="@(Model[i].Department == "経理部")">経理部</option>
                                <option value="総務部" selected="@(Model[i].Department == "総務部")">総務部</option>
                                <option value="営業部" selected="@(Model[i].Department == "営業部")">営業部</option>
                                <option value="技術部" selected="@(Model[i].Department == "技術部")">技術部</option>
                                <option value="法務部" selected="@(Model[i].Department == "法務部")">法務部</option>
                                <option value="情報システム部" selected="@(Model[i].Department == "情報システム部")">情報システム部</option>
                            </select>
                        </td>

                        <td>
                            <select class="form-select" name="Employees[@i].Section">
                                <option value="人事課" selected="@(Model[i].Section == "人事課")">人事課</option>
                                <option value="経理課" selected="@(Model[i].Section == "経理課")">経理課</option>
                                <option value="総務課" selected="@(Model[i].Section == "総務課")">総務課</option>
                                <option value="営業課" selected="@(Model[i].Section == "営業課")">営業課</option>
                                <option value="技術課" selected="@(Model[i].Section == "技術課")">技術課</option>
                                <option value="法務課" selected="@(Model[i].Section == "法務課")">法務課</option>
                                <option value="情報システム課" selected="@(Model[i].Section == "情報システム課")">情報システム課</option>
                            </select>
                        </td>

                        <td>
                            <select class="form-select" name="Employees[@i].JobClass">
                                <option value="執行役員" selected="@(Model[i].JobClass == "執行役員")">執行役員</option>
                                <option value="部長" selected="@(Model[i].JobClass == "部長")">部長</option>
                                <option value="課長" selected="@(Model[i].JobClass == "課長")">課長</option>
                                <option value="主任" selected="@(Model[i].JobClass == "主任")">主任</option>
                                <option value="一般社員" selected="@(Model[i].JobClass == "一般社員")">一般社員</option>
                                <option value="契約社員" selected="@(Model[i].JobClass == "契約社員")">契約社員</option>
                                <option value="パート・アルバイト" selected="@(Model[i].JobClass == "パート・アルバイト")">パート・アルバイト</option>
                            </select>
                        </td>

                        <td>
                            <input type="text" class="form-control" name="Employees[@i].EmployeeName" value="@Model[i].EmployeeName" />
                        </td>

                        <td>
                            <input type="radio" name="Employees[@i].Sex" value="男性" @(Model[i].Sex == "男性" ? "checked" : "") /> 男
                            <input type="radio" name="Employees[@i].Sex" value="女性" @(Model[i].Sex == "女性" ? "checked" : "") /> 女
                        </td>

                        <td>
                            <input type="text" class="form-control" name="Employees[@i].Ipadress" value="@Model[i].Ipadress" />
                        </td>

                        <td class="text-center">
                            <input type="checkbox" name="Employees[@i].IpaddressAlocation" value="true" @(Model[i].IpaddressAlocation ? "checked" : "") />
                            <input type="hidden" name="Employees[@i].IpaddressAlocation" value="false" />
                        </td>
                    </tr>
                }
            </tbody>
        </table>
        <button type="submit" class="btn btn-primary">一括送信</button>
    </div>
</form>

バックエンドの実装

例えば、バックエンドの実装で、下記の図のようにトランザクションを誤った位置に貼ってしまいトランザクションエラーが起こった場合にロールバックを実行してDBの一貫性を保ちます。

image.png

トランザクション−ロールバック機構は下記のように書きます。

sample.cs
SqlTransaction transaction = connection.BeginTransaction();
command.Transaction = transaction;
//~(略)~
Try{
    //更新処理
}
catch(Exception e)
{
    Console.WriteLine(e);
    _logger.LogError($"予期せぬエラーが発生しました。エラーメッセージ:{e.Message}");
    transaction.Rollback();
}

トランザクションエラーが発生するコードは下記になります。

sample.cs
    [HttpPost]
    public IActionResult SubmitEmployeeList(List<AdminEmployeeViewModel> Employees)
    {
        string updateQuery = @"UPDATE [test].[dbo].[employee] WITH (ROWLOCK)
                               SET
                                department = @department,
                                section = @section,
                                jobclass = @jobclass,
                                name = @name,
                                ipaddress = @ipaddress,
                                sex = @sex,
                                ipaddressalocation = @ipaddressalocation
                               WHERE
                                employeeId = @employeeId                               
        ";
        using (SqlConnection connection = new SqlConnection(_connectString))
        {
            connection.Open();
            SqlTransaction transaction = connection.BeginTransaction();
            SqlCommand command = connection.CreateCommand();
            command.Transaction = transaction;
            try
            {
                foreach (var emp in Employees)
                {
                    using (command = new SqlCommand(updateQuery, connection))
                    {
                        command.Parameters.Add("@department", SqlDbType.NVarChar).Value = emp.Department;
                        command.Parameters.Add("@section", SqlDbType.NVarChar).Value = emp.Section;
                        command.Parameters.Add("@jobclass", SqlDbType.NVarChar).Value = emp.JobClass;
                        command.Parameters.Add("@name", SqlDbType.NVarChar).Value = emp.EmployeeName;
                        command.Parameters.Add("@ipaddress", SqlDbType.NVarChar).Value = emp.Ipadress;
                        command.Parameters.Add("@sex", SqlDbType.NVarChar).Value = emp.Sex;
                        command.Parameters.Add("@ipaddressalocation", SqlDbType.Bit).Value = emp.IpaddressAlocation;
                        command.Parameters.Add("@employeeId", SqlDbType.Int).Value = emp.employeeId;

                        // 行ロックを実行
                        int rowsAffected = command.ExecuteNonQuery();
                        Console.WriteLine($"従業員ID: {emp.employeeId} の更新:{rowsAffected}件");
                    }
                    transaction.Commit();
                    TempData["Message"] = "一括更新に成功しました。";
                }
                // トランザクション実行 本当は、はこの箇所に配置するのが正しい
                //transaction.Commit();
                //TempData["Message"] = "一括更新に成功しました。";
            }
            catch (DbException dbexception)
            {
                Console.WriteLine(dbexception);
                _logger.LogError($"データベース接続に失敗しました。エラーメッセージ:{dbexception.Message}");
                transaction.Rollback();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                _logger.LogError($"予期せぬエラーが発生しました。エラーメッセージ:{e.Message}");
                transaction.Rollback();
            }
        }
        // 完了後リダイレクトまたは再表示
        return RedirectToAction("AdminEmployeeMember");
    }
0
0
1

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?