/// <summary>
        /// [LLD-03-05-01:01-01-02]
        /// </summary>
        private void file_create_acl(AppDbContext dbc, FileUpdateQueueItem item, Workspace workspace)
        {
            // 1. 対象ファイルが存在するかチェック
            if (!item.Target.Exists)
            {
                throw new ApplicationException("対象ファイルが指定位置に存在しません。");
            }

            // 3. ACLファイルから、ACLハッシュを取得する
            var aclbin  = VfsLogicUtils.ReadACLFile(new FileInfo(item.Target.FullName));
            var aclhash = aclbin.FindKeyValue("ACLHASH");

            // 4. データベースを参照し、ACLハッシュとファイルマッピング情報(AclHash)を突き合わせる
            FileMappingInfo entity = null;

            var repo = new FileMappingInfoRepository(dbc);

            entity = repo.LoadByAclHash(aclhash);
            if (entity == null)
            {
                throw new ApplicationException();
            }
            if (entity.Workspace == null || entity.Workspace.Id != workspace.Id)
            {
                throw new ApplicationException();
            }


            // 5. ファイルマッピング情報を参照し、物理ファイルが存在するかチェックする
            var phyFileInfo = new FileInfo(Path.Combine(workspace.PhysicalSpacePath, entity.MappingFilePath));

            if (!phyFileInfo.Exists)
            {
                throw new ApplicationException();
            }

            // 6. 物理ファイルを、ACLファイルのパスに対応する物理空間のパスへ移動する
            //    移動先が、同じ場所となる場合は処理しない。
            var aclfileLocalPath_Update = workspace.TrimWorekspacePath(item.Target.FullName, false);
            var extFilePath             = Path.Combine(Path.GetDirectoryName(aclfileLocalPath_Update), Path.GetFileNameWithoutExtension(aclfileLocalPath_Update));
            var toFileInfo = new FileInfo(Path.Combine(workspace.PhysicalSpacePath, extFilePath));

            if (phyFileInfo.FullName != toFileInfo.FullName)
            {
                Directory.CreateDirectory(toFileInfo.Directory.FullName);
                File.Move(phyFileInfo.FullName, toFileInfo.FullName);

                // 7. ファイルマッピング情報をDBに書き込む(コンテキスト初期化)
                entity = repo.LoadByAclHash(aclhash);
                entity.MappingFilePath = extFilePath;                 // 新しいファイルマップパス
                dbc.SaveChanges();
            }
        }
        /// <summary>
        /// LLD-03-05-01:01-02-01
        /// </summary>
        /// <param name="item"></param>
        /// <param name="workspace"></param>
        private void file_remove_acl(AppDbContext dbc, FileUpdateQueueItem item, Workspace workspace)
        {
            var aclfileLocalPath_Remove = workspace.TrimWorekspacePath(item.Target.FullName, false);
            var vrPath_Remove           = Path.Combine(Path.GetDirectoryName(aclfileLocalPath_Remove), Path.GetFileNameWithoutExtension(aclfileLocalPath_Remove));

            // 1. 削除したファイルパスと一致するファイルマッピング情報を取得する

            var repo = new FileMappingInfoRepository(dbc);
            var fmi  = repo.LoadByPath(vrPath_Remove);

            // 2. ファイルマッピング情報から、物理空間のファイルを特定する
            var phyFilePath = Path.Combine(workspace.PhysicalSpacePath, vrPath_Remove);

            // 3. 物理空間のファイルを削除する
            File.Delete(phyFilePath);

            // 4. ファイルマッピング情報をデータベースから削除する
            repo.Delete(fmi);
            dbc.SaveChanges();
        }
        /// <summary>
        /// ファイル更新情報キューに、ファイル更新情報を追加する
        /// </summary>
        /// <remarks>
        /// 対象パスがキューに登録済みの場合は、登録情報の更新を行います。
        /// 未登録の場合は、情報を新規登録します。
        /// </remarks>
        /// <param name="watchTarget">変更通知が発生した、ファイル情報</param>
        /// <param name="watcherChangeType">変更内容区分</param>
        /// <param name="beforeRenamedName">変更内容区分がリネームの場合、リネーム前のファイル名を入力してください</param>
        FileUpdateQueueItem UpdateOrInsertUpdatedFileQueueItem(FileInfo watchTarget, WatcherChangeTypes watcherChangeType, string beforeRenamedName)
        {
            FileUpdateQueueItem fileUpdateQueueItem;
            string key;

            lock (this)
            {
                // 更新イベントの対象ファイルが、ACLファイルか物理ファイルか更新キューに使用するキーが異なる。
                // ACLファイルでは、ACLハッシュをキーに使用します。
                // 物理ファイルでは、ファイルパスをキーに使用します。
                if (watchTarget.Extension == ".aclgene")
                {
                    // ACLファイルの場合、更新イベントを追わなくてもACLハッシュで常にどのファイルが更新キュー内のどこにあるかがわかる
                    // ※ただし、ファイル削除を除く

                    if (watcherChangeType == WatcherChangeTypes.Deleted)
                    {
                        var deletedFileRelativePath = this._Workspace.TrimWorekspacePath(watchTarget.FullName, false);
                        var r = from u in _UpdatesWatchFiles
                                where u.Value.OldRenameNamePath == deletedFileRelativePath
                                select u;
                        var prop = r.FirstOrDefault();
                        if (prop.Key != null)
                        {
                            _UpdatesWatchFiles.TryGetValue(prop.Key, out fileUpdateQueueItem);
                        }
                        else
                        {
                            fileUpdateQueueItem = new FileUpdateQueueItem {
                                Target = watchTarget
                            };

                            _UpdatesWatchFiles.AddOrUpdate(deletedFileRelativePath,
                                                           fileUpdateQueueItem, (_key, _value) => fileUpdateQueueItem
                                                           );

                            // 発生はありえないが、発生した場合は処理せず終了
                            // →発生はありうる。
                            //LOG.Warn("更新キューに登録されていないACLファイルの削除イベント");
                            //return null;
                        }
                    }
                    else
                    {
                        // ACLファイルからACLデータを取得
                        AclFileStructure aclFileData;
                        using (var file = File.OpenRead(watchTarget.FullName))
                        {
                            aclFileData = Serializer.Deserialize <AclFileStructure>(file);
                        }

                        var aclhash = aclFileData.FindKeyValue("ACLHASH");

                        if (!_UpdatesWatchFiles.ContainsKey(aclhash))
                        {
                            fileUpdateQueueItem = new FileUpdateQueueItem {
                                Target = watchTarget
                            };

                            _UpdatesWatchFiles.AddOrUpdate(aclhash, fileUpdateQueueItem, (_key, _value) => fileUpdateQueueItem);
                        }
                        else
                        {
                            // キューから情報を取得
                            _UpdatesWatchFiles.TryGetValue(aclhash, out fileUpdateQueueItem);
                            fileUpdateQueueItem.Target = watchTarget;                             // 登録している物理ファイル情報を最新のオブジェクトにする
                        }

                        // 最後に更新イベントが発生した時のファイルパスを格納しておく(Deleteイベント用)
                        fileUpdateQueueItem.OldRenameNamePath = this._Workspace.TrimWorekspacePath(watchTarget.FullName, false);
                    }
                }
                else
                {
                    if (watcherChangeType == WatcherChangeTypes.Renamed && !string.IsNullOrEmpty(beforeRenamedName))
                    {
                        // 変更内容がリネームの場合、名前変更前のファイルパスで登録済みの項目を取得し、
                        // 名前変更前の項目はキューから削除します。
                        // 名前変更後の項目として、新たにキューに再登録を行います。
                        var renamedFullName = watchTarget.FullName.Replace(watchTarget.FullName, beforeRenamedName);

                        var oldkey = this._Workspace.TrimWorekspacePath(renamedFullName, false);
                        key = this._Workspace.TrimWorekspacePath(watchTarget.FullName, false);

                        if (_UpdatesWatchFiles.ContainsKey(oldkey))
                        {
                            // 古いキーの項目をキューから削除します。
                            // 新しいキーで、キューに情報を再登録します。
                            _UpdatesWatchFiles.TryRemove(oldkey, out fileUpdateQueueItem);
                            _UpdatesWatchFiles.AddOrUpdate(key, fileUpdateQueueItem, (_key, _value) => fileUpdateQueueItem);
                        }
                    }
                    else if (watcherChangeType == WatcherChangeTypes.Created)
                    {
                        key = this._Workspace.TrimWorekspacePath(watchTarget.FullName, false);
                    }
                    else
                    {
                        key = this._Workspace.TrimWorekspacePath(watchTarget.FullName, false);
                    }

                    // 更新通知があったファイルが処理キューに未登録の場合、キューに更新通知情報を新規登録します
                    if (!_UpdatesWatchFiles.ContainsKey(key))
                    {
                        fileUpdateQueueItem = new FileUpdateQueueItem {
                            Target = watchTarget
                        };

                        _UpdatesWatchFiles.AddOrUpdate(key, fileUpdateQueueItem, (_key, _value) => fileUpdateQueueItem);
                    }
                    else
                    {
                        // キューから情報を取得
                        _UpdatesWatchFiles.TryGetValue(key, out fileUpdateQueueItem);
                        fileUpdateQueueItem.Target = watchTarget;                         // 登録している物理ファイル情報を最新のオブジェクトにする
                    }

                    // 更新通知のイベント区分が『リネーム』の場合、元のファイル名も保存しておく。
                    // イベント処理前は物理ディレクトリ空間のファイルパスはリネーム前のパスのままなので、
                    // リネーム前のファイル名のみを保存します。
                    if (watcherChangeType == WatcherChangeTypes.Renamed &&
                        string.IsNullOrEmpty(fileUpdateQueueItem.OldRenameNamePath))
                    {
                        fileUpdateQueueItem.OldRenameNamePath = beforeRenamedName;
                    }
                }



                // 情報に、履歴を追加
                var now = DateTime.Now;
                fileUpdateQueueItem.LastUpdate = now;
                var rec = new RecentInfo
                {
                    EventType  = watcherChangeType,
                    RecentDate = now
                };
                fileUpdateQueueItem.Recents.Add(rec);

                return(fileUpdateQueueItem);
            }
        }
        /// <summary>
        /// [LLD-03-05-01:01-01-01]
        /// </summary>
        private void file_create_normal(AppDbContext dbc, FileUpdateQueueItem item, Workspace workspace)
        {
            // 1. 対象ファイルが存在するかチェック
            if (!item.Target.Exists)
            {
                throw new ApplicationException("対象ファイルが指定位置に存在しません。");
            }

            // 2. 対象ファイルを、物理空間に移動する(ディレクトリが無い場合は、ディレクトリも作成する)
            //    一時ファイル名を使用する。
            var aclfileLocalPath_Update = workspace.TrimWorekspacePath(item.Target.FullName, false);
            // 移動先のディレクトリがサブディレクトリを含む場合、
            // 存在しないサブディレクトリを作成します。
            var newFileInfo = new FileInfo(Path.Combine(workspace.PhysicalSpacePath, aclfileLocalPath_Update));

            Directory.CreateDirectory(newFileInfo.Directory.FullName);

            var fromFilePath = new FileInfo(Path.Combine(workspace.VirtualSpacePath, aclfileLocalPath_Update));
            var toFilePath   = new FileInfo(Path.Combine(workspace.PhysicalSpacePath, aclfileLocalPath_Update + ".tmp"));

            File.Move(fromFilePath.FullName, toFilePath.FullName);

            // 3. ACLファイルを仮想空間に作成する
            var aclfilepath = Path.Combine(workspace.VirtualSpacePath, aclfileLocalPath_Update) + ".aclgene";
            // ACLファイルの作成を行います。
            string aclhash = VfsLogicUtils.GenerateACLHash();
            var    data    = new AclFileStructure();

            data.Version    = AclFileStructure.CURRENT_VERSION;
            data.LastUpdate = DateTime.Now;
            data.Data       = new KeyValuePair <string, string>[] {
                new KeyValuePair <string, string>("ACLHASH", aclhash)
            };

            using (var file = File.Create(aclfilepath))
            {
                Serializer.Serialize(file, data);
            }

            // 4. ファイルマッピング情報を作成し、データベースに格納する
            var entity = new FileMappingInfo
            {
                AclHash         = aclhash,
                Workspace       = workspace,
                Mimetype        = "image/png",          // 未実装(テスト実装)
                MappingFilePath = aclfileLocalPath_Update,
                LostFileFlag    = false,
            };

            VfsLogicUtils.UpdateOrCreate(dbc, entity);
            dbc.SaveChanges();

            // 5. 一時ファイル名を、正しいファイル名にリネームする
            if (!File.Exists(toFilePath.FullName))
            {
                throw new ApplicationException(toFilePath.FullName + "が見つかりません");
            }
            var extFileName = Path.GetFileNameWithoutExtension(toFilePath.Name);

            toFilePath.MoveTo(Path.Combine(toFilePath.DirectoryName, extFileName));
        }