'unitOfWork parameter cannot be null' in B

2020-05-09 08:10发布

问题:

I've started getting these errors. It was working perfectly on my previous server.

using System;
using Abp.Dependency;
using Abp.Domain.Repositories;
using Abp.Threading.BackgroundWorkers;
using EMS.IPs;
using System.Threading.Tasks.Dataflow;
using System.Threading.Tasks;
using System.Linq;
using EMS.Contacts;
using System.Collections.Concurrent;
using Abp.Domain.Uow;
using System.Collections.Generic;
using EMS.EmailValidation;
using Microsoft.AspNetCore.SignalR;
using KellermanSoftware.NetEmailValidation;
using System.Net;
using System.Collections;
using System.Threading;
using System.ComponentModel;
using System.Transactions;

namespace EMS.BackgroundWorkers
{
    public class ContactValidationBackgroundWorker : BackgroundWorkerBase, ITransientDependency
    {
        private readonly IRepository<IP> _ipsRepository;
        private readonly IRepository<Contact> _contactsRepository;
        private readonly IUnitOfWorkManager _unitOfWorkManager;
        private readonly IUnitOfWorkManager _unitOfWorkManager2;
        private readonly IRepository<Contact> _contactRepository;

        private BackgroundWorker bgworker = new BackgroundWorker();

        ActionBlock<ValidationObject> workerBlock;

        public ContactValidationBackgroundWorker(
            IRepository<IP> ipsRepository,
            IRepository<Contact> contactsRepository,
            IUnitOfWorkManager unitOfWorkManager,
            IUnitOfWorkManager unitOfWorkManager2,
            IRepository<Contact> contactRepository)
        {
            _ipsRepository = ipsRepository;
            _contactsRepository = contactsRepository;
            _unitOfWorkManager = unitOfWorkManager;
            _unitOfWorkManager2 = unitOfWorkManager2;
            _contactRepository = contactRepository;

            bgworker.DoWork += Worker;
        }

        public override void Start()
        {
            base.Start();
            bgworker.RunWorkerAsync();
        }

        public override void Stop()
        {
            bgworker.DoWork -= Worker;
        }

        public void Worker(object sender, DoWorkEventArgs e)
        {
            using (var unitOfWork = _unitOfWorkManager.Begin(TransactionScopeOption.RequiresNew))
            {
                var contacts = _contactsRepository.GetAll().Where(x => !x.IsChecked);
                if (!contacts.Any())
                {
                    Logger.Debug("No contacts");
                    return;
                }

                workerBlock = new ActionBlock<ValidationObject>(
                  async (arg) => await Validate(arg),
                  new ExecutionDataflowBlockOptions
                  {
                      MaxDegreeOfParallelism = 5
                  });

                foreach (var contact in contacts)
                {
                    workerBlock.Post(new ValidationObject()
                    {
                        Contact = contact
                    });
                }

                unitOfWork.Complete();
            }

            Logger.Debug("End posting jobs to threads. Awaits results...");

            workerBlock.Complete();
            workerBlock.Completion.Wait();
        }

        public override void WaitToStop()
        {
            base.WaitToStop();
        }

        private async Task Validate(ValidationObject validationObject)
        {
            try
            {
                using (var contactUnitOfWork = _unitOfWorkManager2.Begin(TransactionScopeOption.RequiresNew))
                {
                    Contact contact = validationObject.Contact;
                    contact.IsChecked = true;

                    await _contactRepository.UpdateAsync(contact);
                    await contactUnitOfWork.CompleteAsync();
                }
            } catch (Exception ex) {
                Logger.Error(ex.ToString());
                throw;
            }
        }
    }

    public class ValidationResult
    {
        public ValidationResult()
        {
            IsValid = false;
            Message = "";
        }

        public string Message { get; set; }
        public bool IsValid { get; set; }
    }

    public class ValidationObject
    {
        public Contact Contact { get; set; }
    }
}

It works inside background worker; it worked before. Now it doesn't. Contact object is not null. It seems to ask me to add unitOfWork parameter to Update method. Please help me figure this out.

ERROR 2017-12-26 12:02:34,768 [14   ] orkers.ContactValidationBackgroundWorker - System.ArgumentNullException: Value cannot be null.
Parameter name: unitOfWork
   at Abp.EntityFrameworkCore.Uow.UnitOfWorkExtensions.GetDbContext[TDbContext](IActiveUnitOfWork unitOfWork, Nullable`1 multiTenancySide)
   at Abp.EntityFrameworkCore.Repositories.EfCoreRepositoryBase`3.Update(TEntity entity)
   at Castle.Proxies.Invocations.IRepository`2_Update_8.InvokeMethodOnTarget()
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Abp.Domain.Uow.UnitOfWorkInterceptor.PerformSyncUow(IInvocation invocation, UnitOfWorkOptions options)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.Proxies.IRepository`1Proxy_3.Update(Contact entity)
   at EMS.BackgroundWorkers.ContactValidationBackgroundWorker.<Validate>d__21.MoveNext() in /Users/grinay/Projects/EMSBackend/src/EMS.Application/BackgroundWorkers/ContactValidationBackgroundWorker.cs:line 329

UPDATE1

  ERROR 2017-12-27 05:31:34,500 [9    ] orkers.ContactValidationBackgroundWorker - System.ArgumentNullException: Value cannot be null.
Parameter name: unitOfWork
   at Abp.EntityFrameworkCore.Uow.UnitOfWorkExtensions.GetDbContext[TDbContext](IActiveUnitOfWork unitOfWork, Nullable`1 multiTenancySide)
   at Abp.EntityFrameworkCore.Repositories.EfCoreRepositoryBase`3.UpdateAsync(TEntity entity)
   at Castle.Proxies.Invocations.IRepository`2_UpdateAsync_8.InvokeMethodOnTarget()
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Abp.Domain.Uow.UnitOfWorkInterceptor.PerformAsyncUow(IInvocation invocation, UnitOfWorkOptions options)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.Proxies.IRepository`1Proxy_3.UpdateAsync(Contact entity)
   at EMS.BackgroundWorkers.ContactValidationBackgroundWorker.<>c__DisplayClass23_0.<<Validate>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Nito.AsyncEx.Synchronous.TaskExtensions.WaitAndUnwrapException(Task task)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Nito.AsyncEx.Synchronous.TaskExtensions.WaitAndUnwrapException(Task task)
   at Nito.AsyncEx.AsyncContext.Run(Func`1 action)
   at EMS.BackgroundWorkers.ContactValidationBackgroundWorker.Validate(ValidationObject validationObject)

回答1:

You cannot use async methods with unitOfWork like that in background worker.

Make the changes in these 5 lines:

public void Worker(object sender, DoWorkEventArgs e)
{
    // ...

    workerBlock = new ActionBlock<ValidationObject>(
      (arg) => Validate(arg), // This line #1
      new ExecutionDataflowBlockOptions
      {
          MaxDegreeOfParallelism = 5
      });

    // ...
}

private void Validate(ValidationObject validationObject) // This line #2
{
    try
    {
        using (var contactUnitOfWork = _unitOfWorkManager2.Begin(TransactionScopeOption.RequiresNew))
        {
            Contact contact = validationObject.Contact;
            contact.IsChecked = true;

            AsyncHelper.RunSync(async () => // This line #3
            {                               // This line #4
                await _contactRepository.UpdateAsync(contact);
                await contactUnitOfWork.CompleteAsync();
            });                             // This line #5
        }
    } catch (Exception ex) {
        Logger.Error(ex.ToString());
        throw;
    }
}