Exemplo n.º 1
0
        /// <summary>
        /// This transfers error messages from the ValidationResult collection to the MVC modelState error dictionary.
        /// It looks for errors that have member names corresponding to the properties in the displayDto.
        /// This means that errors associated with a field on display will show next to the name.
        /// Other errors will be shown in the ValidationSummary
        /// </summary>
        /// <param name="status">The status that came back from the BizRunner</param>
        /// <param name="modelState">The MVC modelState to add errors to</param>
        /// <param name="displayDto">This is the Dto that will be used to display the error messages</param>
        public static void CopyErrorsToModelState <T>(this IStatusGeneric status, ModelStateDictionary modelState, T displayDto)
        {
            if (!status.HasErrors)
            {
                return;
            }

            var namesThatWeShouldInclude = PropertyNamesInDto(displayDto);

            foreach (var error in status.Errors)
            {
                if (!error.MemberNames.Any())
                {
                    modelState.AddModelError("", error.ErrorMessage);
                }
                else
                {
                    foreach (var errorKeyName in error.MemberNames)
                    {
                        modelState.AddModelError(
                            (namesThatWeShouldInclude.Any(x => x == errorKeyName) ? errorKeyName : ""),
                            error.ErrorMessage);
                    }
                }
            }
        }
Exemplo n.º 2
0
        private IStatusGeneric <int> //#A
        CallSaveChangesWithExceptionHandler
            (DbContext context,
            Func <int> callBaseSaveChanges)                //#B
        {
            var status = new StatusGenericHandler <int>(); //#C

            do                                             //#D
            {
                try
                {
                    int numUpdated = callBaseSaveChanges(); //#E
                    status.SetResult(numUpdated);           //#F
                    break;                                  //#F
                }
                catch (Exception e)                         //#G
                {
                    IStatusGeneric handlerStatus = null;    //#H
                    if (handlerStatus == null)              //#I
                    {
                        throw;                              //#I
                    }
                    status.CombineStatuses(handlerStatus);  //#J
                }
            } while (status.IsValid);                       //#K

            return(status);                                 //#L
        }
        public MDRDocument BizAction(MDRDocumentDto inputData)
        {
            if (string.IsNullOrWhiteSpace(inputData.Title))
            {
                AddError("title is Required.");
            }
            if (string.IsNullOrWhiteSpace(inputData.FolderName))
            {
                AddError("Folder Name is invalied.");
            }

            var defaultStatus = _dbStatusAccess.GetDefaultStatus(inputData.ProjectId);

            if (defaultStatus == null)
            {
                AddError("Default MDR Status Not Exit.");
            }

            IStatusGeneric <MDRDocument> desStatus = null;

            if (!HasErrors)
            {
                desStatus = MDRDocument.CreateMDRDocument(inputData.Title, inputData.Description,
                                                          inputData.WorkPackageId, inputData.Code, defaultStatus.Id, inputData.ProjectId, inputData.Type);

                var mdr = desStatus.Result;
                mdr.CreateMDRStatus("CREATE MDR", defaultStatus.Id, inputData.FolderName);

                _dbAccess.Add(desStatus.Result);
                CombineErrors(desStatus);
            }

            return(HasErrors ? null : desStatus.Result);
        }
Exemplo n.º 4
0
        public void BizAction(PunchGoDto inputData)
        {
            var punch = _dbAccess.GetPunch(inputData.Id);

            if (punch == null)
            {
                AddError("Could not find the punch. Someone entering illegal ids?");
                return;
            }

            if (punch.CheckDate.HasValue)
            {
                AddError("punch is approved and do not allow for modify");
                return;
            }

            IStatusGeneric status = null;

            if (!punch.ClearDate.HasValue)
            {
                status = punch.UpdateClear(inputData.ClearBy, inputData.ClearDate);
            }
            else if (!punch.CheckDate.HasValue)
            {
                status = punch.UpdateApprove(inputData.CheckBy, inputData.ApproveBy, inputData.CheckDate);
            }

            CombineErrors(status);

            Message = $"punch is update: {punch.ToString()}.";
        }
Exemplo n.º 5
0
        /// <summary>
        /// This ensures there is a UserToRole linking the userId to the given roleName
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="roleName"></param>
        public async Task AddRoleToUserAsync(string userId, string roleName)
        {
            IStatusGeneric <UserToRole> status = await UserToRole.AddRoleToUserAsync(userId, roleName, _repository);

            if (status.IsValid)
            {
                //we assume there is already a link to the role is the status wasn't valid
                await _repository.AddAsync(status.Result);
            }
        }
Exemplo n.º 6
0
        /// <summary>
        /// This allows you to return a CreatedAtRoute result for a Create
        /// </summary>
        /// <param name="status"></param>
        /// <param name="createdRoute"></param>
        /// <returns></returns>
        public static IActionResult Response(this IStatusGeneric status, CreatedAtRouteResult createdRoute)
        {
            if (status.IsValid)
            {
                return(createdRoute);
            }

            //it has errors
            return(CreateBadRequestObjectResult(status.Errors.Select(x => x.ErrorResult)));
        }
Exemplo n.º 7
0
        /// <summary>
        /// This allows you to return a CreatedAtRoute result for a Create
        /// </summary>
        /// <param name="status"></param>
        /// <param name="controller"></param>
        /// <param name="routeName">The values needed to work with the HttpGet to return the correct item</param>
        /// <param name="routeValues"></param>
        /// <param name="dto"></param>
        /// <returns></returns>
        public static ActionResult <T> Response <T>(this IStatusGeneric status,
                                                    ControllerBase controller, string routeName, object routeValues, T dto)
        {
            if (status.IsValid)
            {
                return(controller.CreatedAtRoute(routeName, routeValues, dto));
            }

            //it has errors
            return(CreateBadRequestObjectResult(status.Errors.Select(x => x.ErrorResult)));
        }
Exemplo n.º 8
0
        /// <summary>
        /// This is a spacial form of SaveChanges that returns an <see cref="T:IStatusGeneric{int}"/> status
        /// </summary>
        /// <param name="acceptAllChangesOnSuccess">normal SaveChanges option</param>
        /// <returns>Status, with a Result that is the number of updates down by SaveChanges</returns>
        public IStatusGeneric <int> SaveChangesWithStatus(bool acceptAllChangesOnSuccess = true)
        {
            if (_eventsRunner == null)
            {
                throw new GenericEventRunnerException($"The {nameof(SaveChangesWithStatus)} cannot be used unless the event runner is present");
            }

            StatusFromLastSaveChanges = _eventsRunner.RunEventsBeforeAfterSaveChanges(this, () => ChangeTracker.Entries <EntityEvents>(),
                                                                                      () => base.SaveChanges(acceptAllChangesOnSuccess));

            return(StatusFromLastSaveChanges);
        }
Exemplo n.º 9
0
        /// <summary>
        /// This copies errors for general display where we are not returning to a page with the fields on them
        /// </summary>
        /// <param name="status"></param>
        /// <param name="modelState"></param>
        public static void CopyErrorsToModelState(this IStatusGeneric status, ModelStateDictionary modelState)
        {
            if (!status.HasErrors)
            {
                return;
            }

            foreach (var error in status.Errors)
            {
                modelState.AddModelError("", error.ErrorMessage);
            }
        }
Exemplo n.º 10
0
        /// <summary>
        /// This is a spacial form of SaveChangesAsync that returns an <see cref="T:IStatusGeneric{int)"/> status
        /// </summary>
        /// <param name="acceptAllChangesOnSuccess"></param>
        /// <param name="cancellationToken"></param>
        /// <returns>Status, with a Result that is the number of updates down by SaveChangesAsync</returns>
        public async Task <IStatusGeneric <int> > SaveChangesWithStatusAsync(bool acceptAllChangesOnSuccess      = true,
                                                                             CancellationToken cancellationToken = default)
        {
            if (_eventsRunner == null)
            {
                throw new GenericEventRunnerException($"The {nameof(SaveChangesWithStatusAsync)} cannot be used unless the event runner is present");
            }

            StatusFromLastSaveChanges = await _eventsRunner.RunEventsBeforeAfterSaveChangesAsync(this, () => ChangeTracker.Entries <EntityEvents>(),
                                                                                                 () => base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken));

            return(StatusFromLastSaveChanges);
        }
Exemplo n.º 11
0
        /// <summary>
        /// This transfers error messages from the ValidationResult collection to the MVC modelState error dictionary.
        /// It looks for errors that have member names corresponding to the properties in the displayDto.
        /// This means that errors associated with a field on display will show next to the name.
        /// Other errors will be shown in the ValidationSummary
        /// </summary>
        /// <param name="status">The status that came back from the BizRunner</param>
        /// <param name="modelState">The MVC modelState to add errors to</param>
        /// <param name="displayDto">This is the Dto that will be used to display the error messages</param>
        /// <param name="modelName">When using razor pages you need to prefix the member name by the name of the model's property</param>
        public static void CopyErrorsToModelState <T>(this IStatusGeneric status, ModelStateDictionary modelState, T displayDto, string modelName = null)
        {
            if (status.IsValid)
            {
                return;
            }
            if (displayDto == null)
            {
                status.CopyErrorsToModelState(modelState);
                return;
            }

            CopyErrorsWithFilterOnDto(status.Errors.Select(x => x.ErrorResult), modelState, displayDto, modelName);
        }
Exemplo n.º 12
0
        /// <summary>
        /// If the status has no errors then it will HTTP response with the status code provided in the
        /// validStatusCode property and the message from the status
        /// otherwise it will returns a HTTP 400 with the error information in the standard WebAPI format
        /// </summary>
        /// <param name="status"></param>
        /// <param name="validStatusCode">HTTP status code for non-error status</param>
        /// <returns></returns>
        public static ActionResult <WebApiMessageOnly> ResponseWithValidCode(this IStatusGeneric status, int validStatusCode)
        {
            if (status.IsValid)
            {
                return new ObjectResult(new WebApiMessageOnly(status))
                       {
                           StatusCode = validStatusCode
                       }
            }
            ;

            //it has errors
            return(CreateBadRequestObjectResult(status.Errors.Select(x => x.ErrorResult)));
        }
        //I only have to override these two version of SaveChanges, as the other two versions call these

        /// <summary>
        /// EF Core's SaveChanges, but with domain event handling added
        /// Throws an exception if any of the BeforeSave event handlers return a status with an error in it.
        /// </summary>
        /// <param name="acceptAllChangesOnSuccess"></param>
        /// <returns>number of writes done to the database</returns>
        public override int SaveChanges(bool acceptAllChangesOnSuccess)
        {
            if (_eventsRunner == null)
            {
                return(base.SaveChanges(acceptAllChangesOnSuccess));
            }

            StatusFromLastSaveChanges = SaveChangesWithStatus(acceptAllChangesOnSuccess);

            if (StatusFromLastSaveChanges.IsValid)
            {
                return(StatusFromLastSaveChanges.Result);
            }

            throw new GenericEventRunnerStatusException(StatusFromLastSaveChanges);
        }
Exemplo n.º 14
0
        public Book AddPromotion(AddRemovePromotionDto dto)
        {
            var book = _context.Find <Book>(dto.BookId);

            if (book == null)
            {
                throw new InvalidOperationException($"Could not find the book with Id of {dto.BookId}.");
            }
            Status = book.AddPromotion(dto.ActualPrice, dto.PromotionalText);
            if (Status.HasErrors)
            {
                return(null);
            }

            _context.SaveChanges();
            return(book);
        }
Exemplo n.º 15
0
        //I only have to override these two version of SaveChanges, as the other two versions call these

        /// <summary>
        /// EF Core's SaveChanges, but with domain event handling added
        /// Throws an exception if any of the BeforeSave event handlers return a status with an error in it.
        /// </summary>
        /// <param name="acceptAllChangesOnSuccess"></param>
        /// <returns>number of writes done to the database</returns>
        public override int SaveChanges(bool acceptAllChangesOnSuccess)
        {
            if (_eventsRunner == null)
            {
                return(base.SaveChanges(acceptAllChangesOnSuccess));
            }

            StatusFromLastSaveChanges = _eventsRunner.RunEventsBeforeAfterSaveChanges(this, () => ChangeTracker.Entries <EntityEvents>(),
                                                                                      () => base.SaveChanges(acceptAllChangesOnSuccess));

            if (StatusFromLastSaveChanges.IsValid)
            {
                return(StatusFromLastSaveChanges.Result);
            }

            throw new GenericEventRunnerStatusException(StatusFromLastSaveChanges);
        }
Exemplo n.º 16
0
        /// <summary>
        /// This allows statuses to be combined. Copies over any errors and replaces the Message if the currect message is null
        /// If you are using Headers then it will combine the headers in any errors in combines
        /// e.g. Status1 with header "MyClass" combines Status2 which has header "MyProp" and status2 has errors.
        /// The result would be error message in status2 would be updates to start with "MyClass>MyProp: This is my error message."
        /// </summary>
        /// <param name="status"></param>
        public IStatusGeneric <T> CombineStatuses(IStatusGeneric <T> status)
        {
            if (!status.IsValid)
            {
                _errors.AddRange(string.IsNullOrEmpty(Header)
          ? status.Errors
          : status.Errors.Select(x => new ErrorGeneric(Header, x)));
            }

            if (IsValid && status.Message != DefaultSuccessMessage)
            {
                Message = status.Message;
            }

            StatusCode ??= status.StatusCode;

            return(this);
        }
        /// <summary>
        /// EF Core's SaveChanges, but with domain event handling added
        /// Throws an exception if any of the BeforeSave event handlers return a status with an error in it.
        /// </summary>
        /// <param name="acceptAllChangesOnSuccess"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public override async Task <int> SaveChangesAsync(bool acceptAllChangesOnSuccess,
                                                          CancellationToken cancellationToken = default)
        {
            if (_eventsRunner == null)
            {
                return(await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken).ConfigureAwait(false));
            }

            StatusFromLastSaveChanges = await SaveChangesWithStatusAsync(acceptAllChangesOnSuccess, cancellationToken)
                                        .ConfigureAwait(false);

            if (StatusFromLastSaveChanges.IsValid)
            {
                return(StatusFromLastSaveChanges.Result);
            }

            throw new GenericEventRunnerStatusException(StatusFromLastSaveChanges);
        }
Exemplo n.º 18
0
        /// <summary>
        /// EF Core's SaveChanges, but with domain event handling added
        /// Throws an exception if any of the BeforeSave event handlers return a status with an error in it.
        /// </summary>
        /// <param name="acceptAllChangesOnSuccess"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public override async Task <int> SaveChangesAsync(bool acceptAllChangesOnSuccess,
                                                          CancellationToken cancellationToken = default)
        {
            if (_eventsRunner == null)
            {
                return(await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken).ConfigureAwait(false));
            }

            StatusFromLastSaveChanges = await _eventsRunner.RunEventsBeforeAfterSaveChangesAsync(this, () => ChangeTracker.Entries <EntityEvents>(),
                                                                                                 () => base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken));

            if (StatusFromLastSaveChanges.IsValid)
            {
                return(StatusFromLastSaveChanges.Result);
            }

            throw new GenericEventRunnerStatusException(StatusFromLastSaveChanges);
        }
Exemplo n.º 19
0
        /// <summary>
        /// This adds a role if not present, or updates a role if is present.
        /// </summary>
        /// <param name="roleName"></param>
        /// <param name="description"></param>
        /// <param name="permissions"></param>
        public async Task AddOrUpdateRoleToPermissionsAsync(string roleName, string description, params Permission[] permissions)
        {
            IStatusGeneric <RoleToPermissions> status =
                await RoleToPermissions.CreateRoleWithPermissionsAsync(
                    roleName,
                    description,
                    permissions,
                    _repository);

            if (status.IsValid)
            {
                //Note that CreateRoleWithPermissions will return a invalid status if the role is already present.
                await _repository.AddAsync(status.Result);
            }
            else
            {
                await UpdateRoleAsync(roleName, description, permissions);
            }
        }
Exemplo n.º 20
0
        public static string CopyErrorsToString(this IStatusGeneric status, ModelStateDictionary modelState)
        {
            string lstErrors = "";

            foreach (var error in status.Errors)
            {
                lstErrors += error.ErrorMessage + "\n";
            }

            foreach (var state in modelState)
            {
                foreach (var error in state.Value.Errors)
                {
                    lstErrors += error.ErrorMessage + "\n";
                }
            }

            return(lstErrors);
        }
Exemplo n.º 21
0
        public IImmutableList <ValidationResult> AddQuestionGroup(SurveyDto dto)
        {
            var survey = _context.Find <Survey>(dto.Id);

            if (survey == null)
            {
                Status.AddError($"Could not find Survey with an Id of {dto.Id}");
                return(Status.Errors);
            }

            var questionGroups = _questionMapper.Map(dto.QuestionGroupsDtos, _context);

            Status = survey.AddQuestionGroups(questionGroups, _context);
            if (Status.HasErrors)
            {
                return(Status.Errors);
            }
            _context.SaveChanges();
            return(null);
        }
Exemplo n.º 22
0
        private async Task <IStatusGeneric <int> > CallSaveChangesWithExceptionHandlerAsync(DbContext context, Func <Task <int> > callBaseSaveChangesAsync)
        {
            var status = new StatusGenericHandler <int>();

            do
            {
                try
                {
                    context.ChangeTracker.AutoDetectChangesEnabled = false;

                    status.SetResult(await callBaseSaveChangesAsync().ConfigureAwait(false));
                    break; //This breaks out of the do/while
                }
                catch (Exception e)
                {
                    IStatusGeneric exceptionStatus = null;
                    if (_config.ExceptionHandlerDictionary.TryGetValue(context.GetType(), out var exceptionHandler))
                    {
                        exceptionStatus = exceptionHandler(e, context);
                    }
                    if (exceptionStatus == null)
                    {
                        //This means the SaveChangesExceptionHandler doesn't cover this type of Concurrency Exception
                        throw;
                    }
                    //SaveChangesExceptionHandler ran, so combine its error into the outer status
                    status.CombineStatuses(exceptionStatus);
                }
                finally
                {
                    context.ChangeTracker.AutoDetectChangesEnabled = true;
                }

                //If the SaveChangesExceptionHandler fixed the problem then we call SaveChanges again, but with the same exception catching.
            } while (status.IsValid);

            return(status);
        }
Exemplo n.º 23
0
 /// <summary>
 /// This is used to create a Message-only response from new GenericServices
 /// </summary>
 /// <param name="status"></param>
 public WebApiMessageOnly(IStatusGeneric status)
 {
     Message = status.Message;
 }
Exemplo n.º 24
0
        /// <summary>
        /// This will return a result value, with the status Message. There are three possibilities:
        /// 1. If there are no errors and the result is not null it will return a HTTP response with the status code provided
        ///    in the validStatusCode property, plus a json containing the message from the status and the results object
        /// 2. If there are no errors but result is  null it will return a HTTP 204 (NoContent) with the status Message
        /// 3. If there are errors it returns a HTTP 400 with the error information in the standard WebAPI format
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="status"></param>
        /// <param name="results"></param>
        /// <param name="validStatusCode">The status code to return when the status has no errors and the result is not null</param>
        /// <param name="nullResultStatusCode">Optional, default is 204: The status code to return if the there ar no errors, but the result is null</param>
        /// <returns></returns>
        public static ActionResult <WebApiMessageAndResult <T> > ResponseWithValidCode <T>(this IStatusGeneric status, T results,
                                                                                           int validStatusCode, int nullResultStatusCode = ResultIsNullStatusCode)
        {
            if (status.IsValid)
            {
                return new ObjectResult(new WebApiMessageAndResult <T>(status, results))
                       {
                           StatusCode = results == null ? nullResultStatusCode : validStatusCode
                       }
            }
            ;

            //it has errors
            return(CreateBadRequestObjectResult(status.Errors.Select(x => x.ErrorResult)));
        }
Exemplo n.º 25
0
        public static void CheckResponseWithValidCode <T>(this ActionResult <WebApiMessageAndResult <T> > actionResult, IStatusGeneric status, T results,
                                                          int validStatusCode, int nullResultStatusCode = CreateResponse.ResultIsNullStatusCode)
        {
            actionResult.ShouldNotBeNull();
            var result = actionResult.Result as ObjectResult;

            if (status.IsValid)
            {
                result.ShouldNotBeNull();
                result.StatusCode.ShouldEqual(results == null ? nullResultStatusCode : validStatusCode);
                var returnClass = result.Value as WebApiMessageAndResult <T>;
                returnClass?.Message.ShouldEqual(status.Message);
                returnClass?.Results.ShouldEqual(results);
            }
            else
            {
                CheckErrorResponse(result as BadRequestObjectResult, status.Errors.Select(x => x.ErrorResult));
            }
        }
Exemplo n.º 26
0
 public static void CheckResponse <T>(this ActionResult <WebApiMessageAndResult <T> > actionResult, IStatusGeneric status, T results)
 {
     actionResult.CheckResponseWithValidCode <T>(status, results, CreateResponse.OkStatusCode);
 }
Exemplo n.º 27
0
        public static void CheckResponseWithValidCode(this ActionResult <WebApiMessageOnly> actionResult, IStatusGeneric status, int validStatusCode)
        {
            actionResult.ShouldNotBeNull();
            var result = actionResult.Result as ObjectResult;

            if (status.IsValid)
            {
                actionResult.ShouldNotBeNull();
                Assert.NotNull(result);
                result.StatusCode.ShouldEqual(validStatusCode);
                var returnClass = result.Value as WebApiMessageOnly;
                returnClass?.Message.ShouldEqual(status.Message);
            }
            else
            {
                CheckErrorResponse(result as BadRequestObjectResult, status.Errors.Select(x => x.ErrorResult));
            }
        }
Exemplo n.º 28
0
 public static void CheckResponse(this ActionResult <WebApiMessageOnly> actionResult, IStatusGeneric status)
 {
     actionResult.CheckResponseWithValidCode(status, CreateResponse.OkStatusCode);
 }
Exemplo n.º 29
0
        /// <summary>
        /// This runs the events before, during and after the base SaveChangesAsync method is run
        /// </summary>
        /// <param name="context">The current DbContext</param>
        /// <param name="callBaseSaveChangesAsync">A function that is linked to the base SaveChangesAsync in your DbContext</param>
        /// <param name="cancellationToken"></param>
        /// <returns>Returns the status with the numUpdated number from SaveChanges</returns>
        public async Task <IStatusGeneric <int> > RunEventsBeforeDuringAfterSaveChangesAsync(DbContext context,
                                                                                             Func <Task <int> > callBaseSaveChangesAsync, CancellationToken cancellationToken)
        {
            async Task <IStatusGeneric <int> > RunTransactionWithDuringSaveChangesEventsAsync()
            {
                var localStatus = new StatusGenericHandler <int>();

                //If there is a current transaction then use that, otherwise
                using var transaction = context.Database.CurrentTransaction == null
                    ? await context.Database.BeginTransactionAsync(cancellationToken).ConfigureAwait(false)
                    : null;

                var duringPreStatus = await _eachEventRunner.RunDuringSaveChangesEventsAsync(context, false, true)
                                      .ConfigureAwait(false);

                localStatus.CombineStatuses(duringPreStatus);

                var transactionSaveChanges = await CallSaveChangesWithExceptionHandlerAsync(context, callBaseSaveChangesAsync)
                                             .ConfigureAwait(false);

                if (localStatus.CombineStatuses(transactionSaveChanges).HasErrors)
                {
                    return(localStatus);
                }

                localStatus.SetResult(transactionSaveChanges.Result);

                var duringPostStatus = await _eachEventRunner.RunDuringSaveChangesEventsAsync(context, true, true)
                                       .ConfigureAwait(false);

                if (localStatus.CombineStatuses(duringPostStatus).HasErrors)
                {
                    return(localStatus);
                }

                if (transaction != null)
                {
                    await transaction.CommitAsync(cancellationToken).ConfigureAwait(false);
                }

                return(localStatus);
            }

            var status          = new StatusGenericHandler <int>();
            var hasDuringEvents = _eachEventRunner.SetupDuringEvents(context);

            status.CombineStatuses(await _eachEventRunner.RunBeforeSaveChangesEventsAsync(context, true).ConfigureAwait(false));
            if (!status.IsValid)
            {
                return(status);
            }

            context.ChangeTracker.DetectChanges();

            //This runs any actions adding to the config that match this DbContext type
            RunAnyAfterDetectChangesActions(context);

            //Call SaveChangesAsync with catch for exception handler
            IStatusGeneric <int> callSaveChangesStatus;

            if (!hasDuringEvents)
            {
                //No need for a transaction as no During event. Therefore just call SaveChanges
                callSaveChangesStatus = await CallSaveChangesWithExceptionHandlerAsync(context, callBaseSaveChangesAsync)
                                        .ConfigureAwait(false);
            }
            else if (context.Database.CurrentTransaction == null && context.Database.CreateExecutionStrategy().RetriesOnFailure)
            {
                //There is no existing transactions AND we have to handle retries, then we need to wrap the transaction in a retry
                callSaveChangesStatus = await context.Database.CreateExecutionStrategy().ExecuteAsync(async x =>
                                                                                                      await RunTransactionWithDuringSaveChangesEventsAsync().ConfigureAwait(false), cancellationToken);

                context.ClearDuringEvents();  //clear During events after a successful transaction
            }
            else
            {
                callSaveChangesStatus = await RunTransactionWithDuringSaveChangesEventsAsync().ConfigureAwait(false);

                context.ClearDuringEvents();  //clear During events after a successful transaction
            }

            if (status.CombineStatuses(callSaveChangesStatus).HasErrors)
            {
                return(status);
            }
            do
            {
                try
                {
                    status.SetResult(await callBaseSaveChangesAsync().ConfigureAwait(false));
                    break; //This breaks out of the do/while
                }
                catch (Exception e)
                {
                    IStatusGeneric exceptionStatus = null;
                    if (_config.ExceptionHandlerDictionary.TryGetValue(context.GetType(), out var exceptionHandler))
                    {
                        exceptionStatus = exceptionHandler(e, context);
                    }
                    if (exceptionStatus == null)
                    {
                        //This means the SaveChangesExceptionHandler doesn't cover this type of Concurrency Exception
                        throw;
                    }
                    //SaveChangesExceptionHandler ran, so combine its error into the outer status
                    status.CombineStatuses(exceptionStatus);
                }
                //If the SaveChangesExceptionHandler fixed the problem then we call SaveChanges again, but with the same exception catching.
            } while (status.IsValid);
            await _eachEventRunner.RunAfterSaveChangesEventsAsync(context, true).ConfigureAwait(false);

            return(status);
        }
Exemplo n.º 30
0
 /// <summary>
 /// This will return a HTTP 200 with the status message if Valid,
 /// otherwise it will returns a HTTP 400 with the error information in the standard WebAPI format
 /// </summary>
 /// <param name="status"></param>
 /// <returns></returns>
 public static ActionResult <WebApiMessageOnly> Response(this IStatusGeneric status)
 {
     return(status.ResponseWithValidCode(OkStatusCode));
 }