I have an Asp.net core 2.1 application with a Postgresql database.
A large file (~100K lines) gets uploaded to the database and then each line is processed.
As each line is processed there are approximately 15 sub queries per line taking up about 10 seconds in total per line. I have tried optimizing the queries and loading certain datasets in memory to reduce database calls which has seen a slight improvement. I have also tried reducing the batch size but that has also had minimal reduction.
Are there any other ways I can speed this up? The queries are written in lambda.
The high level function:
public void CDR_Insert(List<InputTable> inputTable, IQueryable<Costing__Table> CostingTable, List<Patterns> PatternsTable, IQueryable<Country_Calling_Codes> CallingCodesTable, IQueryable<Country> CountryTable, string voicemail_number)
{
foreach (var row in inputTable)
{
//if (!string.IsNullOrEmpty(row.finalCalledPartyPattern))
//{
var rowexists = _context.CDR.Where(m => m.InputId == row.id).Count();
if (rowexists == 0)
{
if (ValidateIfOneNumberIsForCountry(row.callingPartyNumber, row.finalCalledPartyNumber, PatternsTable, CountryTable) == true)
{
var CallRingTime = TimeSpan.FromSeconds(Ring_Time(row.dateTimeOrigination, row.dateTimeConnect));
var Call_Direction = CallDirection(row.callingPartyNumber, row.finalCalledPartyNumber, row.finalCalledPartyPattern, voicemail_number);
var CallReason = "Destination Out Of Order";
if (row.dateTimeConnect != "0")
{
CallReason = Call_Reason(row.callingPartyNumber, row.originalCalledPartyNumber, row.finalCalledPartyNumber, voicemail_number);
}
var CallOutcome = Outcome(row.duration, row.dateTimeOrigination, row.dateTimeConnect, row.callingPartyNumber, row.originalCalledPartyNumber, row.finalCalledPartyNumber, voicemail_number);
var CallCategory = GetCategory(row.callingPartyNumber, row.finalCalledPartyNumber, PatternsTable, CallingCodesTable, CountryTable);
var CallCost = 0.00;
if (CallOutcome != "Not Connected" || CallOutcome != "No answer")
{
CallCost = Costing(row.callingPartyNumber, row.finalCalledPartyNumber, row.dateTimeConnect, row.dateTimeDisconnect, row.finalCalledPartyPattern, row.duration, Call_Direction, CostingTable, PatternsTable, voicemail_number, CallingCodesTable, CountryTable);
}
CDR cDR = new CDR()
{
Date_Time = UnixTimeStampToDateTimeConvertor2(row.dateTimeOrigination),
Call_Direction = Call_Direction,
Calling_Party = row.callingPartyNumber_uri,
Calling_Digits = row.callingPartyNumber,
Called_Party = row.finalCalledPartyNumber_uri,
Called_Digits = row.finalCalledPartyNumber,
Duration = TimeSpan.FromSeconds(Convert.ToDouble(row.duration)),
Cost = CallCost,
Reason = CallReason,
Outcome = CallOutcome,
Country_Code = "MUS",//_context.Country.Where(o => o.CountryCode == _CDRExtension.CountryCodeId(_CDRExtension.Call_Type_Id(row.callingPartyNumber))).Select(o => o.CountryCode).FirstOrDefault(),
Second_Ring_Time = CallRingTime,
Category = CallCategory,
CiscoInputId = row.id
};
_context.CDR.Add(cDR);
}
row.Processed = true;
row.DateAdded = DateTime.Now;
_context.Cisco_Input.Update(row);
// }
_context.SaveChanges();
}
}
}
Related
Im facing performance issue in below code in multiple foreach loops. First im getting a list of ReturnDetails and then based on detail id get the HandlingInfo object. Then based on value of action, update the ReturnsDetail Object again.
It take more than a minute for loading 3000 records of ReturnsDetail. While debugging locally, it runs for infinite amount of time.
Please let me know in anyway i can refactor this code .
Thanks for your help.
lstReturnsDetail = dcReturnsService.GetReturnDetailsInfo(header_id);
List<HandlingInfo> lstHandlingInfo = null;
foreach (ReturnsDetail oReturnsDetail in lstReturnsDetail)
{
using (DCReturns_Entities entities = new DCReturns_Entities())
{
lstHandlingInfo = entities.HandlingInfoes.Where(f => f.detail_id == oReturnsDetail.id).ToList();
if(lstHandlingInfo != null)
{
foreach (HandlingInfo oHandlingInfo in lstHandlingInfo)
{
if (oHandlingInfo.action == "DST")
{
oReturnsDetail.destroy += Convert.ToInt32(oHandlingInfo.qty);
}
else if (oHandlingInfo.action == "SHP")
{
oReturnsDetail.to_shop += Convert.ToInt32(oHandlingInfo.qty);
}
else if (oHandlingInfo.action == "RBX")
{
oReturnsDetail.in_stock += Convert.ToInt32(oHandlingInfo.qty);
}
}
}
}
oReturnsDetail.received_qty = oReturnsDetail.destroy + oReturnsDetail.to_shop + oReturnsDetail.in_stock;
}
dgReturnsDetail.DataSource = lstReturnsDetail.OrderByDescending(g => g.id).ToList();
Session[DCReturnsConstants.Returns_Detail_Entity] = lstReturnsDetail;
dgReturnsDetail.DataBind();
this is su-do code! but you should get the jist.
//modify this to return all of them into mem, and then filter on this...
//if it can not be done here then do below..
var lstReturnsDetail = dcReturnsService.GetReturnDetailsInfo(header_id);
//then create a list here which fetches all,
List<[type]> somelist
List<int> listId = lstReturnsDetail.select(x=>x.id).tolist();
using (var db = new DCReturns_Entities())
{
somelist = db.HandlingInfoes.Where(f => listId.Contains( f.detail_id)).ToList();
}
foreach (ReturnsDetail oReturnsDetail in lstReturnsDetail)
{
//performance issue is here
//using (DCReturns_Entities entities = new DCReturns_Entities())
//{
// lstHandlingInfo = entities.HandlingInfoes.Where(f => f.detail_id == oReturnsDetail.id).ToList();
//}
//insead fetach all before, into mem and filter from that list.
var lstHandlingInfo = somelist.Where(f => f.detail_id == oReturnsDetail.id).ToList();
//code ommited for reaablity
}
//code ommited for reaablity
I have simple controller that return list of products.It is take about 5-7sec to return the data for the first time,and then 50-80ms ,but only if i run it immediately(10-30sec) after first one ,if i will run it after 2-3min it will be again 5-7s.
I dont store this list in cache,sow i cant understand why only first time it takes 5sec,and then 50ms.
Important:test that i have made is not on localhost,it is on the production server.On localhost it takes 50ms
How can i speed up?
Do i need to make any change in my code? Or IIS?
My code
public List<PackageModel> GetPromotionOrDefaultPackageList(string domain)
{
List<DBProduct> DBPackageList = new List<DBProduct>();
List<PackageModel> BllPackageList = new List<PackageModel>();
try
{
using (CglDomainEntities _entities = new CglDomainEntities())
{
DBPackageList = _entities.DBProducts
.Where(x =>
x.ProductGroup.Equals(domain + "-package")
&& x.IsPromotionProduct == true)
.ToList();
if (DBPackageList.Count == 0)
{
DBPackageList = _entities.DBProducts
.Where(x =>
x.ProductGroup.Equals(domain + "-package")
&& x.IsBasicProduct == true)
.ToList();
}
}
PackageModel bllPackage = new PackageModel();
foreach (DBProduct dbProduct in DBPackageList)
{
bllPackage = new PackageModel();
bllPackage.Id = dbProduct.Id;
bllPackage.ProductId = dbProduct.ProductId;
bllPackage.IsPromotionProduct = dbProduct.IsPromotionProduct.HasValue ? dbProduct.IsPromotionProduct.Value : false;
bllPackage.ProductName = dbProduct.ProductName;
bllPackage.ProductDescription = dbProduct.ProductDescription;
bllPackage.Price = dbProduct.Price;
bllPackage.Currency = dbProduct.Currency;
bllPackage.CampaignId = dbProduct.CampaignId;
bllPackage.PathToProductImage = dbProduct.PathToProductImage;
bllPackage.PathToProductPromotionImage = dbProduct.PathToProductPromotionImage;
bllPackage.PathToProductInfo = dbProduct.PathToProductInfo;
bllPackage.CssClass = dbProduct.CssClass;
BllPackageList.Add(bllPackage);
}
return BllPackageList;
}
catch (Exception ex)
{
Logger.Error(ex.Message);
return BllPackageList;
}
}
Is there any way I can run a function in parallel with batches of a large dataset on multiple threads in c#?
So I have a list of data with approximate size of 32000 lines. I run the function below which reads each line of the dataset and verifies it. The idea is to separate the dataset into chunks of 5000 and concurrently apply the function below to each chunk/batch.
private void AccountNumberCheck(List<Invoice> invoices, string VendorID)
{
try
{
using (var context = new ApplicationContext())
{
foreach (var invoice in invoices)
{
var invoiceDB = context.Invoices.Find(invoice.Id);
var accountNumber = context.Accounts.Where(m => m.Account_Number == invoice.Account_Number && m.VendorID == VendorID);
if (accountNumber.Count() > 0)
{
var activeAccount = accountNumber.Any(m => m.Active_Status == false);
if (activeAccount == true)
{
invoiceDB.ExceptionFlag = true;
invoiceDB.ExceptionComments = invoiceDB.ExceptionComments + "The Account Number is Inactive.";
}
else
{
invoiceDB.ExceptionFlag = false;
}
}
else
{
invoiceDB.ExceptionFlag = true;
invoiceDB.ExceptionComments = invoiceDB.ExceptionComments + "The Account Number does not exist. ";
}
context.Entry(invoiceDB).State = EntityState.Modified;
context.SaveChanges();
}
}
}
catch (Exception ex)
{
}
}
this might be a simple answer due to my inexperience with C# and .NET. I have two Stripe Test Accounts. TL:DR; is I am essentially looking for a Customers.all solution.
The source account has all the customer, card, and charge data.
The destination account has the copied card and customer data done by Stripe Support.
I have code that loops through the pulls all the data from the source account. It then finds the customer data from the destination account using the collection of customer/card data from the source. After that it then recreates the charges from the source account into the destination account.
I am able to successfully copy the first 100 charges into the destination account using information from the source account but I am having the hardest time getting the rest of the customers.
This is what I have so far:
public static void GenerateDestinationChargeData()
{
// code to get collection of customer data from destination account
StripeConfiguration.SetApiKey(destinationTestKey);
var customerService = new StripeCustomerService();
IEnumerable<StripeCustomer> customerItems = customerService.List(
new StripeCustomerListOptions()
{
Limit = 100,
//this is what I cannot figure out, eventually to get all of the customers from the destination account
StartingAfter = customerItems.LastOrDefault().Id
}
);
// loop through collection of customers from destination acct to fetch customer charge data from source account
foreach (var i in customerItems)
{
bool isError = false;
var liveChargeService = new StripeChargeService();
StripeConfiguration.SetApiKey(sourceTestKey);
StripeList<StripeCharge> chargeItems = new StripeList<StripeCharge>();
chargeItems = liveChargeService.List(
new StripeChargeListOptions()
{
Limit = 100,
CustomerId = i.Id
}
);
// loop through customer charge data from source and re-create charge data on destination Acct
foreach (var c in chargeItems.Data)
{
StripeConfiguration.SetApiKey(sourceTestKey);
var emailReceipt = "";
Dictionary<string, string> chargeMetaData = new Dictionary<string, string>();
var onBehalfOf = "";
var transferGroup = "";
var chargeDescription = "";
var chargeCaptured = "";
var chargeCurrency = "";
var chargeStatementDescriptor = "";
if (c.ReceiptEmail != null)
{
emailReceipt = c.ReceiptEmail;
}
if (c.Metadata != null)
{
chargeMetaData = c.Metadata;
}
if (c.OnBehalfOf != null)
{
onBehalfOf = c.OnBehalfOf.ToString();
}
if (c.TransferGroup != null)
{
transferGroup = c.TransferGroup;
}
if (c.Description != null)
{
chargeDescription = c.Description;
}
if (c.Captured != null)
{
chargeCaptured = c.Captured.ToString();
}
if (c.Currency != null)
{
chargeCurrency = c.Currency;
}
if (c.StatementDescriptor != null)
{
chargeStatementDescriptor = c.StatementDescriptor;
}
try
{
var chargeOptions = new StripeChargeCreateOptions();
chargeOptions.CustomerId = i.Id;
chargeOptions.ReceiptEmail = emailReceipt;
chargeOptions.Metadata = chargeMetaData;
chargeOptions.Description = chargeDescription;
chargeOptions.Capture = c.Captured;
chargeOptions.Currency = chargeCurrency;
chargeOptions.Amount = c.Amount;
chargeOptions.StatementDescriptor = chargeStatementDescriptor;
StripeChargeService chargeService = new StripeChargeService(destinationTestKey);
StripeCharge stripeCharge = chargeService.Create(chargeOptions);
}
catch (Exception ex)
{
Utility.NotifyDevAdminException("test", ex);
isError = true;
}
if (isError) continue;
}
}
}
Thank you so much :)
Since we cannot do a Customers.all with this current Stripe API, the solution is to set an empty variable and assign it the last Customer ID in the first set of 100 that we get and continue the query from that last assigned value
var lastId = String.Empty;
if (String.IsNullOrEmpty(lastId))
{
StripeConfiguration.SetApiKey(sourceCustomerAccountAPIKey);
customerItems = customerService.List(
new StripeCustomerListOptions(){ Limit = 100 });
}
else
{
StripeConfiguration.SetApiKey(sourceCustomerAccountAPIKey);
customerItems = customerService.List(
new StripeCustomerListOptions() {
Limit = 100,
StartingAfter = lastId });
}
lastId = customerItems.LastOrDefault().Id;
I am writing a small data migration tools from one big database to another small database. All of the others data migration method worked satisfactorily, but the following method has given an exception from the SKIP VALUE IS 100. I run this console script remotely as well as inside of the source server also. I tried in many different was to find the actual problem what it is. After then I found that only from the SKIP VALUE IS 100 it is not working for any TAKE 1,2,3,4,5 or ....
Dear expertise, I don't have any prior knowledge on that type of problem. Any kind of suggestions or comments is appreciatable to resolve this problem. Thanks for you time.
I know this code is not clean and the method is too long. I just tried solve this by adding some line of extra code. Because the problem solving is my main concern. I just copy past the last edited method.
In shot the problem I can illustrate with this following two line
var temp = queryable.Skip(90).Take(10).ToList(); //no exception
var temp = queryable.Skip(100).Take(10).ToList(); getting exception
private static void ImporterDataMigrateToRmgDb(SourceDBEntities sourceDb, RmgDbContext rmgDb)
{
int skip = 0;
int take = 10;
int count = sourceDb.FormAs.Where(x=> x.FormAStateId == 8).GroupBy(x=> x.ImporterName).Count();
Console.WriteLine("Total Possible Importer: " + count);
for (int i = 0; i < count/take; i++)
{
IOrderedQueryable<FormA> queryable = sourceDb.FormAs.Where(x => x.FormAStateId == 8).OrderBy(x => x.ImporterName);
List<IGrouping<string, FormA>> list;
try
{
list = queryable.Skip(skip).Take(take).GroupBy(x => x.ImporterName).ToList();
//this line is getting timeout exception from the skip value of 100.
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
sourceDb.Dispose();
rmgDb.Dispose();
sourceDb = new SourceDBEntities();
rmgDb = new RmgDbContext();
skip += take;
continue;
}
if (list.Count > 0)
{
foreach (var l in list)
{
List<FormA> formAs = l.ToList();
FormA formA = formAs.FirstOrDefault();
if (formA == null) continue;
Importer importer = formA.ConvertToRmgImporterFromFormA();
Console.WriteLine(formA.FormANo + " " + importer.Name);
var importers = rmgDb.Importers.Where(x => x.Name.ToLower() == importer.Name.ToLower()).ToList();
//bool any = rmgDb.Importers.Any(x => x.Name.ToLower() == formA.ImporterName.ToLower());
if (importers.Count() == 1)
{
foreach (var imp in importers)
{
Importer entity = rmgDb.Importers.Find(imp.Id);
entity.Country = importer.Country;
entity.TotalImportedAmountInUsd = importer.TotalImportedAmountInUsd;
rmgDb.Entry(entity).State = EntityState.Modified;
}
}
else
{
rmgDb.Importers.Add(importer);
}
rmgDb.SaveChanges();
Console.WriteLine(importer.Name);
}
}
skip += take;
}
Console.WriteLine("Importer Data Migration Completed");
}
I have fixed my problem by modifying following code
var queryable =
sourceDb.FormAs.Where(x => x.FormAStateId == 8)
.Select(x => new Adapters.ImporterBindingModel()
{
Id = Guid.NewGuid().ToString(),
Active = true,
Created = DateTime.Now,
CreatedBy = "System",
Modified = DateTime.Now,
ModifiedBy = "System",
Name = x.ImporterName,
Address = x.ImporterAddress,
City = x.City,
ZipCode = x.ZipCode,
CountryId = x.CountryId
})
.OrderBy(x => x.Name);