简体   繁体   中英

Run a controller as background worker

I am trying to convert a controller to async operation or run it as a background worker.

Solution 1: I tried below method but it give me an error saying DBContext has been disposed.

        public IHttpActionResult ChargebackAllOpenAR(int clientId)
        {          
            HostingEnvironment.QueueBackgroundWorkItem(clt => _clientService.ChargebackAllOpenAR(clientId));
            //_clientService.ChargebackAllOpenAR(clientId);

            return Ok();
        }

Solution 2: But it runs as a normal synchronous process

[HttpPost]
        public async Task<IHttpActionResult> ChargebackAllOpenAR(int clientId)
        {
            await Task.Run(() => _clientService.ChargebackAllOpenAR(clientId));
            //_clientService.ChargebackAllOpenAR(clientId);

            return Ok();
        }

Solution 3: I tried this but give same error DBContext has been disposed.

https://github.com/StephenCleary/AspNetBackgroundTasks

Main Code:

public class ClientService : IClientService
    {
        private IRepository<Client> _clientRepository;
        private IRepository<Invoice> _invoiceRepository;
        private IRepository<Advance> _advanceRepository;
        private IRepository<FinancialAccount> _financialAccountRepository;
        private IClientTableRepository _clientTableRepository;
        private IRepository<Transaction> _transactionRepository;
        private IRepository<AppCredit> _appCreditRepository;
        private IInvoiceService _invoiceService;
        private IUserProfileRepository _userProfileRepository;
        private IRepository<Domain.Payment> _paymentRepository;
        private ARUnitOfWork _unitOfWork;

        public ClientService(IRepository<Client> clientRepository,
            IRepository<Invoice> invoiceRepository,
            IRepository<Advance> advanceRepository,
            IRepository<FinancialAccount> financialAccountRepository,
            IClientTableRepository clientTableRepository, 
            IRepository<Transaction> transactionRepository,
            IRepository<AppCredit> appCreditRepository,
            IInvoiceService invoiceService,
            IUserProfileRepository userProfileRepository,
            IRepository<Domain.Payment> paymentRepository,
            ARUnitOfWork unitOfWork)
        {
            _clientRepository = clientRepository;
            _invoiceRepository = invoiceRepository;
            _advanceRepository = advanceRepository;
            _financialAccountRepository = financialAccountRepository;
            _clientTableRepository = clientTableRepository;
            _transactionRepository = transactionRepository;
            _appCreditRepository = appCreditRepository;
            _invoiceService = invoiceService;
            _userProfileRepository = userProfileRepository;
            _paymentRepository = paymentRepository;
            _unitOfWork = unitOfWork;
        }
 public void ChargebackAllOpenAR(int clientId)
        {
            var client = _clientRepository.Find(c => c.Id == clientId, i => i.FinancialAccounts).First();
            var transaction = new Transaction(clientId, TransactionType.Buyout);

            ChargebackInvoices(client, transaction);
            ChargebackOpenCredits(client, transaction);
            ChargebackAdvances(client, transaction);

            transaction.LineItems = transaction.LineItems.Where(li => li != null).ToList();
            _transactionRepository.Insert(transaction);
            _unitOfWork.SaveChanges();
        }

        private void ChargebackInvoices(Client client, Transaction transaction)
        {
            var openARAccount = client.FinancialAccounts.First(fa => fa.AccountType == ClientAccountType.OpenAR);
            var escrowReservesAccount = client.FinancialAccounts.First(fa => fa.AccountType == ClientAccountType.EscrowReserve);
            var cashReservesAccount = client.FinancialAccounts.First(fa => fa.AccountType == ClientAccountType.CashReserve);
            var factoringFeeRevenueAccount = _financialAccountRepository.GetSingle(fa => fa.Division_Id == openARAccount.Division_Id && fa.AccountType == (int)SystemAccountType.FactoringFeeRevenue);

            IEnumerable<Invoice> invoices = _invoiceRepository.Find(i => i.Client_Id == client.Id
               && i.Asset_Type == AssetType.Invoice
               && i.IsDeleted == false
               && i.Status == InvoiceStatus.Purchased,
               i => i.InvoicePurchase.Terms.CollectionsFees,
               i => i.TransactionLineItems)
               .Where(i => i.AmountOutstanding.Value != 0)
               .ToList();

            foreach (Invoice invoice in invoices)
            {
                invoice.StatusReason = InvoiceStatusReason.Buyout;
                invoice.DateClosed = DateUtil.GetSystemNow();

                //Credit Open A/R for amount outstanding
                transaction.LineItems.Add(openARAccount.CreateTransaction(invoice.AmountOutstanding, AccountingTransactionType.CREDIT, TransactionLineItemType.Buyout, invoice));

                //Credit Fee Revenue for collections fees
                CollectionsFeeCharge collectionsFee = _invoiceService.CalculateCollectionsFeeForInvoiceBuyout(invoice);
                transaction.LineItems.Add(factoringFeeRevenueAccount.CreateTransaction(collectionsFee.Amount, AccountingTransactionType.CREDIT, TransactionLineItemType.Buyout, invoice));

                //Debit Escrow Reserves for remaining invoice escrow
                IEnumerable<TransactionLineItem> transactionLineItems = invoice.TransactionLineItems.Where(tli => tli.FinancialAccount.AccountType == (int)ClientAccountType.EscrowReserve);
                var escrowAmount = transactionLineItems.Sum(tli => tli.AccountingTransactionType == AccountingTransactionType.CREDIT ? tli.Amount : -tli.Amount);
                transaction.LineItems.Add(escrowReservesAccount.CreateTransaction(escrowAmount, AccountingTransactionType.DEBIT, TransactionLineItemType.Buyout, invoice));

                //Debit Cash Reserves for (Open AR - invoice escrow + collections fees)
                transaction.LineItems.Add(cashReservesAccount.CreateTransaction(invoice.AmountOutstanding - escrowAmount + collectionsFee.Amount, AccountingTransactionType.DEBIT, TransactionLineItemType.Buyout, invoice));

                _invoiceRepository.Update(invoice);
            }
        }

        private void ChargebackOpenCredits(Client client, Transaction transaction)
        {
            var cashReservesAccount = client.FinancialAccounts.First(fa => fa.AccountType == ClientAccountType.CashReserve);
            var openCreditsAccount = client.FinancialAccounts.FirstOrDefault(fa => fa.AccountType == ClientAccountType.OpenCredits);

            int loggedInUserId = _userProfileRepository.GetLoggedInUserId();

            IEnumerable<AppCredit> appCredits = _appCreditRepository.Find(ac => (ac.Status == AppCreditStatus.Posted || ac.Status == AppCreditStatus.Ready) 
                && ac.Client_Id == client.Id
                && ac.Type == AppCreditType.OpenCredit);

            var ids = appCredits.Select(ac => ac.Payment_Id).Distinct().Where(id => id != null).Cast<int>();
            var payments = _paymentRepository.Find(p => ids.Contains(p.Id), p => p.AppCredits)
                .ToDictionary(p => p.Id);

            foreach (AppCredit appCredit in appCredits)
            {
                DateTime now = DateUtil.GetSystemNow();

                // mark open credit as removed
                appCredit.Status = AppCreditStatus.Removed;
                appCredit.RemovedBy_Id = loggedInUserId;
                appCredit.DateRemoved = now;

                // add posted reserve app credit to the payment with same amount
                AppCredit reserveAppCredit = new AppCredit()
                {
                    Type = AppCreditType.Reserve,
                    Status = AppCreditStatus.Posted,
                    Amount = appCredit.Amount,
                    Client_Id = appCredit.Client_Id,
                    Payment_Id = appCredit.Payment_Id,
                    PostedBy_Id = loggedInUserId,
                    DatePosted = now
                };

                Domain.Payment payment = payments[appCredit.Payment_Id];

                payment.AppCredits.Add(reserveAppCredit);

                if (payment.Status == Payment.Domain.PaymentStatus.Reopened
                    && payment.AmountRemaining.IsZero()
                    && !payment.AppCredits.Any(ac => ac.Status == AppCreditStatus.Ready))
                {
                    payment.Status = Payment.Domain.PaymentStatus.Posted;
                }

                // Debit Open Credits
                transaction.LineItems.Add(openCreditsAccount.CreateTransaction(appCredit.Amount, AccountingTransactionType.DEBIT, TransactionLineItemType.Buyout));
                // Credit Cash Reserves
                transaction.LineItems.Add(cashReservesAccount.CreateTransaction(appCredit.Amount, AccountingTransactionType.CREDIT, TransactionLineItemType.Buyout));

                payment?.Transactions.Add(transaction);
            }
        }

        private void ChargebackAdvances(Client client, Transaction transaction)
        {
            var cashReservesAccount = client.FinancialAccounts.First(fa => fa.AccountType == ClientAccountType.CashReserve);
            var fuelAdvancceARAccount = client.FinancialAccounts.FirstOrDefault(fa => fa.AccountType == ClientAccountType.FuelAdvanceAR);

            IEnumerable<Advance> advances = _advanceRepository.Find(a => a.Client_Id == client.Id
               && a.Asset_Type == AssetType.Advance
               && a.IsDeleted == false
               && a.Status == InvoiceStatus.Purchased)
               .Where(a => a.AmountOutstanding.Value != 0)
               .ToList();

            foreach (Advance advance in advances)
            {
                advance.StatusReason = InvoiceStatusReason.Buyout;
                advance.DateClosed = DateUtil.GetSystemNow();

                //Debit Cash Reserves
                transaction.LineItems.Add(cashReservesAccount.CreateTransaction(advance.AmountOutstanding, AccountingTransactionType.DEBIT, TransactionLineItemType.Buyout, advance));
                //Credit Fuel Advance A/R
                transaction.LineItems.Add(fuelAdvancceARAccount.CreateTransaction(advance.AmountOutstanding, AccountingTransactionType.CREDIT, TransactionLineItemType.Buyout, advance));
            }
        }

Finally working..

[Route("chargeback-all-open-ar-async")]
        [DeltaAuthorize(Roles.OPS_MANAGER + "," + RoleGroups.TREASURY)]
        [HttpPost]
        public IHttpActionResult ChargebackAllOpenARAsync(int clientId)
        {
            _clientService.ChargebackAllOpenAR(clientId);
            return Ok();
        }

        [Route("chargeback-all-open-ar")]
        [DeltaAuthorize(Roles.OPS_MANAGER + "," + RoleGroups.TREASURY)]
        [HttpPost]
        public IHttpActionResult ChargebackAllOpenAR(int clientId)
        {
            HostingEnvironment.QueueBackgroundWorkItem(clt => ChargebackAllOpenARTask(Request, clientId));
            return Ok();
        }

        private async Task ChargebackAllOpenARTask(HttpRequestMessage request, int clientId)
        {
            //Calls ChargebackAllOpenARAsync method
            using (HttpClient client = new HttpClient())
            {
                client.DefaultRequestHeaders.Authorization = request.Headers.Authorization;
                var url = new Uri(request.RequestUri.AbsoluteUri + "-async", UriKind.Absolute);
                await client.PostAsync(url, new StringContent(string.Empty));
            }
        }
[Route("chargeback-all-open-ar")]
        [DeltaAuthorize(Roles.OPS_MANAGER + "," + RoleGroups.TREASURY)]
        [FireAndForget]
        [HttpPost]
        public IHttpActionResult ChargebackAllOpenAR(int clientId, [FromBody]bool isFireAndForget)
        {
            _clientService.ChargebackAllOpenAR(clientId);
            return Ok();
        }
public class FireAndForgetAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            var request = actionContext.Request;

            bool isfireAndForget = (bool)actionContext.ActionArguments["isFireAndForget"];

            if (isfireAndForget)
            {
                QueueBackgroundWorker(request);
                actionContext.Response = new HttpResponseMessage(HttpStatusCode.OK);
                return;
            }           
        }

        private void QueueBackgroundWorker(HttpRequestMessage request)
        {
            HostingEnvironment.QueueBackgroundWorkItem(clt => GetTask(request));
        }

        private async Task GetTask(HttpRequestMessage request)
        {
            using (HttpClient client = new HttpClient())
            {
                client.DefaultRequestHeaders.Authorization = request.Headers.Authorization;               
                var url = new Uri(request.RequestUri.AbsoluteUri, UriKind.Absolute);
                var param = new Dictionary<string, string>
                {
                    { "isFireAndForget", "false" }
                };
                var req = new HttpRequestMessage(HttpMethod.Post, url) { Content = new FormUrlEncodedContent(param) };

                await client.SendAsync(req);
            }
        }


    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM