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);
}
}
}
Related
In my case after checking one XML file the method will go in to if or else condition where it will check another XML file
so How we can stream through multiple files? below is the code of class file.
public override void Execute(IParserContext context)
{
_context = context;
foreach (var file in context.Files)
{
try
{
IParserFile childFile;
String clusterName;
Cluster cluster = null;
List<string> devices = GetDeviceNames(file);
foreach (string device in devices)
{
childFile = file.Children.Where(x => x.FileName.StartsWith("GetSplitterStates", StringComparison.InvariantCultureIgnoreCase) && x.FullPath.Contains("RECOVERPOINT\\" + device)).FirstOrDefault();
var isRp4vm = CheckIfRP4VM(childFile);
if (!isRp4vm.HasValue)
{
file.Log.Message((int)ErrorCodes.FailClusterIdentification);
continue;
}
if (isRp4vm.HasValue && isRp4vm.Value == true)
{
clusterName = "RP4VM";
childFile = file.Children.Where(x => x.FileName.StartsWith("GetSystemSettings", StringComparison.InvariantCultureIgnoreCase) && x.FullPath.Contains("RECOVERPOINT\\" + device)).FirstOrDefault();
cluster = ParseSystemSettingsByDevice(childFile, device);
if (cluster == null)
{
file.Log.Message((int)ErrorCodes.FailClusterName);
continue;
}
childFile = file.Children.Where(x => x.FileName.StartsWith("GetMonitoredParameters", StringComparison.InvariantCultureIgnoreCase) && x.FullPath.Contains("RECOVERPOINT\\" + device)).FirstOrDefault();
ParseMonitoredParameters(childFile);
}
else
{
childFile = file.Children.Where(x => x.FileName.StartsWith("GetLocalCluster", StringComparison.InvariantCultureIgnoreCase)
&& x.FullPath.Contains("RECOVERPOINT\\" + device, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
clusterName = ParseLocalCluster(childFile);
if (clusterName == null)
{
file.Log.Message((int)ErrorCodes.FailClusterName);
continue; //Skip the device of we can't get the cluster name.
}
childFile = file.Children.Where(x => x.FileName.StartsWith("GetSystemSettings", StringComparison.InvariantCultureIgnoreCase) && x.FullPath.Contains("RECOVERPOINT\\" + device)).FirstOrDefault();
cluster = ParseSystemSettingsByClusterName(childFile, clusterName);
if (cluster == null)
{
file.Log.Message((int)ErrorCodes.FailClusterParsing);
continue; //Skip the device of we can't get the cluster info.
}
childFile = file.Children.Where(x => x.FileName.StartsWith("GetGroupVolumes", StringComparison.InvariantCultureIgnoreCase) && x.FullPath.Contains("RECOVERPOINT\\" + device)).FirstOrDefault();
ParseGroupVolumes(childFile, cluster);
childFile = file.Children.Where(x => x.FileName.StartsWith("GetSANVolumes", StringComparison.InvariantCultureIgnoreCase) && x.FullPath.Contains("RECOVERPOINT\\" + device)).FirstOrDefault();
ParseSANVolumes(childFile, cluster);
}
childFile = file.Children.Where(x => x.FileName.StartsWith("GetSystemReport", StringComparison.InvariantCultureIgnoreCase) && x.FullPath.Contains("RECOVERPOINT\\" + device)).FirstOrDefault();
ParseSystemReport(childFile, cluster);
}
}
catch (Exception ex)
{
file.Log.Message((int)ErrorCodes.ExecuteErrorMessage);
file.Log.Error(file.FileName, ex);
}
}
}
i am able to execute only one method
CheckIfRP4VM. after this method i want to go in to any of the condition to check other files.
how we can achieve that? below is my test class
[Test]
public void Processfile_GetSplitterStatesXML()
{
Mock<IParserContext> mockIParserContext = new Mock<IParserContext>();
Mock<IParserFile> mockIParserChildFile = new Mock<IParserFile>();
Mock<IDictionary<string, string>> mockIDictionary = new Mock<IDictionary<string, string>>();
Mock<IParserLog> mockIParserChildLog = new Mock<IParserLog>();
TestUtility utility = new TestUtility();
string serverFileName = "GetSplitterStates";
string serverFileExtension = ".xml";
string serverFileNameWithExtension = serverFileName + serverFileExtension;
string relativeFilePath = #"\SampleTestFiles\XML\RecoverPointParser\STORAGE\RECOVERPOINT\splitters\" + serverFileNameWithExtension;
string absoluteFilePath = utility.getExecutionAssemblyLocation() + relativeFilePath;
Stream stream = utility.getFileStream(relativeFilePath);
IList<IParserFile> parserFiles = new List<IParserFile>() { };
mockIParserChildFile.Setup(x => x.Guid).Returns(new Guid());
mockIParserChildFile.Setup(x => x.FullPath).Returns(absoluteFilePath);
mockIParserChildFile.Setup(x => x.FileName).Returns(serverFileNameWithExtension);
mockIParserChildFile.Setup(x => x.Extension).Returns(serverFileExtension);
mockIParserChildFile.Setup(x => x.SizeInBytes).Returns(2);
mockIParserChildFile.Setup(x => x.Name).Returns(serverFileName);
mockIParserChildFile.Setup(x => x.ModifiedDateTime).Returns(DateTime.Now);
mockIParserChildFile.Setup(x => x.Children).Returns(parserFiles);
mockIParserChildFile.Setup(x => x.Properties).Returns(mockIDictionary.Object);
mockIParserChildFile.Setup(x => x.Log).Returns(mockIParserChildLog.Object);
mockIParserChildFile.Setup(x => x.RequestFileId).Returns(1);
mockIParserChildFile.Setup(x => x.Open()).Returns(stream);
parserFiles.Add(mockIParserChildFile.Object);
mockIParserContext.Setup(x => x.Files).Returns(parserFiles);
RecoverPointParser RecoverPointParser = new RecoverPointParser();
RecoverPointParser.Execute(mockIParserContext.Object);
mockIParserChildLog.Verify(x => x.Error(It.IsAny<string>(), It.IsAny<Exception>()), Times.AtLeastOnce);
}
so far i am able to check with one file only. as it has if condition i am unable to do it.
I have following service method
public async Task<IResultList<IEnrichedContentEntity>> QueryAsync(Context context, string schemaIdOrName, Q q)
{
Guard.NotNull(context, nameof(context));
if (q == null)
{
return EmptyContents;
}
var schema = await GetSchemaOrThrowAsync(context, schemaIdOrName);
var permissionReadOwn = Permissions.ForApp(Permissions.AppContentReadOwn, context.App.Name, schemaIdOrName);
if (context.Permissions.Allows(permissionReadOwn))
{
q.CreatedBy = context.User.Token();
}
using (Profiler.TraceMethod<ContentQueryService>())
{
q = await queryParser.ParseAsync(context, q, schema);
var contents = await contentRepository.QueryAsync(context.App, schema, q, context.Scope());
if (q.Ids != null && q.Ids.Count > 0)
{
contents = contents.SortSet(x => x.Id, q.Ids);
}
return await TransformAsync(context, contents);
}
}
q.CreatedBy must set value if permission is correct.
How can I test if q.CreatedBy is not empty and has correct value.
I implemented following test, but no idea how check this params?
public async Task QueryAll_should_return_own_user_contents(int isFrontend, int unpublished, SearchScope scope)
{
var ctx = CreateContextWithOwnReadPermission(isFrontend: isFrontend == 1, allowSchema: true)
.WithUnpublished(unpublished == 1);
var content = CreateContent(contentId);
var q = Q.Empty.WithReference(DomainId.NewGuid());
A.CallTo(() => contentRepository.QueryAsync(ctx.App, schema, q, scope))
.Returns(ResultList.CreateFrom(5, content));
//A.CallTo(() => contentRepository.QueryAsync(ctx.App, schema, A<Q>.That.Matches(x => x.CreatedBy == null), scope))
// .MustHaveHappened();
//A.CallTo(() => contentRepository.QueryAsync(ctx.App, schema, A<Q>.That.Matches(x => x.CreatedBy == ctx.User.Token()), scope))
// .MustHaveHappened();
//q.CreatedBy = ctx.User.Token();
var result = await sut.QueryAsync(ctx, schemaId.Name, q);
Assert.Equal(contentData, result[0].Data);
Assert.Equal(contentId, result[0].Id);
Assert.Equal(5, result.Total);
}
Just trying to do normal query on ef but getting below error message
"A second operation started on this context before a previous asynchronous operation completed"
I am using await for all aync call but not sure what could be causing this issue.
here' code
var sids = await context.Sites.Where(s => slist.Contains(s.JVSiteID)).ToListAsync();
var header = await context.GenericCsvHeaders.FirstOrDefaultAsync(x => x.Header == csvh) ?? new GenericCsvHeader { Header = csvh };
context.Entry<GenericCsvHeader>(header).State = (header.GenericCsvHeaderID == 0) ? EntityState.Added : EntityState.Unchanged;
var sa = await context.SAs.Where(x => x.StatusID == Data.Enum.Status.Active && mapkeys.Contains(x.SAName)).Select(x => new { x.SAName, x.SACode, x.SAID }).ToListAsync();
if (sa.Count > 0)
sasitelist = await context.Site_SA.Where(x => x.StatusID == Data.Enum.Status.Active && siteids.Contains(x.SiteID ?? 0)).ToListAsync();
var az = await context.Azimuths.Where(x => x.StatusID == Data.Enum.Status.Active && mapkeys.Contains(x.AzimuthName)).Select(x => new { x.AzimuthName, x.AzimuthID }).ToListAsync();
if (az.Count > 0)
azsitelist = await context.Site_Azimuth.Where(x => x.StatusID == Data.Enum.Status.Active && siteids.Contains(x.SiteID ?? 0)).ToListAsync();
var rows = new List<dynamic>(); //getting this list from csv file via csvHelper
foreach (var r in rows)
{
var s = sids.FirstOrDefault(x => x.JVSiteID == (((IDictionary<String, Object>)r)[siteid]).ToString()) ?? new Site();
UpdateSite(s, r, map);
}
private async void UpdateSite(Site site, dynamic csvSite, IDictionary<string, string> map)
{
context.Entry(site).State = (site.StateID == 0) ? EntityState.Added : EntityState.Modified;
site.SiteForAuditTrail = site;
if (map.ContainsKey("SiteName") && !String.IsNullOrWhiteSpace(GetStringOfDynamic(map,csvSite,"SiteName")))
site.SiteName = GetStringOfDynamic(map,csvSite, "SiteName");
if (map.ContainsKey("State") && !String.IsNullOrWhiteSpace(GetStringOfDynamic(map,csvSite, "State")))
{
//getting exception at below line
var state = (await GetRefTypeList<State>(x => x.StateCode == GetStringOfDynamic(map,csvSite, "State"))) ?? new State { StateCode = GetStringOfDynamic(map,csvSite, "State") };
context.Entry(state).State = (state.StateID == 0) ? EntityState.Added : EntityState.Unchanged;
site.State = state;
state.SiteForAuditTrail = site;
}
}
private async Task<T> GetRefTypeList<T>(Func<T, bool> expression) where T : EntityBase
{
if (!refTypes.ContainsKey(typeof(T).Name))
refTypes[typeof(T).Name] = (await context.Set<T>().Where(x => x.StatusID == Data.Enum.Status.Active).ToListAsync());
return (refTypes[typeof(T).Name] as List<T>)?.FirstOrDefault(expression);
}
private string GetStringOfDynamic(IDictionary<string, string> map,dynamic obj, string propertyName)
{
if (((IDictionary<String, Object>)obj).ContainsKey(map[propertyName]))
return (((IDictionary<String, Object>)obj)[map[propertyName]]??"").ToString();
return "";
}
found problem, was missing await when calling UpdateSite
foreach (var r in rows)
{
var s = sids.FirstOrDefault(x => x.JVSiteID == (((IDictionary<String, Object>)r)[siteid]).ToString()) ?? new Site();
**await** UpdateSite(s, r, map);
}
You are having a race condition here. While you are using await you have other code executing potentially before the variable sids, header, sa and az, have been returned.
You should refactor your code so that the remaining code is not executed until these variables are returned. You can do this by using a task and then using the extension WhenAll which will ensure each await has been completed before proceeding. Here is a link to MS docs on Task and WhenAll implementation
I would like to implement an async function in converting the object to another object then in saving to the database.
public List<Order> GetOrdersFromTradeGeckoCount()
{
string orderLimit = base.StorePlugin.Store.OrderLimit.HasValue ? base.StorePlugin.Store.OrderLimit.Value.ToString() : "250";
string filters = string.Format("?status=finalized&limit={0}", orderLimit);
HttpResponseMessage response = _requestHelper.GetHttpResponse("orders" + filters);
var tgOrders = GetOrdersResponse(response);
//Async Convert and Save Order
ConvertToORouterOrdersAsync(tgOrders);
return ConvertToORouterOrdersCount(tgOrders);
}
I would like this method ConvertToORouterOrdersAsync(tgOrders); will run in the background and will return the Count of Orders from this ConvertToORouterOrdersCount(tgOrders) before the conversion is done.
Please help me to change the implementation to asynchronous.
public async void ConvertToORouterOrdersAsync(List<TGOrder> tgOrders)
{
var orderMgr = new OrderDAC();
var orders = new List<Order>(tgOrders.Count());
foreach (TGOrder tgOrder in tgOrders)
{
try
{
var order = new Order();
var orderId = TryConvertInt64(CleanUpOrderId(tgOrder.order_number));
if (orderId == null) continue;
var tempOrderId = string.Format("{0}{1}", base.StoreId, orderId.Value);
order.OrderId = TryConvertInt64(tempOrderId).Value;
order.StoreOrderId = tgOrder.id.ToString();
order.WarehouseOrderId = tgOrder.order_number;
var orderFromDb = orderMgr.GetOrder(order.OrderId, base.StoreId);
if (orderFromDb != null) continue; // make sure we only import new order(i.e. doesn't exists in database)
// shipping address
var tgShippingAddress = GetAddress(tgOrder.shipping_address_id);
if (tgShippingAddress == null) continue;
order.ShipFirstName = tgShippingAddress.first_name;
order.ShipLastName = tgShippingAddress.last_name;
order.ShipCompanyName = tgShippingAddress.company_name;
order.ShipAddress1 = tgShippingAddress.address1;
order.ShipAddress2 = tgShippingAddress.address2;
order.ShipCity = tgShippingAddress.suburb;
order.ShipState = tgShippingAddress.state;
order.ShipPostalCode = tgShippingAddress.zip_code;
order.ShipCountry = tgShippingAddress.country;
order.ShipPhoneNumber = tgShippingAddress.phone_number;
order.CustomerEmail = tgOrder.email;
// billing address
var tgBillingAddress = GetAddress(tgOrder.billing_address_id);
if (tgBillingAddress == null) continue;
// line items
var lineItems = GetOrderLineItems(tgOrder.id);
foreach (TGOrderLineItem lineItem in lineItems)
{
var ol = new OrderLine();
if (lineItem.variant_id.HasValue)
{
var variant = GetVariant(lineItem.variant_id.Value);
if (variant == null) continue;
ol.ProductName = variant.product_name;
ol.SKU = variant.sku;
ol.ThreePLSKU = ol.SKU;
ol.Qty = Convert.ToInt16(TryGetDecimal(lineItem.quantity));
ol.OrderId = order.OrderId;
ol.Price = TryGetDecimal(lineItem.price);
ol.SubTotal = (ol.Qty * ol.Price);
ol.StoreOrderLineId = Convert.ToString(lineItem.id);
order.OrderLines.Add(ol);
}
}
var validator = new Validator(base.Task);
if (validator.IsValidOrder(order))
{
orderMgr.Add(order);
}
}
catch (Exception ex)
{
AppendError(ex.Message);
}
}
}
To really have a advantage of the async and await the underlying calls should have async versions, for example the database calls should be async, don't know if you use EF or just plain SqlCommand but both have async versions of their calls.
Other calls that can be async is HTTP calls.
I have edited youre code with the assumption you are able to convert the underlying code to async.
public async Task<List<Order> GetOrdersFromTradeGeckoCount()
{
string orderLimit = base.StorePlugin.Store.OrderLimit.HasValue ? base.StorePlugin.Store.OrderLimit.Value.ToString() : "250";
string filters = string.Format("?status=finalized&limit={0}", orderLimit);
HttpResponseMessage response = await _requestHelper.GetHttpResponseAsync("orders" + filters).ConfigureAwait(false);
var tgOrders = GetOrdersResponse(response);
//Async Convert and Save Order
await ConvertToORouterOrdersAsync(tgOrders).ConfigureAwait(false);
return await ConvertToORouterOrdersCountAsync(tgOrders).ConfigureAwait(false);
}
public async Task ConvertToORouterOrdersAsync(List<TGOrder> tgOrders)
{
var orderMgr = new OrderDAC();
var orders = new List<Order>(tgOrders.Count());
foreach (TGOrder tgOrder in tgOrders)
{
try
{
var order = new Order();
var orderId = TryConvertInt64(CleanUpOrderId(tgOrder.order_number));
if (orderId == null) continue;
var tempOrderId = string.Format("{0}{1}", base.StoreId, orderId.Value);
order.OrderId = TryConvertInt64(tempOrderId).Value;
order.StoreOrderId = tgOrder.id.ToString();
order.WarehouseOrderId = tgOrder.order_number;
var orderFromDb = await orderMgr.GetOrderAsync(order.OrderId, base.StoreId).ConfigureAwait(false);
if (orderFromDb != null) continue; // make sure we only import new order(i.e. doesn't exists in database)
// shipping address
var tgShippingAddress = GetAddress(tgOrder.shipping_address_id);
if (tgShippingAddress == null) continue;
order.ShipFirstName = tgShippingAddress.first_name;
order.ShipLastName = tgShippingAddress.last_name;
order.ShipCompanyName = tgShippingAddress.company_name;
order.ShipAddress1 = tgShippingAddress.address1;
order.ShipAddress2 = tgShippingAddress.address2;
order.ShipCity = tgShippingAddress.suburb;
order.ShipState = tgShippingAddress.state;
order.ShipPostalCode = tgShippingAddress.zip_code;
order.ShipCountry = tgShippingAddress.country;
order.ShipPhoneNumber = tgShippingAddress.phone_number;
order.CustomerEmail = tgOrder.email;
// billing address
var tgBillingAddress = GetAddress(tgOrder.billing_address_id);
if (tgBillingAddress == null) continue;
// line items
var lineItems = GetOrderLineItems(tgOrder.id);
foreach (TGOrderLineItem lineItem in lineItems)
{
var ol = new OrderLine();
if (lineItem.variant_id.HasValue)
{
var variant = GetVariant(lineItem.variant_id.Value);
if (variant == null) continue;
ol.ProductName = variant.product_name;
ol.SKU = variant.sku;
ol.ThreePLSKU = ol.SKU;
ol.Qty = Convert.ToInt16(TryGetDecimal(lineItem.quantity));
ol.OrderId = order.OrderId;
ol.Price = TryGetDecimal(lineItem.price);
ol.SubTotal = (ol.Qty * ol.Price);
ol.StoreOrderLineId = Convert.ToString(lineItem.id);
order.OrderLines.Add(ol);
}
}
var validator = new Validator(base.Task);
if (validator.IsValidOrder(order))
{
await orderMgr.AddAsync(order).ConfigureAwait(false);
}
}
catch (Exception ex)
{
AppendError(ex.Message);
}
}
}
Or if you just want to run the code in the background you can also just wrap it in a Task.Run
public async Task<List<Order> GetOrdersFromTradeGeckoCount()
{
string orderLimit = base.StorePlugin.Store.OrderLimit.HasValue ? base.StorePlugin.Store.OrderLimit.Value.ToString() : "250";
string filters = string.Format("?status=finalized&limit={0}", orderLimit);
HttpResponseMessage response = _requestHelper.GetHttpResponse("orders" + filters);
var tgOrders = GetOrdersResponse(response);
//Async Convert and Save Order
await ConvertToORouterOrdersAsync(tgOrders).ConfigureAwait(false);
return ConvertToORouterOrdersCount(tgOrders);
}
public Task ConvertToORouterOrdersAsync(List<TGOrder> tgOrders)
{
return Task.Run(() =>
{
var orderMgr = new OrderDAC();
var orders = new List<Order>(tgOrders.Count());
foreach (TGOrder tgOrder in tgOrders)
{
try
{
var order = new Order();
var orderId = TryConvertInt64(CleanUpOrderId(tgOrder.order_number));
if (orderId == null) continue;
var tempOrderId = string.Format("{0}{1}", base.StoreId, orderId.Value);
order.OrderId = TryConvertInt64(tempOrderId).Value;
order.StoreOrderId = tgOrder.id.ToString();
order.WarehouseOrderId = tgOrder.order_number;
var orderFromDb = await orderMgr.GetOrder(order.OrderId, base.StoreId);
if (orderFromDb != null) continue; // make sure we only import new order(i.e. doesn't exists in database)
// shipping address
var tgShippingAddress = GetAddress(tgOrder.shipping_address_id);
if (tgShippingAddress == null) continue;
order.ShipFirstName = tgShippingAddress.first_name;
order.ShipLastName = tgShippingAddress.last_name;
order.ShipCompanyName = tgShippingAddress.company_name;
order.ShipAddress1 = tgShippingAddress.address1;
order.ShipAddress2 = tgShippingAddress.address2;
order.ShipCity = tgShippingAddress.suburb;
order.ShipState = tgShippingAddress.state;
order.ShipPostalCode = tgShippingAddress.zip_code;
order.ShipCountry = tgShippingAddress.country;
order.ShipPhoneNumber = tgShippingAddress.phone_number;
order.CustomerEmail = tgOrder.email;
// billing address
var tgBillingAddress = GetAddress(tgOrder.billing_address_id);
if (tgBillingAddress == null) continue;
// line items
var lineItems = GetOrderLineItems(tgOrder.id);
foreach (TGOrderLineItem lineItem in lineItems)
{
var ol = new OrderLine();
if (lineItem.variant_id.HasValue)
{
var variant = GetVariant(lineItem.variant_id.Value);
if (variant == null) continue;
ol.ProductName = variant.product_name;
ol.SKU = variant.sku;
ol.ThreePLSKU = ol.SKU;
ol.Qty = Convert.ToInt16(TryGetDecimal(lineItem.quantity));
ol.OrderId = order.OrderId;
ol.Price = TryGetDecimal(lineItem.price);
ol.SubTotal = (ol.Qty * ol.Price);
ol.StoreOrderLineId = Convert.ToString(lineItem.id);
order.OrderLines.Add(ol);
}
}
var validator = new Validator(base.Task);
if (validator.IsValidOrder(order))
{
orderMgr.Add(order);
}
}
catch (Exception ex)
{
AppendError(ex.Message);
}
}
});
}
i've translated the quickblox's sample app for Xamarin forms in Xamarin iOS and Android native.
Everything works "except" that the retrieving of the message fails.
I send the message from one client, and the event is catched from the other chat occupant:
XMPP: DispatchEvents ====> <message id="580735ed335fb760ae0017ec" xmlns="jabber:client" from="18029700-46533#chat.quickblox.com/1220770403-quickblox-68179" type="chat" to="18976912-46533#chat.quickblox.com"><extraParams xmlns="jabber:client"><save_to_history>1</save_to_history><dialog_id>5800aea1a28f9a1c1f000010</dialog_id><message_id>580735ed335fb760ae0017ec</message_id><date_sent>1476867565</date_sent></extraParams><body>test+message</body><thread>5800aea1a28f9a1c1f000010</thread></message>
XMPP: OnMessageReceived ====> From: 18029700 To: 18976912 Body: DateSent 1476867565 FullXmlMessage: <message id="580735ed335fb760ae0017ec" xmlns="jabber:client" from="18029700-46533#chat.quickblox.com/1220770403-quickblox-68179" type="chat" to="18976912-46533#chat.quickblox.com"><extraParams xmlns="jabber:client"><save_to_history>1</save_to_history><dialog_id>5800aea1a28f9a1c1f000010</dialog_id><message_id>580735ed335fb760ae0017ec</message_id><date_sent>1476867565</date_sent></extraParams><body>test+message</body><thread>5800aea1a28f9a1c1f000010</thread></message>
as you can see the Body part into the event OnMessageReceived is empty!
But the html part in the end of this snippet contains the message "test+message"
this is the PrivateChat Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using QbChat.Pcl.Repository;
using Quickblox.Sdk.GeneralDataModel.Models;
using Quickblox.Sdk.Modules.ChatXmppModule;
using Quickblox.Sdk.Modules.UsersModule.Models;
using UIKit;
using Xmpp.Im;
namespace KeepInTouch.iOS
{
public partial class PrivateChat : BaseChat
{
PrivateChatManager privateChatManager;
public PrivateChat(string dialogId, string nibname) : base(dialogId, nibname) { }
public async override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
AppDelegate.R.NavController.SetNavigationBarHidden(true, true);
await MessageProvider.Reconnect();
}
public override void ViewDidLoad()
{
base_tb_chat = tb_chat;
base_txt_chat = txt_chat;
base.ViewDidLoad();
view_top.BackgroundColor = UIColor.FromPatternImage(UIImage.FromFile("navbar_top_kit.png"));
tb_chat.TableFooterView = new UIView();
IsBusyIndicatorVisible = true;
var dialog = Database.Instance().GetDialog(dialogId);
var opponentId = dialog.OccupantIds.Split(',').Select(int.Parse).First(id => id != QbChat.UserId);
ChatXmppClient xmpc = QbChat.QbProvider.GetXmppClient();
privateChatManager = xmpc.GetPrivateChatManager(opponentId, dialogId);
privateChatManager.MessageReceived += OnMessageReceived;
DialogName = dialog.Name;
//xmpc.MessageReceived += OnMessageReceived;
btn_chat.TouchUpInside += async delegate {
await SendMessageCommandExecute();
txt_chat.Text = "";
};
txt_chat.ShouldReturn += (textField) => {
textField.ResignFirstResponder();
return true;
};
txt_chat.EditingChanged += async delegate {
MessageText = txt_chat.Text;
await MessageProvider.Reconnect();
};
btn_back.TouchUpInside += delegate {
DismissViewController(false, null);
};
IsBusyIndicatorVisible = true;
Task.Factory.StartNew(async () => {
var users = await QbChat.QbProvider.GetUsersByIdsAsync(dialog.OccupantIds);
var opponentUser = users.FirstOrDefault(u => u.Id != QbChat.UserId);
if (opponentUser != null && opponentUser.BlobId.HasValue) {
await QbChat.QbProvider.GetImageAsync(opponentUser.BlobId.Value).ContinueWith((task, result) => {
//var bytes =
task.ConfigureAwait(true).GetAwaiter().GetResult();
}, TaskScheduler.FromCurrentSynchronizationContext());
}
opponentUsers = new List<User> { opponentUser };
await LoadMessages();
InvokeOnMainThread(() =>
IsBusyIndicatorVisible = false
);
});
}
async void OnMessageReceived(object sender, MessageEventArgs messageEventArgs)
{
if (messageEventArgs.MessageType == MessageType.Chat ||
messageEventArgs.MessageType == MessageType.Groupchat) {
string decodedMessage = System.Net.WebUtility.UrlDecode(messageEventArgs.Message.MessageText);
var messageTable = new MessageTable();
messageTable.SenderId = messageEventArgs.Message.SenderId;
messageTable.DialogId = messageEventArgs.Message.ChatDialogId;
messageTable.DateSent = messageEventArgs.Message.DateSent;
if (messageEventArgs.Message.NotificationType != 0) {
if (messageEventArgs.Message.NotificationType == NotificationTypes.GroupUpdate) {
if (messageEventArgs.Message.AddedOccupantsIds.Any()) {
var userIds = new List<int>(messageEventArgs.Message.AddedOccupantsIds);
userIds.Add(messageEventArgs.Message.SenderId);
var users = await QbChat.QbProvider.GetUsersByIdsAsync(string.Join(",", userIds));
var addedUsers = users.Where(u => u.Id != messageEventArgs.Message.SenderId);
var senderUser = users.First(u => u.Id == messageEventArgs.Message.SenderId);
messageTable.Text = senderUser.FullName + " added users: " + string.Join(",", addedUsers.Select(u => u.FullName));
} else if (messageEventArgs.Message.DeletedOccupantsIds.Any()) {
var userIds = new List<int>(messageEventArgs.Message.DeletedOccupantsIds);
var users = await QbChat.QbProvider.GetUsersByIdsAsync(string.Join(",", userIds));
messageTable.Text = string.Join(",", users.Select(u => u.FullName)) + " left this room";
}
//var dialogInfo = await QbChat.QbProvider.GetDialogAsync(messageEventArgs.Message.ChatDialogId);
//if (dialogInfo == null)
//{
// return;
//}
//var dialog = new DialogTable(dialogInfo);
//Database.Instance().SaveDialog(dialog);
}
} else {
messageTable.Text = decodedMessage;
}
await SetRecepientName(messageTable);
Messages.Add(messageTable);
InvokeOnMainThread(async () => {
tb_chat.ReloadData();
await ScrollList();
});
}
}
public async Task LoadMessages()
{
List<Message> messages;
try {
messages = await QbChat.QbProvider.GetMessagesAsync(dialogId);
} catch (Exception ex) {
Console.WriteLine(ex);
return;
}
if (messages != null) {
messages = messages.OrderBy(message => message.DateSent).ToList();
foreach (var message in messages) {
var chatMessage = new MessageTable();
chatMessage.DateSent = message.DateSent;
chatMessage.SenderId = message.SenderId;
chatMessage.MessageId = message.Id;
if (message.RecipientId.HasValue)
chatMessage.RecepientId = message.RecipientId.Value;
chatMessage.DialogId = message.ChatDialogId;
chatMessage.IsRead = message.Read == 1;
await SetRecepientName(chatMessage);
chatMessage.Text = System.Net.WebUtility.UrlDecode(message.MessageText);
InvokeOnMainThread(() =>
Messages.Add(chatMessage)
);
}
InvokeOnMainThread(async () => {
tb_chat.ReloadData();
await ScrollList();
});
}
}
async Task SendMessageCommandExecute()
{
var message = MessageText != null ? MessageText.Trim() : string.Empty;
if (!string.IsNullOrEmpty(message)) {
var m = new MessageTable();
m.SenderId = QbChat.UserId;
m.Text = message;
m.DialogId = dialogId;
m.RecepientFullName = "Me";
try {
await MessageProvider.Reconnect();
var encodedMessage = System.Net.WebUtility.UrlEncode(message);
privateChatManager.SendMessage(encodedMessage);
} catch (Exception ex) {
Console.WriteLine(ex);
return;
}
long unixTimestamp = DateTime.UtcNow.Ticks - new DateTime(1970, 1, 1).Ticks;
unixTimestamp /= TimeSpan.TicksPerSecond;
m.DateSent = unixTimestamp;
m.ID = Database.Instance().SaveMessage(m);
var dialog = Database.Instance().GetDialog(dialogId);
dialog.LastMessage = m.Text;
dialog.LastMessageSent = DateTime.UtcNow;
Database.Instance().SaveDialog(dialog, true);
Messages.Add(m);
MessageText = "";
InvokeOnMainThread(async () => {
tb_chat.ReloadData();
await ScrollList();
});
}
}
public override void DidReceiveMemoryWarning()
{
base.DidReceiveMemoryWarning();
}
}
}
long story short the sdk receive the message but apparently fail to parse the body message. fire the handler but if empty don't call the privateChatManager.MessageReceived.
could anyone help please?
Thank you