static Workflow() { var assemblies = ObjectEx.GetNonSystemAssemblies(); WorkflowConfigurations = assemblies.SelectMany(a => a.GetTypes().Where(type => typeof(IWorkflowConfiguration).IsAssignableFrom(type) && type != typeof(IWorkflowConfiguration)).Select(t => (IWorkflowConfiguration)Activator.CreateInstance(t))).ToDictionary <IWorkflowConfiguration, Guid>(k => k.ID); }
public async Task <HttpResponseMessage> ExecuteScheduledNotifications(string userName, string password) { var bguser = await(from u in DataContext.Users where u.UserName == userName && u.Active && !u.Deleted select u).FirstOrDefaultAsync(); if (bguser == null || bguser.UserType != DTO.Enums.UserTypes.BackgroundTask || bguser.PasswordHash != password.ComputeHash()) { return(Request.CreateErrorResponse(HttpStatusCode.Forbidden, "You do not have permission to execute this notification. Either the login, password or User Type is wrong for the specified credentials")); } //Handle cleaning of documents //This is done here because we're executing notifications on a daily basis. This can be moved if necessary. var days = WebConfigurationManager.AppSettings["KeepResponseDocumentsDays"].ToInt32(); if (days > 0) { try { var dt = DateTime.UtcNow.AddDays(days * -1); var docIds = await(from d in DataContext.Documents where d.CreatedOn < dt select d.ID).ToArrayAsync(); Logger.Debug($"{docIds.Count()} Documents pending deletion."); await DataContext.Database.ExecuteSqlCommandAsync("DELETE FROM Documents where CreatedOn < {0}", dt); } catch (Exception ex) { Logger.Error("Error happened when deleting documents: ", ex); } } var runTime = DateTime.UtcNow; var Logging = new ConcurrentBag <object>(); //Get all of the loggers var assemblies = ObjectEx.GetNonSystemAssemblies(); foreach (var assembly in assemblies.AsParallel()) { var logTypesToRegister = assembly.GetTypes().Where( type => type.BaseType != null && !type.IsAbstract && type.BaseType.IsGenericType && (type.BaseType.GetGenericTypeDefinition() == typeof(EntityLoggingConfiguration <,>))); foreach (object configurationInstance in logTypesToRegister.Select(Activator.CreateInstance)) { Logging.Add(configurationInstance); } } var notifications = new ConcurrentBag <Notification>(); //Loop through all of them generating notifications foreach (var log in Logging.AsParallel()) { try { var method = log.GetType().GetMethod("GenerateNotificationsFromLogs"); var task = (Task <IEnumerable <Notification> >)method.Invoke(log, new object[] { DataContext }); foreach (var n in await task) { notifications.Add(n); } } catch (Exception ex) { Logger.Error("Error Occured while executing notifications", ex); } } //Flip the results so that we get a list of users, and a sub list of their notifications. var users = (from n in notifications from r in n.Recipients select r).DistinctBy(n => n.Email); var sendNotifications = new ConcurrentBag <Notification>(); foreach (var user in users.AsParallel()) { try { //We are multi-threading this so that we can send as many as possible at one time. //Create a single notification for the user var notification = new Notification { Recipients = new Recipient[] { user }, }; var uNotes = notifications.Where(n => n.Recipients.Any(r => r.Email == user.Email)).Select(n => new { n.Body, n.Subject }); if (uNotes.Count() > 1) {//If there is more than one notification being aggrigated then set the subject to a generic title. notification.Subject = "Notifications and Reminders"; } else //If there is only one, use the specific subject created by the notification. { notification.Subject = uNotes.First().Subject; } notification.Body = string.Join("<hr/>", uNotes.Select(n => n.Body).ToArray()); //This adds it the aggrigate notification to the list of notifications that will be sent. //Each of these notifications only have one recipient. sendNotifications.Add(notification); } catch (Exception ex) { Logger.Error("Error Occured while sending notifications", ex); } } //Create the notifier var notifier = new Notifier(); //Asynchronously send all of the notifications await Task.Run(() => notifier.Notify(sendNotifications.AsEnumerable())); //Upon completion update all of the notifications that should have run with the new time that they should be run again. var subscriptions = await(from s in DataContext.UserEventSubscriptions where (s.NextDueTime == null || s.NextDueTime.Value < runTime) && s.User.Active && s.Frequency != DTO.Enums.Frequencies.Immediately select s).ToArrayAsync(); foreach (var sub in subscriptions) { switch (sub.Frequency) { case DTO.Enums.Frequencies.Daily: sub.NextDueTime = runTime.AddDays(1); sub.LastRunTime = runTime; break; case DTO.Enums.Frequencies.Monthly: sub.NextDueTime = runTime.AddMonths(1); sub.LastRunTime = runTime; break; case DTO.Enums.Frequencies.Weekly: sub.NextDueTime = runTime.AddDays(7); sub.LastRunTime = runTime; break; } } var mySubscriptions = await(from s in DataContext.UserEventSubscriptions where (s.NextDueTime == null || s.NextDueTime.Value < runTime) && s.User.Active && s.FrequencyForMy != DTO.Enums.Frequencies.Immediately select s).ToArrayAsync(); foreach (var mySub in subscriptions) { switch (mySub.FrequencyForMy) { case DTO.Enums.Frequencies.Daily: mySub.NextDueTimeForMy = runTime.AddDays(1); mySub.LastRunTime = runTime; break; case DTO.Enums.Frequencies.Monthly: mySub.NextDueTimeForMy = runTime.AddMonths(1); mySub.LastRunTime = runTime; break; case DTO.Enums.Frequencies.Weekly: mySub.NextDueTimeForMy = runTime.AddDays(7); mySub.LastRunTime = runTime; break; } } await DataContext.SaveChangesAsync(); if (Request != null) { return(Request.CreateResponse(HttpStatusCode.OK)); } else { //request does not exist when run in test harness. return(new HttpResponseMessage(HttpStatusCode.OK)); } }