public CannedReply(CannedReplyAnswer cra) { Id = cra.Id; MerchantId = cra.MerchantId; UserId = cra.UserId; CategoryName = cra.CategoryName; ReplyContent = cra.ReplyContent; IsAutoReplyEnabled = cra.IsAutoReplyEnabled; CreationTime = cra.CreationTime; Questions = cra.CannedReplyQuestions?.Select(crq => crq.Question) ?? Enumerable.Empty <string>(); }
// DELETE: odata/CannedReplies(5) public async Task <string> Delete(long key) { CannedReplyAnswer cannedReplyAnswer = await Db.CannedReplyAnswers.Include(c => c.CannedReplyQuestions).SingleOrDefaultAsync(c => c.Id == key); if (cannedReplyAnswer == null) { return("404"); } Db.CannedReplyQuestions.RemoveRange(cannedReplyAnswer.CannedReplyQuestions); Db.CannedReplyAnswers.Remove(cannedReplyAnswer); await Db.SaveChangesAsync(); return(""); }
private async Task <List <long> > AttachQuestionsToAnswerAndSaveInDb(CannedReplyAnswer cannedReplyAnswer, IEnumerable <string> targetQuestions) { List <long> specialQuestionIdLst = new List <long>(); if (targetQuestions != null) { var targetQuestionsHashSet = new HashSet <string>(targetQuestions); if (targetQuestionsHashSet.Any()) { specialQuestionIdLst = await AddQuestionsToAnswerAndNotSaveInDb(cannedReplyAnswer, targetQuestionsHashSet); await Db.SaveChangesAsync(); } } return(specialQuestionIdLst); }
private async Task <List <long> > AddQuestionsToAnswerAndNotSaveInDb(CannedReplyAnswer cannedReplyAnswer, HashSet <string> targetQuestions) { //these question ids in this lst will not be inserted List <long> specialQuestionIdLst = new List <long>(); if (targetQuestions != null) { foreach (var question in targetQuestions) { string csQuestion = CannedReply.GetMd5Hash(question); var crqFromDbId = await Db.CannedReplyQuestions.Where(c => c.CS_Question == csQuestion && c.MerchantId == cannedReplyAnswer.MerchantId && c.UserId == cannedReplyAnswer.UserId) .Where(c => c.Question == question) .Select(c => c.Id) .FirstOrDefaultAsync(); if (crqFromDbId == 0) { Db.CannedReplyQuestions.Add(new CannedReplyQuestion(cannedReplyAnswer, question)); } else { //Instead of update, we choose skip /* * if (crqFromDb.CannedReplyAnswerId != cannedReplyAnswer.Id) * { * crqFromDb.CannedReplyAnswerId = cannedReplyAnswer.Id; * } */ specialQuestionIdLst.Add(crqFromDbId); } //We don't need this line unless we choose update instead of skip //await Db.SaveChangesAsync(); } } return(specialQuestionIdLst); }
private async Task <CannedReply.CannedReplyAnswerInternalResult> UpdateCannedReplyAnswerToDb(CannedReply cannedReply) { var curMerchantIdFromDb = await Db.Merchants.Select(m => m.Id).FirstOrDefaultAsync(m => m == cannedReply.MerchantId); if (curMerchantIdFromDb == 0) { return(new CannedReply.CannedReplyAnswerInternalResult { CannedReplyAnswer = null, Status = "Error", ErrorMessage = "MerchantId is not valid or There is no such MerchantId in DB." }); } CannedReplyAnswer craFromDb = await Db.CannedReplyAnswers.Include(c => c.CannedReplyQuestions).SingleOrDefaultAsync(c => c.Id == cannedReply.Id); if (craFromDb == null) { return(new CannedReply.CannedReplyAnswerInternalResult { CannedReplyAnswer = null, Status = "Error", ErrorMessage = "record not found in DB" }); } craFromDb.ReplyContent = cannedReply.ReplyContent ?? craFromDb.ReplyContent; string csReplyContent = CannedReply.GetMd5Hash(craFromDb.ReplyContent); CannedReplyAnswer craFromDbWithSameAnswer = await Db.CannedReplyAnswers.Where(c => c.CS_ReplyContent == csReplyContent && c.MerchantId == craFromDb.MerchantId && c.UserId == craFromDb.UserId && c.Id != craFromDb.Id) .Include(c => c.CannedReplyQuestions) .FirstOrDefaultAsync(c => c.ReplyContent == craFromDb.ReplyContent); if (craFromDbWithSameAnswer == null) { craFromDb.IsAutoReplyEnabled = cannedReply.IsAutoReplyEnabled; craFromDb.CategoryName = cannedReply.CategoryName ?? craFromDb.CategoryName; try { if (cannedReply.Questions != null) { List <long> specialQuestionIdLst = await ForceAttachWithNewQuestionsToAnswerAndNotSaveInDb(craFromDb, cannedReply.Questions); await Db.SaveChangesAsync(); if (specialQuestionIdLst.Any()) { return(new CannedReply.CannedReplyAnswerInternalResult { CannedReplyAnswer = craFromDb, Status = "UpdatedWithSkip", ErrorMessage = $"Questions are skipped because this answer or other answers have these questions, questionIds: {string.Join(",", specialQuestionIdLst)}", //Status = "UpdateWithForceUpdate", //ErrorMessage = $"Questions are updated with this new answer, questionIds: {string.Join(",", specialQuestionIdLst)}", QuestionIds = specialQuestionIdLst }); } else { return(new CannedReply.CannedReplyAnswerInternalResult { CannedReplyAnswer = craFromDb, Status = "Updated", ErrorMessage = "" }); } } else { await Db.SaveChangesAsync(); return(new CannedReply.CannedReplyAnswerInternalResult { CannedReplyAnswer = craFromDb, Status = "Updated", ErrorMessage = "" }); } } catch (DbUpdateConcurrencyException ex) { if (!CannedReplyExists(cannedReply.Id)) { return(new CannedReply.CannedReplyAnswerInternalResult { CannedReplyAnswer = null, Status = "Error", ErrorMessage = "record not found in DB" }); } else { return(new CannedReply.CannedReplyAnswerInternalResult { CannedReplyAnswer = null, Status = "Error", ErrorMessage = ex.Message + ex.StackTrace }); } } catch (Exception ex) { ex = GetInnerException(ex); return(new CannedReply.CannedReplyAnswerInternalResult { CannedReplyAnswer = null, Status = "Error", ErrorMessage = ex.Message + ex.StackTrace }); } } else { cannedReply.CategoryName = cannedReply.CategoryName ?? craFromDb.CategoryName; if (cannedReply.Questions == null) { var mergeResult = await MergeExistingQuestionsToExistingAnswerInDb(craFromDb, craFromDbWithSameAnswer); Db.CannedReplyAnswers.Remove(craFromDb); await Db.SaveChangesAsync(); return(mergeResult); } else { Db.CannedReplyQuestions.RemoveRange(craFromDb.CannedReplyQuestions); Db.CannedReplyAnswers.Remove(craFromDb); await Db.SaveChangesAsync(); return(await MergeCannedReplyToExistingAnswerInDb(cannedReply, craFromDbWithSameAnswer)); } } }
private async Task <CannedReply.CannedReplyAnswerInternalResult> CreateCannedReplyAnswerToDb(CannedReply cannedReply) { var curMerchantIdFromDb = await Db.Merchants.Select(m => m.Id).FirstOrDefaultAsync(m => m == cannedReply.MerchantId); if (curMerchantIdFromDb == 0) { return(new CannedReply.CannedReplyAnswerInternalResult { CannedReplyAnswer = null, Status = "Error", ErrorMessage = "MerchantId is not valid or There is no such MerchantId in DB." }); } if (cannedReply.Questions == null || !cannedReply.Questions.Any()) { return(new CannedReply.CannedReplyAnswerInternalResult { CannedReplyAnswer = null, Status = "Error", ErrorMessage = "No questions to Add when creat canned Reply." }); } string csReplyContent = CannedReply.GetMd5Hash(cannedReply.ReplyContent); CannedReplyAnswer craFromDb = await Db.CannedReplyAnswers.Where(c => c.CS_ReplyContent == csReplyContent && c.MerchantId == cannedReply.MerchantId && c.UserId == cannedReply.UserId) .Include(c => c.CannedReplyQuestions) .FirstOrDefaultAsync(c => c.ReplyContent == cannedReply.ReplyContent); if (craFromDb != null) { cannedReply.CategoryName = cannedReply.CategoryName ?? ""; return(await MergeCannedReplyToExistingAnswerInDb(cannedReply, craFromDb)); } else { try { CannedReplyAnswer cannedReplyAnswer = new CannedReplyAnswer(cannedReply); Db.CannedReplyAnswers.Add(cannedReplyAnswer); await Db.SaveChangesAsync(); //these question ids in this lst will not be inserted List <long> specialQuestionIdLst = await AttachQuestionsToAnswerAndSaveInDb(cannedReplyAnswer, cannedReply.Questions); if (specialQuestionIdLst.Any()) { if (specialQuestionIdLst.Count == new HashSet <string>(cannedReply.Questions).Count) { //TODO: backend job to clean these answers if transaction failed Db.CannedReplyAnswers.Remove(cannedReplyAnswer); await Db.SaveChangesAsync(); return(new CannedReply.CannedReplyAnswerInternalResult { CannedReplyAnswer = null, Status = "Error", ErrorMessage = "No questions to Add or all questions are duplicated when creating canned Reply." }); } return(new CannedReply.CannedReplyAnswerInternalResult { CannedReplyAnswer = cannedReplyAnswer, Status = "CreatedWithSkip", ErrorMessage = $"Questions are skipped because this answer or other answers have these questions, questionIds: {string.Join(",", specialQuestionIdLst)}", //Status = "CreatedWithUpdate", //ErrorMessage = $"Questions are updated to be attached with this new answer, questionIds: {string.Join(",", specialQuestionIdLst)}", QuestionIds = specialQuestionIdLst }); } else { return(new CannedReply.CannedReplyAnswerInternalResult { CannedReplyAnswer = cannedReplyAnswer, Status = "Created", ErrorMessage = "" }); } } catch (Exception ex) { ex = GetInnerException(ex); return(new CannedReply.CannedReplyAnswerInternalResult { CannedReplyAnswer = null, Status = "Error", ErrorMessage = ex.Message + ex.StackTrace }); } } }
private async Task <CannedReply.CannedReplyAnswerInternalResult> MergeExistingQuestionsToExistingAnswerInDb(CannedReplyAnswer craFromDbOri, CannedReplyAnswer craFromDbWithSameAnswerDes) { craFromDbOri.CannedReplyQuestions.ForEach(crq => crq.CannedReplyAnswerId = craFromDbWithSameAnswerDes.Id); await Db.SaveChangesAsync(); return(new CannedReply.CannedReplyAnswerInternalResult { CannedReplyAnswer = craFromDbWithSameAnswerDes, Status = "Updated", ErrorMessage = $"Merged Questions of Answer {craFromDbOri.Id} to Answer {craFromDbWithSameAnswerDes.Id}" }); }
private async Task <CannedReply.CannedReplyAnswerInternalResult> MergeCannedReplyToExistingAnswerInDb(CannedReply cannedReply, CannedReplyAnswer craFromDb) { craFromDb.IsAutoReplyEnabled = cannedReply.IsAutoReplyEnabled; craFromDb.CategoryName = cannedReply.CategoryName ?? craFromDb.CategoryName; //don't use this if we have changes on CannedReply's dependency, //we may lose info in the CannedReply after it's dependency get changed by this operation. //Here I remove it just because it is no needed, //await Db.SaveChangesAsync(); List <long> specialQuestionIdLst = await AttachQuestionsToAnswerAndSaveInDb(craFromDb, cannedReply.Questions); if (specialQuestionIdLst.Any()) { return(new CannedReply.CannedReplyAnswerInternalResult { CannedReplyAnswer = craFromDb, Status = "UpdatedWithSkip", ErrorMessage = $"Merged Questions to Answer {craFromDb.Id}. Questions are skipped because this answer or other answers have these questions, questionIds: {string.Join(",", specialQuestionIdLst)}", //Status = "UpdateWithForceUpdate", //ErrorMessage = $"Questions are updated with this new answer, questionIds: {string.Join(",", specialQuestionIdLst)}", QuestionIds = specialQuestionIdLst }); } else { return(new CannedReply.CannedReplyAnswerInternalResult { CannedReplyAnswer = craFromDb, Status = "Updated", ErrorMessage = $"Merged Questions to Answer {craFromDb.Id}" }); } }
private async Task <List <long> > ForceAttachWithNewQuestionsToAnswerAndNotSaveInDb(CannedReplyAnswer cannedReplyAnswer, IEnumerable <string> targetQuestions) { if (targetQuestions == null) { throw new Exception("Error: not set question parameter when trying to update questions"); } Dictionary <string, CannedReplyQuestion> oriQuestionDict = new Dictionary <string, CannedReplyQuestion>(); cannedReplyAnswer.CannedReplyQuestions.ForEach(crq => { if (!oriQuestionDict.ContainsKey(crq.Question)) { oriQuestionDict.Add(crq.Question, crq); } }); HashSet <string> oriQuestionSet = new HashSet <string>(cannedReplyAnswer.CannedReplyQuestions.Select(crq => crq.Question)); HashSet <string> desQuestionSet = new HashSet <string>(targetQuestions); HashSet <string> innerSet = new HashSet <string>(oriQuestionSet); innerSet.IntersectWith(desQuestionSet); oriQuestionSet.ExceptWith(innerSet); desQuestionSet.ExceptWith(innerSet); foreach (var deleteQuestion in oriQuestionSet) { //this may throw error when save, because there may be multiple thread trying to update or delete Db.CannedReplyQuestions.Remove(oriQuestionDict[deleteQuestion]); } //these question ids in this lst will not be inserted List <long> specialQuestionIdLst = await AddQuestionsToAnswerAndNotSaveInDb(cannedReplyAnswer, desQuestionSet); if (specialQuestionIdLst.Count == desQuestionSet.Count && innerSet.Count == 0) { throw new Exception("Error: all questions have existing answer when trying to update this answer's questions"); } //await Db.SaveChangesAsync(); return(specialQuestionIdLst); }