Base solution for your next web application
Open Closed

Background Worker and _notificationPublisher.Publish() not working! #9555


User avatar
0
Leonardo.Willrich created

I am converting an application AspNetZero MVC/JQuery .Core 3.1 from version 5.5 to version 9.0.1 and I am getting some issues with Background Worker routines.

In my background worker, I have some methods to check data and sent user notifications using INotificationPublisher. When there is some notification to be sent, it is raising an exception after the whole methods are done and the methods DoWork is completed successfully. Note, all notifications are persisted in the database correctly, but, there is no signalR to the browser to update the UI.

I have removed all background works and left on this one running in case of being a concurrency issue, but it didn't work, I am still getting this exception.

In version 5.5 all Background Workers are working well!

Here is the exception:

ERROR 2020-08-26 11:57:23,019 [65 ] ASL.iAPIS.Debugging.ExceptionHandlerEx - ExceptionHandlerEx Abp.BackgroundJobs.BackgroundJobException: A background job execution is failed. See inner exception for details. See BackgroundJob property to get information on the background job. ---> Abp.Domain.Uow.AbpDbConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions. ---> Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions. at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ThrowAggregateUpdateConcurrencyException(Int32 commandIndex, Int32 expectedRowsAffected, Int32 rowsAffected) at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ConsumeResultSetWithoutPropagation(Int32 commandIndex, RelationalDataReader reader) at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.Consume(RelationalDataReader reader) at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection) at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable1 commandBatches, IRelationalConnection connection) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IList1 entriesToSave) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(DbContext _, Boolean acceptAllChangesOnSuccess) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func3 operation, Func3 verifySucceeded) at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess) at Abp.EntityFrameworkCore.AbpDbContext.SaveChanges() --- End of inner exception stack trace --- at Abp.EntityFrameworkCore.AbpDbContext.SaveChanges() at Abp.Zero.EntityFrameworkCore.AbpZeroCommonDbContext3.SaveChanges() at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.SaveChangesInDbContext(DbContext dbContext) at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.SaveChanges() at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.CompleteUow() at Abp.Domain.Uow.UnitOfWorkBase.Complete() at Abp.Domain.Uow.UnitOfWorkInterceptor.InterceptSynchronous(IInvocation invocation) at Castle.DynamicProxy.AbstractInvocation.Proceed() at Castle.Proxies.IRepository2Proxy_1.Delete(BackgroundJobInfo entity) at Abp.BackgroundJobs.BackgroundJobStore.Delete(BackgroundJobInfo jobInfo) at Castle.Proxies.BackgroundJobStoreProxy.Delete_callback(BackgroundJobInfo jobInfo) at Castle.Proxies.Invocations.IBackgroundJobStore_Delete.InvokeMethodOnTarget() at Castle.DynamicProxy.AbstractInvocation.Proceed() at Castle.DynamicProxy.AbstractInvocation.Proceed() at Castle.Proxies.BackgroundJobStoreProxy.Delete(BackgroundJobInfo jobInfo) at Abp.BackgroundJobs.BackgroundJobManager.TryProcessJob(BackgroundJobInfo jobInfo) --- End of inner exception stack trace ---

Here is the method to notify users about expired visits:

private void ProcessScheduledVisitExpired(DateTime startTime, int tenantId, List<LocalizableMessageNotificationData> emailBody)
        {

            Logger.Debug("Notification Rules - ProcessScheduledVisitExpired - Start");

            var active = SettingManager.GetSettingValueForTenant<bool>(AppSettings.iApisNotificationRules.NotCompletedVisitExpiredActive, tenantId);
            if (!active)
            {
                Logger.Debug("Notification Rules - ProcessScheduledVisitExpired - Aborted - Rule is not Actived");
                return;
            }

            var today = startTime.Date;
            var visitExpiredDays = SettingManager.GetSettingValueForTenant<int>(AppSettings.iApisNotificationRules.NotCompletedVisitExpiredDays, tenantId);
            var expiredDate = today.AddDays(-visitExpiredDays).Date;

            Logger.Debug("Notification Rules - ProcessScheduledVisitExpired- Scheduled Not Completed Visit Expired Days: " + visitExpiredDays.ToString());

            var expiredVisits = _visitReportRepository.GetAllIncluding(v => v.Apiary, v => v.Visit)
                .Where(v => v.IsComplete == false && v.Visit.ScheduledDate.HasValue == true && v.Visit.ScheduledDate.Value.Date <= expiredDate)
                .ToList();

            Logger.Debug("Notification Rules - ProcessScheduledVisitExpired - expiredVisits lenght: " + expiredVisits.Count());

            foreach (var visit in expiredVisits)
            {
                Logger.Debug("Notification Rules - ProcessScheduledVisitExpired- processing visit: " + visit.VisitId);
                
                // Send the notification for users
                var notificationData = new LocalizableMessageNotificationData(
                new LocalizableString(
                    "ScheduledVisitExpired",
                    iAPISConsts.LocalizationSourceName
                    )
                );

                var days = visit.Visit.ScheduledDate.Value.Date.Subtract(today).Days + 1;
                notificationData["notificationType"] = "Visit Scheduled Expired";
                notificationData["visitId"] = visit.VisitId;
                notificationData["apiaryName"] = visit.Apiary.ApiaryName;
                notificationData["days"] = Math.Abs(days);

                Logger.Debug("Notification Rules - ProcessScheduledVisitExpired - before Notification visit: " + visit.VisitId);

                emailBody.Add(notificationData);
                
                **// If this command is removed, the background Worker doesn't raise the exception.**
                _notificationPublisher.Publish(
                    AppNotificationNames.NotificationRules,
                    notificationData,
                    severity: NotificationSeverity.Info);

                Logger.Debug("Notification Rules - ProcessScheduledVisitExpired - after Notification visit: " + visit.VisitId);
            }
            Logger.Debug("Notification Rules - ProcessScheduledVisitExpired - End");
        }

3 Answer(s)
  • User Avatar
    0
    Leonardo.Willrich created

    I have tried to use a UnitOfWork transaction context and have changed the Sync method to Async, but, I am still getting the same exception.

    Using UnitOfWorkManager to start a transaction.

    Logger.Debug("Notification Rules - Processing Tenant: " + tenantProcessTime.Key.ToString() + " - startTime: " + startTime.ToString("HH:mm"));
    
                                using (var uow = UnitOfWorkManager.Begin())
                                {
                                    using (CurrentUnitOfWork.SetTenantId(tenantId))
                                    using (_session.Use(tenantId, null))
                                    using (CurrentUnitOfWork.EnableFilter(AbpDataFilters.MustHaveTenant))
                                    using (CurrentUnitOfWork.SetFilterParameter(AbpDataFilters.MustHaveTenant, AbpDataFilters.Parameters.TenantId, tenantId))
                                    {
                                        _notificationRulesManager.ProcessTenant(startTime, tenantId);
                                        //CurrentUnitOfWork.SaveChanges();
                                        uow.Complete();
                                    }
                                }
    

    Running the PulishAsync() instead of Publish():

    AsyncHelper.RunSync(() => _notificationPublisher.PublishAsync(
                        AppNotificationNames.NotificationRules,
                        notificationData,
                        severity: NotificationSeverity.Info));
    
  • User Avatar
    0
    Leonardo.Willrich created

    I have cleaned the tables below and it seems to be working:

    AbpBackgroundJobs AbpNotifications AbpUserNotifications AbpTenantNotifications

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @Leonardo.Willrich,

    Sorry for the late reply and thank you for the feedback.