コード例 #1
0
        /// <summary>
        /// This runs the events before, during, and after the base SaveChanges method is run
        /// </summary>
        /// <param name="context">The current DbContext</param>
        /// <param name="callBaseSaveChanges">A function that is linked to the base SaveChanges in your DbContext</param>
        /// <returns>Returns the status with the numUpdated number from SaveChanges</returns>
        public IStatusGeneric <int> RunEventsBeforeDuringAfterSaveChanges(DbContext context, Func <int> callBaseSaveChanges)
        {
            var eachEventRunner = new RunEachTypeOfEvents(_serviceProvider, _logger, _config);

            IStatusGeneric <int> RunTransactionWithDuringSaveChangesEvents()
            {
                var localStatus = new StatusGenericHandler <int>();

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

                var duringPreValueTask = eachEventRunner.RunDuringSaveChangesEventsAsync(false, false);

                duringPreValueTask.CheckSyncValueTaskWorked();
                duringPreValueTask.CheckSyncValueTaskWorked();
                localStatus.CombineStatuses(duringPreValueTask.Result);

                var transactionSaveChanges = CallSaveChangesWithExceptionHandler(context, callBaseSaveChanges);

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

                localStatus.SetResult(transactionSaveChanges.Result);

                var duringPostValueTask = eachEventRunner.RunDuringSaveChangesEventsAsync(true, false);

                duringPostValueTask.CheckSyncValueTaskWorked();
                if (localStatus.CombineStatuses(duringPostValueTask.Result).HasErrors)
                {
                    return(localStatus);
                }

                transaction?.Commit();

                return(localStatus);
            }

            var status = new StatusGenericHandler <int>();

            var beforeValueTask = eachEventRunner.RunBeforeSaveChangesEventsAsync(context, false);

            beforeValueTask.CheckSyncValueTaskWorked();
            status.CombineStatuses(beforeValueTask.Result);
            if (!status.IsValid)
            {
                return(status);
            }

            var hasDuringEvents = !_config.NotUsingDuringSaveHandlers && eachEventRunner.SetupDuringEvents(context);

            context.ChangeTracker.DetectChanges();

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

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

            if (!hasDuringEvents)
            {
                //No need for a transaction as no During event. Therefore just call SaveChanges
                callSaveChangesStatus = CallSaveChangesWithExceptionHandler(context, callBaseSaveChanges);
            }
            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 = context.Database.CreateExecutionStrategy().Execute(RunTransactionWithDuringSaveChangesEvents);
                context.ClearDuringEvents();  //clear During events after a successful transaction
            }
            else
            {
                callSaveChangesStatus = RunTransactionWithDuringSaveChangesEvents();
                context.ClearDuringEvents();  //clear During events after a successful transaction
            }

            if (status.CombineStatuses(callSaveChangesStatus).HasErrors)
            {
                return(status);
            }

            //Copy over the saveChanges result
            status.SetResult(callSaveChangesStatus.Result);

            var afterValueTask = eachEventRunner.RunAfterSaveChangesEventsAsync(context, false);

            afterValueTask.CheckSyncValueTaskWorked();

            return(status);
        }
コード例 #2
0
 /// <summary>
 /// This is the class that will manage the events inside your DbContext
 /// </summary>
 /// <param name="serviceProvider"></param>
 /// <param name="logger"></param>
 /// <param name="config"></param>
 public EventsRunner(IServiceProvider serviceProvider, ILogger <EventsRunner> logger, IGenericEventRunnerConfig config)
 {
     _config          = config ?? throw new ArgumentNullException(nameof(config));
     _eachEventRunner = new RunEachTypeOfEvents(serviceProvider, logger, _config);
 }
コード例 #3
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)
        {
            var eachEventRunner = new RunEachTypeOfEvents(_serviceProvider, _logger, _config);

            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(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(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>();

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

            var hasDuringEvents = !_config.NotUsingDuringSaveHandlers && eachEventRunner.SetupDuringEvents(context);

            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);
            }

            //Copy over the saveChanges result
            status.SetResult(callSaveChangesStatus.Result);

            await eachEventRunner.RunAfterSaveChangesEventsAsync(context, true);

            return(status);
        }