/// <summary>
        /// DT_PARENT_CHILD_CONNECTテーブルにメッセージを追加またはメッセージの内容で更新処理を行う(親フラグ=trueの場合)
        /// </summary>
        /// <param name="inData">更新データ</param>
        /// <returns>追加または更新したデータ。「確認日時」が既存レコードより古く更新しなかった場合には、nullを返す</returns>
        public DtParentChildConnect Save(DtParentChildConnectFromParent inData)
        {
            DtParentChildConnect model = null;

            try
            {
                _dbPolly.Execute(() =>
                {
                    using (DBAccessor.Models.RmsDbContext db = new DBAccessor.Models.RmsDbContext(_appSettings))
                        using (var tran = db.Database.BeginTransaction())
                        {
                            // 現在時刻を取得
                            var now = _timePrivder.Now;

                            // メッセージクラス→Modelクラス変換時に、通信が成功だった場合は最終接続日時にデータが格納される
                            // ここで初期値だった場合には通信失敗と判断できる
                            if (inData.ParentLastConnectDatetime == null || inData.ParentLastConnectDatetime == default(DateTime))
                            {
                                inData.ParentLastConnectDatetime = now;
                            }

                            // DB格納データクラス
                            DBAccessor.Models.DtParentChildConnect entity = CreateEntityFromParent(inData, db);

                            // 親機器 - 子機器 - 親機器確認結果の組み合わせをチェックする
                            // 3つの組み合わせでレコードを一意に決めることができる
                            var sids = CreateSqlParameterSet(entity.ParentDeviceSid, entity.ChildDeviceSid);
                            DBAccessor.Models.DtParentChildConnect targetRecord
                                = db.DtParentChildConnect.FromSql(SelectParentSqlCommand, sids).AsNoTracking().FirstOrDefault();

                            if (targetRecord == null || targetRecord.Sid == 0)
                            {
                                // 作成日時+更新日時
                                // 通常はSaveChangesにTimeProvideを渡し、DBが自動的に作成日時と更新日時を設定する。
                                // しかし親子間通信データについては、
                                // 通信失敗のケースにおいて最終更新日時にはレコード作成日時を格納する
                                // (nullを回避しつつ、通信失敗ケースであったことを後から判断できるようにするため)。
                                // DBが自動的に時刻を設定した場合に、リポジトリ内で取得した時刻と時間差が発生する可能性があるため、
                                // リポジトリ内で明示的に時刻を取得してDBにレコードを挿入する。
                                entity.CreateDatetime = now;
                                entity.UpdateDatetime = now;

                                // レコード追加
                                var dbdata = db.DtParentChildConnect.Add(entity).Entity;

                                // 最終日時更新データと作成日時・更新日時を一致させたいので、TimeProviderは渡さずに明示的な日時データを使う
                                db.SaveChanges();
                                model = dbdata.ToModel();
                            }
                            else
                            {
                                // データの時刻チェック: データがDBに格納されたデータよりも新しい場合のみ更新処理を行う
                                // DBよりも古い確認時刻だった場合には更新せずにnullを返す
                                DateTime?dbTime = targetRecord.ParentConfirmDatetime;

                                if (dbTime == null || inData.ParentConfirmDatetime.Value.CompareTo(dbTime.Value) > 0)
                                {
                                    // 更新対象のレコードのSIDを設定
                                    entity.Sid = targetRecord.Sid;

                                    db.DtParentChildConnect.Attach(entity);

                                    // 更新のあったデータのみを更新する
                                    db.Entry(entity).Property(x => x.ParentConfirmDatetime).IsModified = true;
                                    db.Entry(entity).Property(x => x.ParentResult).IsModified          = true;
                                    if (entity.ParentResult != null && entity.ParentResult.Value)
                                    {
                                        db.Entry(entity).Property(x => x.ParentLastConnectDatetime).IsModified = true;
                                    }

                                    db.SaveChanges(_timePrivder);
                                    model = entity.ToModel();
                                }
                            }

                            // トランザクション終了
                            tran.Commit();
                        }
                });

                return(model);
            }
            catch (ValidationException e)
            {
                throw new RmsParameterException(e.ValidationResult.ErrorMessage, e);
            }
            catch (Exception e)
            {
                throw new RmsException("DT_PARENT_CHILD_CONNECTテーブルの更新処理に失敗しました。", e);
            }
            finally
            {
                _logger.LeaveJson("{0}", model);
            }
        }
        public void SaveFromParentTest(
            string no,
            string in_InsertNewDataSqlPath,
            string in_DeleteNewDataSqlPath,
            string expected_DataJsonPath,
            string expected_ExceptionType,
            string expected_ExceptionMessage,
            string remarks)
        {
            // 初期データ挿入
            RepositoryTestHelper.ExecInsertSql(in_InsertNewDataSqlPath);

            // データを作成する
            var newParentData = new DtParentChildConnectFromParent()
            {
                ParentDeviceUid       = "ParentDeviceUid",
                ChildDeviceUid        = "ChildDeviceUid",
                ParentResult          = false,
                ParentConfirmDatetime = DateTime.Parse("2020/4/2 0:00:00")
            };

            if (expected_ExceptionType == typeof(RmsException).FullName)
            {
                // 各パラメータでnull非許容の場合の確認
                if (remarks.Contains("ParentResult"))
                {
                    newParentData.ParentResult = null;
                }
                else if (remarks.Contains("ParentConfirmDatetime"))
                {
                    newParentData.ParentConfirmDatetime = null;
                }
                else if (remarks.Contains("All"))
                {
                    newParentData = null;
                }
                else
                {
                    // UIDを存在しないものにする
                    newParentData.ParentDeviceUid = null;
                    newParentData.ChildDeviceUid  = null;
                }
            }

            string exceptionName    = "";
            string exceptionMessage = "";

            try
            {
                var savedParentData = _parentChildConnectRepository.Save(newParentData);
                if (savedParentData != null)
                {
                    // 比較に使用しない値はnull or 固定値とする
                    savedParentData.CreateDatetime = DateTime.Parse("2020/4/1 0:00:00");
                    savedParentData.UpdateDatetime = DateTime.Parse("2020/4/1 0:00:00");
                }

                // データのjson化
                string readJson   = Utility.ObjectExtensions.ToStringJson(savedParentData);
                string expectJson = null;
                if (File.Exists(expected_DataJsonPath))
                {
                    expectJson = File.ReadAllText(expected_DataJsonPath);
                }

                // データの比較
                Assert.AreEqual(expectJson, readJson);

                // TODO DBデータ内容をチェックする
            }
            catch (RmsAlreadyExistException e)
            {
                exceptionName    = e.GetType().FullName;
                exceptionMessage = e.Message;
            }
            catch (RmsException e)
            {
                exceptionName    = e.GetType().FullName;
                exceptionMessage = e.Message;
            }
            // 例外発生チェック
            Assert.AreEqual(expected_ExceptionType, exceptionName);
            Assert.AreEqual(expected_ExceptionMessage, exceptionMessage);

            // 後処理
            RepositoryTestHelper.ExecDeleteSql(in_DeleteNewDataSqlPath);
        }