public async Task <AddCommentToChatTxn_Output> Execute(AddCommentToChatTxn_Input input, MVCDbContext contextFather = null, bool autoCommit = true, IEventSender eventSender = null, IEventRegistry eventRegistry = null) { AddCommentToChatTxn_Output output = new AddCommentToChatTxn_Output(); output.ResultConfirmation = resultConfirmation.resultBad(_ResultMessage: "TXN_NOT_STARTED"); // Error handling bool error = false; // To Handle Only One Error DateTime nowUTC = DateTime.UtcNow; List <notification> notifications = new List <notification>(); try { MVCDbContext contextMGT = (contextFather != null) ? contextFather : new MVCDbContext(); // An using statement is in reality a try -> finally statement, disposing the element in the finally. So we need to take advance of that to create a DBContext inheritance try { // DBContext by convention is a UnitOfWork, track changes and commits when SaveChanges is called // Multithreading issue: so if we use only one DBContext, it will track all entities (_context.Customer.Add) and commit them when SaveChanges is called, // No Matter what transactions or client calls SaveChanges. // Note: Rollback will not remove the entities from the tracker in the context. so better dispose it. //***** 0. Make The Validations - Be careful : Concurrency. Same name can be saved multiple times if called at the exact same time. Better have an alternate database constraint // Call database to validate ChatId and UserId existance or just try to execute the query and if it fails is because one of those is wrong /* * Chat chatDb = await contextMGT.Chat.Where(x => x.ChatId == input.Comment.ChatId).FirstOrDefaultAsync(); * * if (chatDb == null) * { * error = true; * output.ResultConfirmation = ResultConfirmation.resultBad(_ResultMessage: "CHAT_DOES_NOT_EXIST_ERROR", _ResultDetail: ""); * } */ // Call Database to validate that: 1. ChatId exists 2. User Exists 3. User is part of the Company 4. User is Participant of the Chat chat chatDb0 = await contextMGT.Chat.Include(o => o.participants) .Where(x => x.chatId == input.Comment.chatId) .Where(x => x.participants.Any(y => y.userAppId.Equals(input.Comment.userAppId))) .FirstOrDefaultAsync(); chat chatDb = await contextMGT.Chat .Where(x => x.chatId == input.Comment.chatId) .Where(x => x.participants.Any(y => y.userAppId.Equals(input.Comment.userAppId))) .Include(o => o.participants) //.ThenInclude(i => i.User) .FirstOrDefaultAsync(); /* * var blogs = context.Blogs * .Include(blog => blog.Posts) * .ThenInclude(post => post.Author) * .ThenInclude(author => author.Photo) * .ToList(); */ //.Where(x => x.ChatId == input.Comment.ChatId) ; // 1. ChatId exists if (chatDb == null) { error = true; output.ResultConfirmation = resultConfirmation.resultBad(_ResultMessage: "USER_NOT_AUTHORIZED"); } // Find and validate user. Future: Create a transaction userApp userApp = await contextMGT.UserApp.Where(x => x.userAppId.Equals(input.Comment.userAppId)).FirstOrDefaultAsync(); if (userApp == null) { error = true; output.ResultConfirmation = resultConfirmation.resultBad(_ResultMessage: "USER_NOT_FOUND"); } if (!error) { //***** 1. Create the Comment (Atomic because is same Context DB) // Update the Chat chatDb.updatedAt = nowUTC; contextMGT.Chat.Update(chatDb); // Create the Chat To Save input.Comment.commentId = 0; input.Comment.createdAt = DateTime.UtcNow; // Create and Save the CommentInfo for each user. var query = from user in chatDb.participants where !user.userAppId.Equals(input.Comment.userAppId) select new commentInfo { commentInfoId = 0, commentId = 0, createdAt = nowUTC, delivered = false, seen = false, userAppId = user.userAppId }; // the current user must be Seen = True; SeenAt = NowUTC commentInfo ThisUser = new commentInfo { commentInfoId = 0, commentId = 0, createdAt = nowUTC, delivered = false, seen = true, seenAt = nowUTC, userAppId = input.Comment.userAppId }; input.Comment.commentsInfo = new List <commentInfo>(); input.Comment.commentsInfo = query.ToList(); input.Comment.commentsInfo.Add(ThisUser); // Save the chat to the context contextMGT.Comment.Add(input.Comment); //***** 4. Save and Commit to the Database (Atomic because is same Context DB) if (!error && autoCommit) { // This commit returns the user from Database and updates input.Comment await contextMGT.SaveChangesAsync(); // Call it only once so do all other operations first } // Save the notifications String chatGroup = (chatDb.participants.Count > 1) ? chatDb.name : ""; // Send to everyone except to himself var queryNotification = from user in chatDb.participants where user.userAppId != userApp.userAppId select new notification { notificationId = 0, type = "NewComment", title = "*NewComment*", subtitle = chatGroup, body = chatGroup + " *has a new comment from* " + userApp.userName, message = input.Comment.message, toUserAppId = user.userAppId, route = "Comment", routeAction = "New", routeId = input.Comment.commentId.ToString(), createdAt = nowUTC, seen = false }; notifications = queryNotification.ToList(); // Save the Notifications to the context contextMGT.Notification.AddRange(notifications); if (!error && autoCommit) { // This commit returns the user from Database and updates input.Comment await contextMGT.SaveChangesAsync(); // Call it only once so do all other operations first } //***** 5. Execute Send e-mails or other events once the database has been succesfully saved //***** If this task fails, there are options -> 1. Retry multiple times 2. Save the event as Delay, 3.Rollback Database, Re //***** 6. Confirm the Result (Pass | Fail) If gets to here there are not errors then return the new data from database output.ResultConfirmation = resultConfirmation.resultGood(_ResultMessage: "COMMENT_SUCESSFULLY_CREATED"); // If OK output.Comment = input.Comment; }// if (!error) if (!error) // Send the notification to the subscriptors { // Create a Notification Queue //var result = eventSender.SendAsync(new OnEventMessageDefault<Comment>(eventName: "onCommentAddedToChat", argumentTag: "chatId", argumentValue: output.Comment.ChatId.ToString(), outputType: output.Comment)); // Send the notification to each user * New Logic * foreach (var notification in notifications) { var result = eventSender.SendAsync(new OnEventMessageDefault <notification>(eventName: "onNotificationToUser", argumentTag: "userAppId", argumentValue: notification.toUserAppId, outputType: notification)); } /* * String chatGroup = (chatDb.Participants.Count > 1) ? " @ " + chatDb.Name : ""; * * int countDelivered = 0; * foreach (var user in chatDb.Participants) * { * String message = output.Comment.Message + " Mr. " + user.UserAppId; * * message = userApp.UserName + " Says " + output.Comment.Message + chatGroup; * Notification notification = new Notification { NotificationId = 0, Created = nowUTC, Message = message }; * var result = eventSender.SendAsync(new OnEventMessageDefault<Notification>(eventName: "onNotificationToUser", argumentTag: "userAppId", argumentValue: user.UserAppId, outputType: notification)); * * if (result.IsCompletedSuccessfully) * { * countDelivered++; * } * * * } */ // } } finally { // If the context Father is null the context was created on his own, so dispose it if (contextMGT != null && contextFather == null) { contextMGT.Dispose(); } } } catch (Exception ex) // Main try { System.Diagnostics.Debug.WriteLine("Error: " + ex.Message); string innerError = (ex.InnerException != null) ? ex.InnerException.Message : ""; System.Diagnostics.Debug.WriteLine("Error Inner: " + innerError); output = new AddCommentToChatTxn_Output(); // Restart variable to avoid returning any already saved data output.ResultConfirmation = resultConfirmation.resultBad(_ResultMessage: "EXCEPTION", _ResultDetail: ex.Message); } finally { // Save Logs if needed } return(output); }
public printer(Config Config = default, ref ptr <token.FileSet> fset = default, slice <byte> output = default, long indent = default, long level = default, pmode mode = default, bool endAlignment = default, bool impliedSemi = default, token.Token lastTok = default, token.Token prevOpen = default, slice <whiteSpace> wsbuf = default, token.Position pos = default, token.Position @out = default, token.Position last = default, ref ptr <long> linePtr = default, slice <ptr <ast.CommentGroup> > comments = default, bool useNodeComments = default, commentInfo commentInfo = default, map <ast.Node, long> nodeSizes = default, token.Pos cachedPos = default, long cachedLine = default) { this.m_ConfigRef = new ptr <Config>(Config); this.fset = fset; this.output = output; this.indent = indent; this.level = level; this.mode = mode; this.endAlignment = endAlignment; this.impliedSemi = impliedSemi; this.lastTok = lastTok; this.prevOpen = prevOpen; this.wsbuf = wsbuf; this.pos = pos; this.@out = @out; this.last = last; this.linePtr = linePtr; this.comments = comments; this.useNodeComments = useNodeComments; this.m_commentInfoRef = new ptr <commentInfo>(commentInfo); this.nodeSizes = nodeSizes; this.cachedPos = cachedPos; this.cachedLine = cachedLine; }
public async Task <UserEntersChatTxn_Output> Execute(UserEntersChatTxn_Input input, MVCDbContext contextFather = null, bool autoCommit = true, IEventSender eventSender = null) { UserEntersChatTxn_Output output = new UserEntersChatTxn_Output(); output.ResultConfirmation = resultConfirmation.resultBad(_ResultMessage: "TXN_NOT_STARTED"); // Error handling bool error = false; // To Handle Only One Error try { MVCDbContext contextMGT = (contextFather != null) ? contextFather : new MVCDbContext(); // An using statement is in reality a try -> finally statement, disposing the element in the finally. So we need to take advance of that to create a DBContext inheritance try { // DBContext by convention is a UnitOfWork, track changes and commits when SaveChanges is called // Multithreading issue: so if we use only one DBContext, it will track all entities (_context.Customer.Add) and commit them when SaveChanges is called, // No Matter what transactions or client calls SaveChanges. // Note: Rollback will not remove the entities from the tracker in the context. so better dispose it. //***** 0. Make The Validations - Be careful : Concurrency. Same name can be saved multiple times if called at the exact same time. Better have an alternate database constraint // Call database to validate ChatId and UserId existance or just try to execute the query and if it fails is because one of those is wrong /* * Chat chatDb = await contextMGT.Chat.Where(x => x.ChatId == input.Comment.ChatId).FirstOrDefaultAsync(); * * if (chatDb == null) * { * error = true; * output.ResultConfirmation = ResultConfirmation.resultBad(_ResultMessage: "CHAT_DOES_NOT_EXIST_ERROR", _ResultDetail: ""); * } */ // Call Database to validate that: 1. ChatId exists 2. User Exists 3. User is part of the Company 4. User is Participant of the Chat // Find all the messaget that this user has not seen in this Chat // Chat exists and the user belong to this Chat chat chatDb = await contextMGT.Chat.Include(o => o.participants) .Where(x => x.chatId == input.Chat.chatId) .Where(x => x.participants.Any(y => y.userAppId.Equals(input.User.userAppId))) .FirstOrDefaultAsync(); ; if (chatDb == null) { error = true; output.ResultConfirmation = resultConfirmation.resultBad(_ResultMessage: "USER_NOT_AUTHORIZED"); } /* Find the pending comments to be read for the user. There are two types * 1. CommentInfo that dont exist (there is no row). No even delivery * 1. CommentInfo that exist and Seen is false and SeenAt is Empty (there is no row). No even delivery */ if (!error) { //***** 1. Create the Comment (Atomic because is same Context DB) // Create the Chat To Save // input.Comment.CommentId = 0; // input.Comment.Created = DateTime.UtcNow; // Save the chat to the context DateTime nowUTC = DateTime.UtcNow; List <comment> commentsToReadNoExist = await contextMGT.Comment.Include(o => o.commentsInfo) .Where(x => x.chatId == input.Chat.chatId) .Where(x => !x.commentsInfo.Any(y => y.userAppId.Equals(input.User.userAppId))) .ToListAsync(); ; foreach (var comment in commentsToReadNoExist) { commentInfo commentInfo = new commentInfo { commentInfoId = 0, commentId = comment.commentId, delivered = true, deliveredAt = nowUTC, seen = true, seenAt = nowUTC, userAppId = input.User.userAppId }; contextMGT.CommentInfo.Add(commentInfo); } // contextMGT.Comment.Add(input.Comment); //***** 4. Save and Commit to the Database (Atomic because is same Context DB) if (!error && autoCommit) { // This commit returns the user from Database and updates input.Comment await contextMGT.SaveChangesAsync(); // Call it only once so do all other operations first } //***** 5. Execute Send e-mails or other events once the database has been succesfully saved //***** If this task fails, there are options -> 1. Retry multiple times 2. Save the event as Delay, 3.Rollback Database, Re //***** 6. Confirm the Result (Pass | Fail) If gets to here there are not errors then return the new data from database output.ResultConfirmation = resultConfirmation.resultGood(_ResultMessage: "CHAT_SUCESSFULLY_FOUND"); // If OK chatDb.comments = new List <comment>(); chatDb.comments = commentsToReadNoExist; output.Chat = chatDb; }// if (!error) if (!error) // Send the notification to the subscriptors { // await eventSender.SendAsync(new OnEventMessageDefault<Comment>(eventName: "onCommentAddedToChat", argumentTag: "chatId", argumentValue: output.Comment.ChatId.ToString(), outputType: output.Comment)); } } finally { // If the context Father is null the context was created on his own, so dispose it if (contextMGT != null && contextFather == null) { contextMGT.Dispose(); } } } catch (Exception ex) // Main try { System.Diagnostics.Debug.WriteLine("Error: " + ex.Message); string innerError = (ex.InnerException != null) ? ex.InnerException.Message : ""; System.Diagnostics.Debug.WriteLine("Error Inner: " + innerError); output = new UserEntersChatTxn_Output(); // Restart variable to avoid returning any already saved data output.ResultConfirmation = resultConfirmation.resultBad(_ResultMessage: "EXCEPTION", _ResultDetail: ex.Message); } finally { // Save Logs if needed } return(output); }