public ActionResult RunEngine(int submissionCycleId) { SubmissionCycle submissionCycle = _submissionCycleService.GetSubmissionCycle(submissionCycleId); if (submissionCycle == null) { string strMessage = $"Collection cycle with id {submissionCycleId} not found."; _loggingService.LogErrorMessage(strMessage); throw new InvalidOperationException(strMessage); } var session = _appUserService.GetSession(); var edOrg = _edOrgService.GetEdOrgById(session.FocusedEdOrgId, session.FocusedSchoolYearId); _rulesEngineService.DeleteOldValidationRuns(submissionCycle, edOrg.Id); // TODO: Validate the user's access to district, action, school year // todo: all security ValidationReportSummary summary = _rulesEngineService.SetupValidationRun( submissionCycle, submissionCycle.CollectionId); HostingEnvironment.QueueBackgroundWorkItem( cancellationToken => _rulesEngineService.RunValidationAsync( submissionCycle, summary.ValidationReportSummaryId)); return(Json(summary)); }
private void PopulateDropDownLists(SubmissionCycle submissionCycle = null) { List <SelectListItem> schoolYears = _submissionCycleService.GetSchoolYearsSelectList(submissionCycle); var ruleCollections = _rulesEngineService.GetCollections().Select(c => c.CollectionId); ViewData["schoolYears"] = schoolYears; ViewData["RuleCollections"] = ruleCollections; }
public ActionResult AddSubmissionCycle() { PopulateDropDownLists(); var submissionCycle = new SubmissionCycle { StartDate = DateTime.Now, EndDate = DateTime.Now }; return(PartialView("Partials/SubmissionCycleEditModal", submissionCycle)); }
public SubmissionCycle SchoolYearCollectionAlreadyExists(SubmissionCycle submissionCycle) { using (var validationPortalDataContext = ValidationPortalDataContextFactory.Create()) { var duplicateCycle = validationPortalDataContext.SubmissionCycles .FirstOrDefault(x => x.SchoolYearId == submissionCycle.SchoolYearId && x.CollectionId == submissionCycle.CollectionId); return(duplicateCycle); } }
public bool AddSubmissionCycle(SubmissionCycle submissionCycle) { using (var validationPortalDataContext = ValidationPortalDataContextFactory.Create()) { validationPortalDataContext.SubmissionCycles.Add(submissionCycle); validationPortalDataContext.SaveChanges(); return(true); // why have a return type at all? todo } }
public List <SelectListItem> GetSchoolYearsSelectList(SubmissionCycle submissionCycle = null) { var schoolYearsEnumerable = SchoolYearService.GetSubmittableSchoolYearsDictionary().OrderByDescending(x => x.Value); List <SelectListItem> schoolYears = schoolYearsEnumerable.Select(kvPair => new SelectListItem { Value = kvPair.Key.ToString(), Text = kvPair.Value, Selected = (submissionCycle != null) && (kvPair.Key == submissionCycle.SchoolYearId) }).ToList(); if (submissionCycle != null) { schoolYears[0].Selected = true; } return(schoolYears); }
public void SaveSubmissionCycle(SubmissionCycle submissionCycle) { try { using (var validationPortalDataContext = ValidationPortalDataContextFactory.Create()) { if (submissionCycle == null) { throw new Exception("Attempted to save a null SubmissionCycle."); } if (submissionCycle.Id == 0) { validationPortalDataContext.SubmissionCycles.Add(submissionCycle); validationPortalDataContext.SaveChanges(); } else { var existingSubmissionCycle = validationPortalDataContext.SubmissionCycles .FirstOrDefault(s => s.Id == submissionCycle.Id); if (existingSubmissionCycle == null) { throw new Exception($"SubmissionCycle with id {submissionCycle.Id} does not exist."); } existingSubmissionCycle.CollectionId = submissionCycle.CollectionId; existingSubmissionCycle.StartDate = submissionCycle.StartDate; existingSubmissionCycle.EndDate = submissionCycle.EndDate; existingSubmissionCycle.SchoolYearId = submissionCycle.SchoolYearId; validationPortalDataContext.SaveChanges(); } } } catch (Exception ex) { string strMessage = string.Empty; if (submissionCycle != null) { strMessage = $" with id = { submissionCycle.Id }, StartDate = { submissionCycle.StartDate }, EndDate = { submissionCycle.EndDate}, SchoolYearID = { submissionCycle.SchoolYearId }"; } LoggingService.LogErrorMessage($"An error occurred while saving announcement {strMessage}: {ex.ChainInnerExceptionMessages()}"); throw new Exception("An error occurred while saving announcement."); } }
public bool AddSubmissionCycle(string collectionId, DateTime startDate, DateTime endDate) { if (collectionId == null) { // todo: better null/invalid argument handling return(false); } using (var validationPortalDataContext = ValidationPortalDataContextFactory.Create()) { var newSubmissionCycle = new SubmissionCycle(collectionId, startDate, endDate); validationPortalDataContext.SubmissionCycles.Add(newSubmissionCycle); validationPortalDataContext.SaveChanges(); return(true); } }
public void DeleteOldValidationRuns(SubmissionCycle submissionCycle, int edOrgId) { if (submissionCycle?.SchoolYearId == null) { throw new ArgumentException("Submission cycle is null, or SchoolYearId is null", nameof(submissionCycle)); } using (var validationDbContext = DbContextFactory.Create()) { var reportSummaries = validationDbContext.ValidationReportSummaries .Where(x => x.Collection == submissionCycle.CollectionId && x.SchoolYearId == submissionCycle.SchoolYearId && x.EdOrgId == edOrgId); if (reportSummaries.Count() > 1) { reportSummaries = reportSummaries.OrderByDescending(x => x.RequestedWhen).Skip(1); foreach (var reportSummary in reportSummaries) { var reportDetails = validationDbContext.ValidationReportDetails.Where( x => x.ValidationReportSummaryId == reportSummary.ValidationReportSummaryId); validationDbContext.ValidationReportDetails.RemoveRange(reportDetails); validationDbContext.ValidationReportSummaries.Remove(reportSummary); var schoolYear = SchoolYearService.GetSchoolYearById(reportSummary.SchoolYearId); string fourDigitOdsDbYear = schoolYear.EndYear; using (var odsRawDbContext = OdsDbContextFactory.CreateWithParameter(fourDigitOdsDbYear)) { var ruleValidations = odsRawDbContext.RuleValidations.Where( x => x.RuleValidationId == reportSummary.RuleValidationId); odsRawDbContext.RuleValidations.RemoveRange(ruleValidations); odsRawDbContext.SaveChanges(); } } validationDbContext.SaveChanges(); } } }
public void SetupValidationRun_Should_ThrowForNullSchoolYearId() { var rulesEngineService = new RulesEngineService( AppUserServiceMock.Object, EdOrgServiceMock.Object, SchoolYearServiceMock.Object, EngineConfigMock.Object, LoggingServiceMock.Object, DbContextFactoryMock.Object, SchoolYearDbContextFactoryMock.Object, EngineObjectModel, null); var submissionCycle = new SubmissionCycle { SchoolYearId = null }; Assert.Throws <ArgumentException>(() => rulesEngineService.SetupValidationRun(submissionCycle, null)); }
public Task RunValidationAsync(SubmissionCycle submissionCycle, long ruleValidationId) { var schoolYear = _schoolYearService.GetSchoolYearById(submissionCycle.SchoolYearId.Value); string fourDigitOdsDbYear = schoolYear.EndYear; _loggingService.LogInfoMessage($"===== Starting validation run for year {fourDigitOdsDbYear}, ruleValidationId {ruleValidationId}"); return(Task.Factory .StartNew(() => RunValidation(submissionCycle, ruleValidationId)) .ContinueWith(task => { _loggingService.LogInfoMessage($"===== Completed validation run for year {fourDigitOdsDbYear}, ruleValidationId {ruleValidationId}"); if (task.Exception != null) { _loggingService.LogErrorMessage(task.Exception.Flatten().ChainInnerExceptionMessages()); } })); }
public void SetupValidationRun_Should_ReturnValidationReportSummary() { var rulesEngineService = new RulesEngineService( AppUserServiceMock.Object, EdOrgServiceMock.Object, SchoolYearServiceMock.Object, EngineConfigMock.Object, LoggingServiceMock.Object, DbContextFactoryMock.Object, SchoolYearDbContextFactoryMock.Object, EngineObjectModel, null); var schoolYear = new SchoolYear { Id = 1, Enabled = true, ErrorThreshold = null, StartYear = "2019", EndYear = "2020" }; var submissionCycle = new SubmissionCycle { SchoolYearId = schoolYear.Id, CollectionId = "collection" }; SchoolYearServiceMock.Setup(x => x.GetSchoolYearById(schoolYear.Id)).Returns(schoolYear); AppUserServiceMock.Setup(x => x.GetSession()).Returns(DefaultTestAppUserSession); AppUserServiceMock.Setup(x => x.GetUser()).Returns(DefaultTestAppUserSession.UserIdentity); var result = rulesEngineService.SetupValidationRun(submissionCycle, submissionCycle.CollectionId); result.ShouldNotBeNull(); result.SchoolYearId.ShouldEqual(schoolYear.Id); result.Collection.ShouldEqual(submissionCycle.CollectionId); SchoolYearDbContextMock.Verify( x => x.RuleValidations.Add( It.Is <RuleValidation>(y => y.CollectionId == submissionCycle.CollectionId))); }
public ActionResult SaveSubmissionCycle(SubmissionCycle submissionCycle) { if (submissionCycle.EndDate < submissionCycle.StartDate) { ModelState.AddModelError("EndDate", "End Date needs to be later than Start Date"); } // We should not let the user add a collection cycle with the same school year and collectionId combination // that already exists in the database, or set the school year and collectionId of an existing cycle // to ones of another existing collection cycle. SubmissionCycle duplicate = _submissionCycleService.SchoolYearCollectionAlreadyExists(submissionCycle); if (duplicate != null && (submissionCycle.Id == 0 || submissionCycle.Id != duplicate.Id)) { ModelState.AddModelError("CollectionId", "A collection cycle with this School Year and Collection already exists."); } if (ModelState.IsValid) { try { _submissionCycleService.SaveSubmissionCycle(submissionCycle); return(RedirectToAction("Index", new { tab = "submissioncycles" })); } catch (Exception ex) { ModelState.AddModelError("General Error", ex.Message); PopulateDropDownLists(submissionCycle); return(PartialView("Partials/SubmissionCycleEditModal", submissionCycle)); } } else { PopulateDropDownLists(submissionCycle); return(PartialView("Partials/SubmissionCycleEditModal", submissionCycle)); } }
public ValidationReportSummary SetupValidationRun(SubmissionCycle submissionCycle, string collectionId) { if (submissionCycle?.SchoolYearId == null) { throw new ArgumentException("Submission cycle is null or contains null SchoolYearId", nameof(submissionCycle)); } var schoolYear = SchoolYearService.GetSchoolYearById(submissionCycle.SchoolYearId.Value); string fourDigitOdsDbYear = schoolYear.EndYear; ValidationReportSummary newReportSummary; using (var odsRawDbContext = OdsDbContextFactory.CreateWithParameter(fourDigitOdsDbYear)) { LoggingService.LogDebugMessage( $"Connecting to the Ed Fi ODS {fourDigitOdsDbYear} to run the Rules Engine. Submitting the RulesValidation run ID."); // Add a new execution of the Validation Engine to the ODS database, (required by the Engine) and get an ID back representing this execution. var newRuleValidationExecution = new RuleValidation { CollectionId = collectionId }; odsRawDbContext.RuleValidations.Add(newRuleValidationExecution); odsRawDbContext.SaveChanges(); LoggingService.LogDebugMessage( $"Successfully submitted RuleValidationId {newRuleValidationExecution.RuleValidationId.ToString()} to the Rules Engine database table."); // Add a new execution of the Validation Engine to the Validation database, (required by the Portal) and get an ID back representing this execution. /* todo: using this (Id, SchoolYearId) as a PK - this isn't reliable because it comes from the ods's id. * we can stomp other execution runs from other districts etc. the ID is the identity column in another database. * it doesn't know about what we're doing ... change the ID to the ods's execution id and set up our own identity column * that's independent (and change all references to this "id" */ newReportSummary = new ValidationReportSummary { Collection = collectionId, CompletedWhen = null, ErrorCount = null, WarningCount = null, TotalCount = 0, RuleValidationId = newRuleValidationExecution.RuleValidationId, EdOrgId = AppUserService.GetSession().FocusedEdOrgId, SchoolYearId = schoolYear.Id, InitiatedBy = AppUserService.GetUser().FullName, RequestedWhen = DateTime.UtcNow, Status = "In Progress - Starting" }; LoggingService.LogDebugMessage( $"Successfully submitted Validation Report Summary ID {newReportSummary.ValidationReportSummaryId} " + $"to the Validation Portal database for Rules Validation Run {newRuleValidationExecution.RuleValidationId.ToString()}."); } using (var validationDbContext = DbContextFactory.Create()) { validationDbContext.ValidationReportSummaries.Add(newReportSummary); validationDbContext.SaveChanges(); } return(newReportSummary); }
public void RunValidation(SubmissionCycle submissionCycle, long ruleValidationId) { if (submissionCycle?.SchoolYearId == null) { throw new ArgumentException("Submission cycle is null, or SchoolYearId is null", nameof(submissionCycle)); } var schoolYear = SchoolYearService.GetSchoolYearById(submissionCycle.SchoolYearId.Value); var fourDigitOdsDbYear = schoolYear.EndYear; using (var odsRawDbContext = OdsDbContextFactory.CreateWithParameter(fourDigitOdsDbYear)) { using (var validationDbContext = DbContextFactory.Create()) { var newReportSummary = validationDbContext .ValidationReportSummaries .Include(x => x.SchoolYear) .FirstOrDefault(x => x.ValidationReportSummaryId == ruleValidationId && x.SchoolYearId == schoolYear.Id); if (newReportSummary == null) { throw new InvalidOperationException($"Unable to find report summary {ruleValidationId} for year {schoolYear.Id}"); } string savedSqlPath = string.Empty; if (EngineConfig.SaveGeneratedRulesSqlToFiles) { savedSqlPath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "ValidationRulesSql", ruleValidationId.ToString()); LoggingService.LogInfoMessage("Saved SQL Directory is " + savedSqlPath); if (Directory.Exists(savedSqlPath)) { LoggingService.LogInfoMessage("Clearing contents of " + savedSqlPath); Directory.Delete(savedSqlPath, true); } Directory.CreateDirectory(savedSqlPath); } var newRuleValidationExecution = odsRawDbContext.RuleValidations .FirstOrDefault(x => x.RuleValidationId == newReportSummary.RuleValidationId); if (newRuleValidationExecution == null) { throw new InvalidOperationException($"Unable to find execution {ruleValidationId}"); } var collectionId = newRuleValidationExecution.CollectionId; // todo: check collectionId for null/empty and why is it a string!? // Now, store each Ruleset ID and Rule ID that the engine will run. Save it in the Engine database. LoggingService.LogDebugMessage($"Getting the rules to run for the chosen collection {collectionId}."); var rules = EngineObjectModel.GetRules(collectionId).ToArray(); var ruleComponents = rules.SelectMany( r => r.Components.Distinct().Select( c => new { r.RulesetId, r.RuleId, Component = c })); foreach (var singleRuleNeedingToBeValidated in ruleComponents) { odsRawDbContext.RuleValidationRuleComponents.Add( new RuleValidationRuleComponent { RuleValidationId = newRuleValidationExecution.RuleValidationId, RulesetId = singleRuleNeedingToBeValidated.RulesetId, RuleId = singleRuleNeedingToBeValidated.RuleId, Component = singleRuleNeedingToBeValidated.Component }); } odsRawDbContext.SaveChanges(); LoggingService.LogDebugMessage($"Saved the rules to run for the chosen collection {collectionId}."); // The ValidationReportDetails is one-for-one with the ValidationReportSummary - it should be refactored away. It contains the error/warning details. LoggingService.LogDebugMessage( $"Adding additional Validation Report details to the Validation Portal database for EdOrgID {newReportSummary.EdOrgId}."); var newReportDetails = new ValidationReportDetails { CollectionName = collectionId, SchoolYearId = newReportSummary.SchoolYear.Id, DistrictName = $"{EdOrgService.GetEdOrgById(newReportSummary.EdOrgId, newReportSummary.SchoolYear.Id).OrganizationName} ({newReportSummary.EdOrgId.ToString()})", ValidationReportSummaryId = newReportSummary.ValidationReportSummaryId }; validationDbContext.ValidationReportDetails.Add(newReportDetails); try { validationDbContext.SaveChanges(); } catch (Exception ex) { LoggingService.LogErrorMessage(ex.ChainInnerExceptionMessages()); } LoggingService.LogDebugMessage( $"Successfully added additional Validation Report details to the Validation Portal database for EdOrgID {newReportSummary.EdOrgId}."); // Execute each individual rule. List <RulesEngineExecutionException> rulesEngineExecutionExceptions = new List <RulesEngineExecutionException>(); for (var i = 0; i < rules.Length; i++) { var rule = rules[i]; try { //Execute the SQL files in here? We have the RuleSetName and the Rule Id //e.g. RuleSetId = MultipleEnrollment // RuleId = 10.10.6175 var toExecute = ManualRuleExecutionService.GetManualSqlFile(rule.RulesetId, rule.RuleId).Result; // By default, rules are run against ALL districts in the Ed Fi ODS. This line filters for multi-district/multi-tenant ODS's. rule.AddDistrictWhereFilter(newReportSummary.EdOrgId); LoggingService.LogDebugMessage($"Executing Rule {rule.RuleId}."); var detailParams = new List <SqlParameter> { new SqlParameter( "@RuleValidationId", newRuleValidationExecution.RuleValidationId) }; detailParams.AddRange( EngineObjectModel.GetParameters(collectionId) .Select(x => new SqlParameter(x.ParameterName, x.Value))); odsRawDbContext.Database.CommandTimeout = EngineConfig.RulesExecutionTimeout; if (toExecute.Count > 0) { foreach (var sql in toExecute) { detailParams.Add( new SqlParameter( "@DistrictId", newReportSummary.EdOrgId)); LoggingService.LogDebugMessage($"Executing Rule SQL {sql}."); var resultManualSql = odsRawDbContext.Database.ExecuteSqlCommand(sql, detailParams.ToArray <object>()); LoggingService.LogDebugMessage($"Executing Rule {rule.RuleId} rows affected = {resultManualSql}."); } } else { if (EngineConfig.SaveGeneratedRulesSqlToFiles && !string.IsNullOrWhiteSpace(savedSqlPath)) { var rulesetPath = Path.Combine(savedSqlPath, rule.RulesetId); if (!Directory.Exists(rulesetPath)) { Directory.CreateDirectory(rulesetPath); } var rulePath = Path.Combine(rulesetPath, rule.RuleId + ".sql"); File.WriteAllText(rulePath, rule.Sql); } LoggingService.LogDebugMessage($"Executing Rule SQL {rule.ExecSql}."); var result = odsRawDbContext.Database.ExecuteSqlCommand(rule.ExecSql, detailParams.ToArray <object>()); LoggingService.LogDebugMessage($"Executing Rule {rule.RuleId} rows affected = {result}."); } // Record the results of this rule in the Validation Portal database, accompanied by more detailed information. PopulateErrorDetailsFromViews( rule, odsRawDbContext, newRuleValidationExecution.RuleValidationId, newReportDetails.Id); newReportSummary.Status = $"In Progress - {(int)((float)i / rules.Length * 100)}% complete"; validationDbContext.SaveChanges(); } catch (Exception ex) { rulesEngineExecutionExceptions.Add( new RulesEngineExecutionException { RuleId = rule.RuleId, Sql = rule.Sql, ExecSql = rule.ExecSql, DataSourceName = $"Database Server: {odsRawDbContext.Database.Connection.DataSource}{Environment.NewLine} " + $"Database: {odsRawDbContext.Database.Connection.Database}", ChainedErrorMessages = ex.ChainInnerExceptionMessages() }); } } LoggingService.LogDebugMessage("Counting errors and warnings."); newReportSummary.CompletedWhen = DateTime.UtcNow; newReportSummary.ErrorCount = odsRawDbContext.RuleValidationDetails.Count( rvd => rvd.RuleValidation.RuleValidationId == newRuleValidationExecution.RuleValidationId && rvd.IsError); newReportSummary.WarningCount = odsRawDbContext.RuleValidationDetails.Count( rvd => rvd.RuleValidation.RuleValidationId == newRuleValidationExecution.RuleValidationId && !rvd.IsError); var hasExecutionErrors = rulesEngineExecutionExceptions.Count > 0; newReportSummary.Status = hasExecutionErrors ? $"Completed - {rulesEngineExecutionExceptions.Count} rules did not execute, ask an administrator to check the log for errors, Report Summary Number {newReportSummary.ValidationReportSummaryId.ToString()}" : "Completed"; LoggingService.LogDebugMessage($"Saving status {newReportSummary.Status}."); // Log Execution Errors LoggingService.LogErrorMessage( GetLogExecutionErrorsMessage(rulesEngineExecutionExceptions, newReportSummary.ValidationReportSummaryId)); newReportDetails.CompletedWhen = newReportDetails.CompletedWhen ?? DateTime.UtcNow; validationDbContext.SaveChanges(); LoggingService.LogDebugMessage("Saved status."); } } }
public void RunValidation(SubmissionCycle submissionCycle, long ruleValidationId) { var schoolYear = _schoolYearService.GetSchoolYearById(submissionCycle.SchoolYearId.Value); string fourDigitOdsDbYear = schoolYear.EndYear; // todo: dependency inject the initialization of RawOdsDbContext with a year. introduce a factory for most simple implementation using (var odsRawDbContext = new RawOdsDbContext(fourDigitOdsDbYear)) { using (var validationDbContext = DbContextFactory.Create()) { var newReportSummary = validationDbContext .ValidationReportSummaries .Include(x => x.SchoolYear) .FirstOrDefault(x => x.ValidationReportSummaryId == ruleValidationId && x.SchoolYearId == schoolYear.Id); var newRuleValidationExecution = odsRawDbContext.RuleValidations.FirstOrDefault(x => x.RuleValidationId == newReportSummary.RuleValidationId); var collectionId = newRuleValidationExecution.CollectionId; #region Now, store each Ruleset ID and Rule ID that the engine will run. Save it in the Engine database. _loggingService.LogDebugMessage($"Getting the rules to run for the chosen collection {collectionId}."); var rules = _engineObjectModel.GetRules(collectionId).ToArray(); var ruleComponents = rules.SelectMany( r => r.Components.Distinct().Select( c => new { r.RulesetId, r.RuleId, Component = c })); foreach (var singleRuleNeedingToBeValidated in ruleComponents) { odsRawDbContext.RuleValidationRuleComponents.Add( new RuleValidationRuleComponent { RuleValidationId = newRuleValidationExecution.RuleValidationId, RulesetId = singleRuleNeedingToBeValidated.RulesetId, RuleId = singleRuleNeedingToBeValidated.RuleId, Component = singleRuleNeedingToBeValidated.Component }); } odsRawDbContext.SaveChanges(); _loggingService.LogDebugMessage($"Saved the rules to run for the chosen collection {collectionId}."); #endregion Now, store each Ruleset ID and Rule ID that the engine will run. Save it in the Engine database. #region The ValidationReportDetails is one-for-one with the ValidationReportSummary - it should be refactored away. It contains the error/warning details. _loggingService.LogDebugMessage( $"Adding additional Validation Report details to the Validation Portal database for EdOrgID {newReportSummary.EdOrgId}."); var newReportDetails = new ValidationReportDetails { CollectionName = collectionId, SchoolYearId = newReportSummary.SchoolYear.Id, DistrictName = $"{_edOrgService.GetEdOrgById(newReportSummary.EdOrgId, newReportSummary.SchoolYear.Id).OrganizationName} ({newReportSummary.EdOrgId.ToString()})", ValidationReportSummaryId = newReportSummary.ValidationReportSummaryId }; validationDbContext.ValidationReportDetails.Add(newReportDetails); try { validationDbContext.SaveChanges(); } catch (Exception ex) { _loggingService.LogErrorMessage(ex.ChainInnerExceptionMessages()); } _loggingService.LogDebugMessage( $"Successfully added additional Validation Report details to the Validation Portal database for EdOrgID {newReportSummary.EdOrgId}."); #endregion The ValidationReportDetails is one-for-one with the ValidationReportSummary - it should be refactored away. It contains the error/warning details. #region Execute each individual rule. List <RulesEngineExecutionException> rulesEngineExecutionExceptions = new List <RulesEngineExecutionException>(); for (var i = 0; i < rules.Length; i++) { var rule = rules[i]; try { // By default, rules are run against ALL districts in the Ed Fi ODS. This line filters for multi-district/multi-tenant ODS's. // rule.AddDistrictWhereFilter(newReportSummary.EdOrgId); _loggingService.LogDebugMessage($"Executing Rule {rule.RuleId}."); _loggingService.LogDebugMessage($"Executing Rule SQL {rule.Sql}."); var detailParams = new List <SqlParameter> { new SqlParameter( "@RuleValidationId", newRuleValidationExecution.RuleValidationId) }; detailParams.AddRange( _engineObjectModel.GetParameters(collectionId) .Select(x => new SqlParameter(x.ParameterName, x.Value))); odsRawDbContext.Database.CommandTimeout = 60; var result = odsRawDbContext.Database.ExecuteSqlCommand(rule.ExecSql, detailParams.ToArray()); _loggingService.LogDebugMessage($"Executing Rule {rule.RuleId} rows affected = {result}."); #region Record the results of this rule in the Validation Portal database, accompanied by more detailed information. PopulateErrorDetailsFromViews( rule, odsRawDbContext, newRuleValidationExecution.RuleValidationId, newReportDetails.Id); newReportSummary.Status = $"In Progress - {(int)((float)i / rules.Length * 100)}% complete"; validationDbContext.SaveChanges(); #endregion Record the results of this rule in the Validation Portal database, accompanied by more detailed information. } catch (Exception ex) { rulesEngineExecutionExceptions.Add( new RulesEngineExecutionException { RuleId = rule.RuleId, Sql = rule.Sql, ExecSql = rule.ExecSql, DataSourceName = $"Database Server: {odsRawDbContext.Database.Connection.DataSource}{Environment.NewLine} Database: {odsRawDbContext.Database.Connection.Database}", ChainedErrorMessages = ex.ChainInnerExceptionMessages() }); } } #endregion Execute each individual rule. _loggingService.LogDebugMessage($"Counting errors and warnings."); newReportSummary.CompletedWhen = DateTime.UtcNow; newReportSummary.ErrorCount = odsRawDbContext.RuleValidationDetails.Count( rvd => rvd.RuleValidation.RuleValidationId == newRuleValidationExecution.RuleValidationId && rvd.IsError); newReportSummary.WarningCount = odsRawDbContext.RuleValidationDetails.Count( rvd => rvd.RuleValidation.RuleValidationId == newRuleValidationExecution.RuleValidationId && !rvd.IsError); var hasExecutionErrors = rulesEngineExecutionExceptions.Count > 0; newReportSummary.Status = hasExecutionErrors ? $"Completed - {rulesEngineExecutionExceptions.Count} rules did not execute, ask an administrator to check the log for errors, Report Summary Number {newReportSummary.ValidationReportSummaryId.ToString()}" : "Completed"; _loggingService.LogDebugMessage($"Saving status {newReportSummary.Status}."); // Log Execution Errors _loggingService.LogErrorMessage( GetLogExecutionErrorsMessage(rulesEngineExecutionExceptions, newReportSummary.ValidationReportSummaryId)); newReportDetails.CompletedWhen = newReportDetails.CompletedWhen ?? DateTime.UtcNow; validationDbContext.SaveChanges(); _loggingService.LogDebugMessage($"Saved status."); } } }