internal ActionResult CreateView(GroupThread thread, Message message)
        {
            db.Configuration.ProxyCreationEnabled = false;
            db.Configuration.LazyLoadingEnabled = false;

            // 現在のプロジェクトに属し、かつ、公開されているグループを取得する。
            var groups = db.Groups.OfType<ProjectGroup>().Where(x => x.Project.ID == this.Project.ID && x.Accessibility == ProjectGroupAccessibility.Public).ToList();

            var selectList = groups.Select(x => new SelectListItem { Text = x.GroupName, Value = x.ID.ToString() });
            ViewBag.GroupId = selectList;
            ViewBag.Message = message ?? new Message();
            ViewBag.GroupThread = thread ?? new Thread();
            ViewBag.Project = this.Project;
            return View(thread);
        }
        internal void AttachFile(Message message, HttpPostedFileBase uploadedFile)
        {
            if (message == null || uploadedFile == null)
            {
                return;
            }

            // TODO: 見直し。プロトでは特に検証せずDBに保存する。
            var fileName = Path.GetFileName(uploadedFile.FileName);
            var extension = Path.GetExtension(fileName);
            var binaryData = GetBytes(uploadedFile.InputStream);

            if (binaryData.Length > 0)
            {
                var document = new Document
                {
                    ID = Guid.NewGuid(),
                    Uploaded = this.ExecutionContext.Now,
                    User = this.CurrentUser,
                    FileExtension = extension,
                    BinaryData = binaryData,
                };
                this.DbContext.Documents.Add(document);
                message.AttachedFileName = fileName;
                message.AttachedDocumentID = document.ID;
            }
        }
        public ActionResult Create(
            int? groupId,
            string threadName,
            string body,
            HttpPostedFileBase uploadedFile)
        {
            db.Configuration.ProxyCreationEnabled = false;
            db.Configuration.LazyLoadingEnabled = false;

            // TODO: トランザクション

            // 現在のプロジェクトと一緒に、実行ユーザーと所属企業もAttachされる。
            db.Projects.Attach(this.Project);

            if (groupId == null)
            {
                ModelState.AddModelError("GroupId", "グループを選択してください。");
            }

            // 正しいグループかどうかチェック
            var targetGroup = db.Entry(this.Project)
                .Collection(p => p.ProjectGroups)
                .Query()
                .Include(g => g.ParticipantUsers)
                .Include(g => g.ParticipantUsers.Select(u => u.ParticipantUser))
                .FirstOrDefault(g => g.ID == groupId);

            if (targetGroup == null)
            {
                ModelState.AddModelError("GroupId", "選択したグループは存在しません。");
            }
            else
            {
                if (targetGroup.Accessibility != ProjectGroupAccessibility.Public)
                {
                    ModelState.AddModelError("GroupId", "選択したグループは非公開です。");
                }

                if (targetGroup.ParticipantUsers.Count == 0)
                {
                    ModelState.AddModelError("GroupId", "選択したグループにはメンバーがいません。");
                }
            }

            if (string.IsNullOrEmpty(threadName))
            {
                ModelState.AddModelError("ThreadName", "スレッド名を入力してください。");
            }

            if (string.IsNullOrEmpty(body))
            {
                ModelState.AddModelError("Body", "本文を入力してください。");
            }

            GroupThread groupThread = new GroupThread { ThreadName = threadName };
            Message message = new Message { Body = body };

            if (!ModelState.IsValid)
            {
                return CreateView(groupThread, message);
            }

            AttachFile(message, uploadedFile);

            // Threadの追加
            // TODO: Durationの意味。返信可能な期限?
            db.Configuration.ProxyCreationEnabled = false;
            db.Configuration.LazyLoadingEnabled = false;

            groupThread.Group = targetGroup;
            groupThread.Duration = new Duration();
            db.Threads.Add(groupThread);

            // Messageの追加
            message.SentUser = ctx.CurrentUser;
            message.Sent = ctx.Now;
            groupThread.Message.Add(message);
            foreach (var groupuser in targetGroup.ParticipantUsers)
            {
                var relationship = new UserMessage { Message = message, User = groupuser.ParticipantUser };
                db.UserMessages.Add(relationship);
            }

            db.SaveChanges();
            return RedirectToAction("Index");
        }
        public ActionResult Reply(int? groupId, int? threadId, string body, HttpPostedFileBase uploadedFile)
        {
            if (groupId == null || threadId == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }

            if (!IsAccessibleProject(this.Group))
            {
                // 参加者がアクセスできない公開範囲である場合はエラー
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            
            var db = this.DbContext;
            Thread thread = db.Threads.Find(threadId);

            if (thread == null)
            {
                // IDに一致するスレッドが存在しない場合はエラー
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }

            Message message = new Message
            {
                Body = body,
                Sent = this.ExecutionContext.Now,
            };

            if (string.IsNullOrEmpty(body))
            {
                ViewBag.Body = body;
                ViewBag.ThreadId = threadId;
                return RedirectToAction("Messages", new { groupId = groupId, threadId = threadId, fromReply = true, body = body });
            }

            // メッセージ投稿処理
#if DEBUG
            var currentUserState = this.DbContext.Entry(this.CurrentUser).State;
            Debug.Assert(currentUserState == System.Data.EntityState.Unchanged, "CurrentUser is " + currentUserState);
#endif

            AttachFile(message, uploadedFile);

            message.SentUser = this.CurrentUser;
            message.Thread = thread;

            db.Users.Attach(message.SentUser);
            db.Threads.Attach(message.Thread);
            db.Messages.Add(message);

            db.SaveChanges();

            return RedirectToAction("Messages", new { groupId = groupId, threadId = threadId });
        }
        public ActionResult Create(int? groupId, string threadName, string body, HttpPostedFileBase uploadedFile)
        {
            // 基底クラスの共通処理により、Groupプロパティには必ず値が入っている。
            var group = this.Group;
            Debug.Assert(group != null, "this.Group is null");

            if (!IsAccessibleProject(group))
            {
                // 参加者がアクセスできない公開範囲である場合はエラー
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }

            if (string.IsNullOrEmpty(threadName))
            {
                ModelState.AddModelError("ThreadName", "スレッドタイトルを入力してください。");
            }

            if (string.IsNullOrEmpty(body))
            {
                ModelState.AddModelError("Body", "スレッドの説明を入力してください。");
            }

            GroupThread thread = new GroupThread { ThreadName = threadName };
            ViewBag.Body = body;

            if (!ModelState.IsValid)
            {
                ViewBag.GroupName = group.GroupName;
                ViewBag.GroupId = groupId;
                return View(thread);
            }

            var db = this.DbContext;
            Message message = new Message
            {
                Body = body,
                Sent = this.ExecutionContext.Now,
            };
            AttachFile(message, uploadedFile);
            message.SentUser = this.CurrentUser;
            db.Users.Attach(message.SentUser);

            thread.Message.Add(message);
            //TODO スレッドのDurationの扱いについて要検討
            thread.Duration = new Duration();
            thread.Group = db.Groups.Find(groupId.Value);

            db.Groups.Attach(thread.Group);
            db.Threads.Add(thread);

            db.SaveChanges();
            return RedirectToAction("Index", new { groupId = groupId });
        }