public async Task <ActionResult <UserInstitution> > PostUserInstitution(UserInstitution userInstitution) { _context.UserInstitutions.Add(userInstitution); await _context.SaveChangesAsync(); return(CreatedAtAction("GetUserInstitution", new { id = userInstitution.Id }, userInstitution)); }
public async Task <IActionResult> PutUserInstitution(Guid id, UserInstitution userInstitution) { if (id != userInstitution.Id) { return(BadRequest()); } _context.Entry(userInstitution).State = EntityState.Modified; try { await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!UserInstitutionExists(id)) { return(NotFound()); } else { throw; } } return(NoContent()); }
/// <summary> /// Scrap user by using browser (usually Chrome browser). /// Slow algorithm but safe method /// </summary> /// <param name="profileLink">Full link to user profile starts with 'https://m.facebook.com/'</param> /// <param name="isInFriendList">If user has in your friend list</param> /// <param name="callback">Callback function after completing this method</param> public void ScrapUserBrowser(string profileLink, bool isInFriendList = false, Action callback = null) { _startupWindow = _driver.CurrentWindowHandle; OpenNewTab(profileLink); WaitForReady(By.Id("timelineContextList")); _htmlDoc.LoadHtml(_driver.PageSource); var actionBarButtonNodes = _htmlDoc.DocumentNode.SelectNodes("//*[@data-sigil='hq-profile-logging-action-bar-button flyout-causal']"); string jsonData; if (actionBarButtonNodes.Count >= 2) { jsonData = _htmlDoc.DocumentNode.SelectNodes("//*[@data-sigil='hq-profile-logging-action-bar-button flyout-causal']")[1].Attributes["data-store"].DeEntitizeValue; } else { jsonData = _htmlDoc.DocumentNode.SelectNodes("//*[@data-sigil='hq-profile-logging-action-bar-button flyout-causal']")[0].Attributes["data-store"].DeEntitizeValue; } string profileId = JObject.Parse(jsonData)["hq-profile-logging"]["profile_id"].ToString(); string sessionToken = JObject.Parse(jsonData)["hq-profile-logging"]["profile_session_token"].ToString(); using (var db = new ApplicationDbContext()) { if (UserExistsInDb((i => i.Id == profileId), db) || profileId == "100004966276119") { _driver.Close(); _driver.SwitchTo().Window(_startupWindow); callback?.Invoke(); return; } var membershipNode = _htmlDoc.DocumentNode.SelectSingleNode("//*[@id='profile_intro_card']/.//span[contains(text(), 'На Facebook с')]"); var bioNode = _htmlDoc.GetElementbyId("bio"); string userFullName = _htmlDoc.DocumentNode.SelectSingleNode("//*[@id='cover-name-root']/h3").InnerText; string username = profileLink.Split('/').Last(); string headerImgSrc = "null"; string profileImgSrc = "null"; string profileBio = "null"; if (IsElementPresent(By.XPath("//div[@data-sigil='cover-photo']/a/i"))) { headerImgSrc = _driver.FindElement(By.XPath("//div[@data-sigil='cover-photo']/a/i")).GetCssValue("background").Split("url(")[1].Split("\"")[1]; } if (IsElementPresent(By.XPath("//i[@class='img profpic']"))) { profileImgSrc = _driver.FindElement(By.XPath("//i[@class='img profpic']")).GetCssValue("background").Split("url(")[1].Split("\"")[1]; } if (bioNode != null) { profileBio = bioNode.InnerText; } var user = new User() { Id = profileId, Username = username, ProfilePhotoSrc = profileImgSrc, HeaderPhotoSrc = headerImgSrc, Bio = profileBio, IsMyFriend = isInFriendList }; user.ParseFLName(userFullName); if (membershipNode != null) { user.MemberSince = DateTime.Parse(membershipNode.InnerText.Split(" с ")[1]); } string aboutUrl = "about"; if (profileLink.Contains("profile.php")) { aboutUrl = $"?v=info&Ist={sessionToken}&id={sessionToken.Split(':')[1]}"; _driver.Url = $"https://m.facebook.com/profile.php{aboutUrl}"; } else { _driver.Navigate().GoToUrl($"{profileLink.Trim()}/{aboutUrl}"); } _htmlDoc.LoadHtml(_driver.PageSource); var contactInfo = _htmlDoc.DocumentNode.SelectSingleNode("//*[@id='contact-info']/div"); var basicInfo = _htmlDoc.DocumentNode.SelectSingleNode("//*[@id='basic-info']/div"); var educations = _htmlDoc.DocumentNode.SelectSingleNode("//*[@id='education']/div"); var works = _htmlDoc.DocumentNode.SelectSingleNode("//*[@id='work']/div"); var livedCities = _htmlDoc.DocumentNode.SelectSingleNode("//*[@id='living']/div"); var skills = _htmlDoc.DocumentNode.SelectSingleNode("//*[@id='skills']/div"); var quote = _htmlDoc.DocumentNode.SelectSingleNode("//*[@id='quote']/div"); var relationship = _htmlDoc.DocumentNode.SelectSingleNode("//*[@id='relationship']/div"); if (basicInfo != null) { foreach (var item in basicInfo.ChildNodes) { if (item.Attributes["title"].Value == "Дата рождения") { string birthdayNode = item.SelectSingleNode(".//div[@class='lr']/div[contains(@class, ' r')]").InnerText; if (DateTime.TryParse(birthdayNode, out DateTime birthday)) { user.Birthday = birthday; } } if (item.Attributes["title"].Value == "Пол") { user.Gender = item.SelectSingleNode(".//div[@class='lr']/div[contains(@class, ' r')]").InnerText; } if (item.Attributes["title"].Value == "Языки") { user.Languages = item.SelectSingleNode(".//div[@class='lr']/div[contains(@class, ' r')]").InnerText; } if (item.Attributes["title"].Value == "Религиозные взгляды") { user.ReligiousView = item.SelectSingleNode(".//div[@class='lr']/div[contains(@class, ' r')]").InnerText; } } } if (contactInfo != null) { var contactNumbersList = new List <string>(); var webSitesList = new List <string>(); foreach (var item in contactInfo.ChildNodes) { if (item.Attributes["title"].Value == "Мобильный") { contactNumbersList.Add(item.SelectSingleNode(".//div[@class='lr']/div[contains(@class, ' r')]").InnerText); } if (item.Attributes["title"].Value == "OK") { user.OK = item.SelectSingleNode(".//div[@class='lr']/div[contains(@class, ' r')]").InnerText; } if (item.Attributes["title"].Value == "Twitter") { user.Twitter = item.SelectSingleNode(".//div[@class='lr']/div[contains(@class, ' r')]").InnerText; } if (item.Attributes["title"].Value == "VK") { user.VK = item.SelectSingleNode(".//div[@class='lr']/div[contains(@class, ' r')]").InnerText; } if (item.Attributes["title"].Value == "Instagram") { user.Instagram = item.SelectSingleNode(".//div[@class='lr']/div[contains(@class, ' r')]").InnerText; } if (item.Attributes["title"].Value == "YouTube") { user.YouTube = item.SelectSingleNode(".//div[@class='lr']/div[contains(@class, ' r')]").InnerText; } if (item.Attributes["title"].Value == "LinkedIn") { user.LinkedIn = item.SelectSingleNode(".//div[@class='lr']/div[contains(@class, ' r')]").InnerText; } if (item.Attributes["title"].Value == "GitHub") { user.GitHub = item.SelectSingleNode(".//div[@class='lr']/div[contains(@class, ' r')]").InnerText; } if (item.Attributes["title"].Value == "Веб-сайты") { webSitesList.Add(item.SelectSingleNode(".//div[@class='lr']/div[contains(@class, ' r')]").InnerText); } } user.ContactNumbers = string.Join(',', contactNumbersList); user.WebSites = string.Join(',', webSitesList); } if (educations != null) { var institutions = new HashSet <UserInstitution>(new UserInstitutionComparer()); foreach (var item in educations.ChildNodes) { var node = item.SelectSingleNode(".//div/span/a"); string link = node.Attributes["href"].Value; string name = node.InnerText; string institutionType = item.SelectNodes(".//div/span")[1].InnerText; var institution = new Institution() { Name = name, Link = link }; if (institutionType != "Средняя школа") { institution.IsHigherEducation = true; } if (db.Institutions.Where(i => i.Link == institution.Link).FirstOrDefault() != null) { institution = db.Institutions.Where(i => i.Link == institution.Link).FirstOrDefault(); } else { institution = db.Institutions.Add(institution).Entity; } var userInstitution = new UserInstitution() { Institution = institution }; institutions.Add(userInstitution); } user.Institutions.AddRange(institutions); } if (works != null) { foreach (var item in works.ChildNodes) { var node = item.SelectSingleNode(".//span/a"); string link = node.Attributes["href"].Value; string name = node.InnerText; string position = "null"; string workingPeriod = "null"; var company = new Company() { Name = name, Link = link }; if (db.Companies.Where(i => i.Link == company.Link).FirstOrDefault() != null) { company = db.Companies.Where(i => i.Link == company.Link).FirstOrDefault(); } else { company = db.Companies.Add(company).Entity; } var work = new Employee() { Company = company }; if (item.SelectNodes(".//span").Count > 2) { position = item.SelectNodes(".//span")[1].InnerText; workingPeriod = item.SelectNodes(".//span")[2].InnerText; work.Position = position; if (workingPeriod.Contains("по настоящее время")) { work.IsCurrentlyWorking = true; string startDateString = workingPeriod.Substring(3, (workingPeriod.IndexOf("по ") - 3)); if (DateTime.TryParse(startDateString, out DateTime startWorkDate)) { work.StartWorkDate = startWorkDate; } } else if (workingPeriod == "Я работаю здесь в настоящее время") { work.IsCurrentlyWorking = true; } else { string[] dates = null; if (workingPeriod.Contains('-')) { dates = workingPeriod.Split('-'); } else if (workingPeriod.Contains('—')) { dates = workingPeriod.Split('—'); } if (dates != null && dates.Length >= 2) { if (DateTime.TryParse(dates[0], out DateTime startWorkDate)) { work.StartWorkDate = startWorkDate; } if (DateTime.TryParse(dates[1], out DateTime endWorkDate)) { work.EndWorkDate = endWorkDate; } } } } user.Works.Add(work); } } if (livedCities != null) { var livedCitiesList = new List <string>(); foreach (var item in livedCities.ChildNodes) { var nodes = item.SelectNodes(".//header/h4"); string cityName = nodes[0].InnerText; string livingType = nodes[1].InnerText; if (livingType == "Родной город") { user.Hometown = cityName; } else { livedCitiesList.Add(cityName); } } user.LivedCities = string.Join(" * ", livedCitiesList); } if (skills != null) { user.Skills = skills.InnerText; } if (quote != null) { user.Quote = quote.InnerText; } if (relationship != null) { user.MaritalStatus = relationship.InnerText; } db.Users.Add(user); db.SaveChanges(); Console.Write(user); } _driver.Close(); _driver.SwitchTo().Window(_startupWindow); callback?.Invoke(); }
/// <summary> /// Scrap user by using browser (usually Chrome browser). /// Fast algorithm but safeless method /// </summary> /// <param name="profileLink">Full link to user profile starts with 'https://m.facebook.com/'</param> /// <param name="isInFriendList">If user has in your friend list</param> /// <param name="callback">Callback function, will invoke after completing this method</param> public void ScrapUserHidelessBrowser(string profileLink, bool isInFriendList = false, Action callback = null) { var aboutPage = _browser.NavigateToPage(new Uri($"{profileLink}/about/")); var document = _parser.ParseDocument(aboutPage.Content); var profileId = document.Body.QuerySelectorAll("a").Where(i => i.GetAttribute("href").Contains("owner_id")).First().GetAttribute("href").Split("owner_id=")[1]; using (var db = new ApplicationDbContext()) { if (UserExistsInDb((i => i.Id == profileId), db) || profileId == "100004966276119") { return; } string username = profileLink.Split('/').Last(); string profileBio = document.Body.QuerySelector("div.co.cp.cq.cr")?.TextContent; string userFullName = document.Body.QuerySelector("span > strong")?.TextContent; string headerImgSrc = document.Body.QuerySelector("#profile_cover_photo_container > a > img")?.GetAttribute("src"); string profileImgSrc = document.Body.QuerySelector("img.cb.r")?.GetAttribute("src"); var user = new User() { Id = profileId, Username = username, ProfilePhotoSrc = profileImgSrc, HeaderPhotoSrc = headerImgSrc, Bio = profileBio, IsMyFriend = isInFriendList }; user.ParseFLName(userFullName); var basicInfo = document.Body.QuerySelector("#basic-info > div > div:nth-child(2)"); var contactInfo = document.Body.QuerySelector("#contact-info > div > div:nth-child(2)"); var educations = document.Body.QuerySelector("#education > div > div:nth-child(2)"); var works = document.Body.QuerySelector("#work > div > div:nth-child(2)"); var livedCities = document.Body.QuerySelector("#living > div > div:nth-child(2)"); var skills = document.Body.QuerySelector("#skills > div > div:nth-child(2)"); var quote = document.Body.QuerySelector("#quote > div > div:nth-child(2)"); var relationship = document.Body.QuerySelector("#relationship > div > div:nth-child(2)"); var contactNumbersList = new List <string>(); var webSitesList = new List <string>(); var livedCitiesList = new List <string>(); var institutions = new HashSet <UserInstitution>(new UserInstitutionComparer()); if (basicInfo != null) { foreach (var item in basicInfo.Children) { if (item.Attributes["title"].Value == "Дата рождения") { string birthdayNode = item.QuerySelector("td[valign='top']:nth-child(2)").TextContent; if (DateTime.TryParse(birthdayNode, out DateTime birthday)) { user.Birthday = birthday; } } if (item.Attributes["title"].Value == "Пол") { user.Gender = item.QuerySelector("td[valign='top']:nth-child(2)").TextContent; } if (item.Attributes["title"].Value == "Языки") { user.Languages = item.QuerySelector("td[valign='top']:nth-child(2)").TextContent; } if (item.Attributes["title"].Value == "Религиозные взгляды") { user.ReligiousView = item.QuerySelector("td[valign='top']:nth-child(2)").TextContent; } } } if (contactInfo != null) { foreach (var item in contactInfo.Children) { if (item.Attributes["title"].Value == "Мобильный") { contactNumbersList.Add(item.QuerySelector("td[valign='top']:nth-child(2)").TextContent); } if (item.Attributes["title"].Value == "OK") { user.OK = item.QuerySelector("td[valign='top']:nth-child(2)").TextContent; } if (item.Attributes["title"].Value == "Twitter") { user.Twitter = item.QuerySelector("td[valign='top']:nth-child(2)").TextContent; } if (item.Attributes["title"].Value == "VK") { user.VK = item.QuerySelector("td[valign='top']:nth-child(2)").TextContent; } if (item.Attributes["title"].Value == "Instagram") { user.Instagram = item.QuerySelector("td[valign='top']:nth-child(2)").TextContent; } if (item.Attributes["title"].Value == "YouTube") { user.YouTube = item.QuerySelector("td[valign='top']:nth-child(2)").TextContent; } if (item.Attributes["title"].Value == "LinkedIn") { user.LinkedIn = item.QuerySelector("td[valign='top']:nth-child(2)").TextContent; } if (item.Attributes["title"].Value == "GitHub") { user.GitHub = item.QuerySelector("td[valign='top']:nth-child(2)").TextContent; } if (item.Attributes["title"].Value == "Веб-сайты") { webSitesList.Add(item.QuerySelector("td[valign='top']:nth-child(2)").TextContent); } } } if (educations != null) { foreach (var item in educations.Children) { var nodes = item.QuerySelectorAll("div > span"); var eduNode = nodes.Select(i => i.QuerySelector("a")).First(); string link = eduNode.Attributes["href"].Value; string name = eduNode.TextContent; string institutionType = nodes[1].TextContent; var institution = new Institution() { Name = name, Link = link }; if (institutionType != "Средняя школа") { institution.IsHigherEducation = true; } if (db.Institutions.Where(i => i.Link == institution.Link).FirstOrDefault() != null) { institution = db.Institutions.Where(i => i.Link == institution.Link).FirstOrDefault(); } else { institution = db.Institutions.Add(institution).Entity; } var userInstitution = new UserInstitution() { Institution = institution }; institutions.Add(userInstitution); } } if (works != null) { foreach (var item in works.Children) { var node = item.QuerySelector("span > a"); string link = node.Attributes["href"].Value; string name = node.TextContent; string position = "null"; string workingPeriod = "null"; var company = new Company() { Name = name, Link = link }; if (db.Companies.Where(i => i.Link == company.Link).FirstOrDefault() != null) { company = db.Companies.Where(i => i.Link == company.Link).FirstOrDefault(); } else { company = db.Companies.Add(company).Entity; } var work = new Employee() { Company = company }; if (item.QuerySelectorAll("span").Length > 2) { position = item.QuerySelectorAll("span")[1]?.TextContent; workingPeriod = item.QuerySelectorAll("span")[2]?.TextContent; work.Position = position; if (workingPeriod.Contains("по настоящее время")) { work.IsCurrentlyWorking = true; string startDateString = workingPeriod.Substring(3, (workingPeriod.IndexOf("по ") - 3)); if (DateTime.TryParse(startDateString, out DateTime startWorkDate)) { work.StartWorkDate = startWorkDate; } } else if (workingPeriod == "Я работаю здесь в настоящее время") { work.IsCurrentlyWorking = true; } else { string[] dates = null; if (workingPeriod.Contains('-')) { dates = workingPeriod.Split('-'); } else if (workingPeriod.Contains('—')) { dates = workingPeriod.Split('—'); } if (dates != null && dates.Length >= 2) { if (DateTime.TryParse(dates[0], out DateTime startWorkDate)) { work.StartWorkDate = startWorkDate; } if (DateTime.TryParse(dates[1], out DateTime endWorkDate)) { work.EndWorkDate = endWorkDate; } } } } user.Works.Add(work); } } if (livedCities != null) { foreach (var item in livedCities.Children) { string cityName = item.QuerySelector("td[valign='top']:nth-child(2)").TextContent; string livingType = item.QuerySelector("td[valign='top']:nth-child(1)").TextContent; if (livingType == "Родной город") { user.Hometown = cityName; } else { livedCitiesList.Add(cityName); } } } user.Skills = skills?.TextContent; user.Quote = quote?.TextContent; user.MaritalStatus = relationship?.TextContent; if (contactNumbersList.Count > 0) { user.ContactNumbers = string.Join(',', contactNumbersList); } if (webSitesList.Count > 0) { user.WebSites = string.Join(',', webSitesList); } if (livedCitiesList.Count > 0) { user.LivedCities = string.Join(" * ", livedCitiesList); } if (institutions.Count > 0) { user.Institutions.AddRange(institutions); } db.Users.Add(user); db.SaveChanges(); Console.Write(user); } callback?.Invoke(); }
private async Task GenerateTestData() { var overallStopwatch = Stopwatch.StartNew(); var userId = Guid.NewGuid(); var now = DateTime.UtcNow; var random = new Random(); var userStopwatch = Stopwatch.StartNew(); var user = new User { Id = userId.ToString(), CreatedDateTimeUTC = now, FiservUser = new FiservUser { CashEdgeId = "123456", CreatedDateTimeUTC = now, ModifiedDateTimeUTC = now, Password = Guid.NewGuid().ToString(), Username = Guid.NewGuid().ToString() }, PartitionKey = userId.ToString(), }; await _userPartitionRepository.AddItemAsync(user); userStopwatch.Stop(); _logger.LogInformation("Added user in {userElapsed}ms", userStopwatch.ElapsedMilliseconds); var numberOfUserInstitutions = random.Next(1, 5); for (int i = 0; i < numberOfUserInstitutions; i++) { var userInstitutionStopwatch = Stopwatch.StartNew(); var userInstitutionId = Guid.NewGuid(); var userInstitution = new UserInstitution { Active = true, CreatedDateTimeUTC = now, CredentialUpdateRequestDateTimeUTC = now, FinancialInstitutionLoginAccountId = 123, HarvestAddId = "ABC123", HasInvalidCredentials = true, HasMfa = false, Institution = new InstitutionSlim { Name = "Chase" }, Id = userInstitutionId.ToString(), LoginParameterHash = "hash", ModifiedDateTimeUTC = now, RunId = 1234567890, PartitionKey = userId.ToString() }; await _userPartitionRepository.AddItemAsync(userInstitution); userInstitutionStopwatch.Stop(); _logger.LogInformation("Added user institutions in {elapsed}ms", userInstitutionStopwatch.ElapsedMilliseconds); var numberOfFInancialAccounts = random.Next(1, 8); for (int j = 0; j < numberOfFInancialAccounts; j++) { var financialAccountStopwatch = Stopwatch.StartNew(); var financialAccountId = Guid.NewGuid(); var financialAccount1 = new FinancialAccount { AggregatorAccountId = "1", AggregatorLastUpdateAttempt = now, AggregatorLastUpdateSuccess = now, CreatedDateTimeUTC = now, ModifiedDateTimeUTC = now, Type = AcctType.CCA, ExtendedType = ExtAcctType.CCA, Owners = new List <Owner> { new Owner { CreatedDateTimeUTC = now, ModifiedDateTimeUTC = now, Name = "Lewis Hamilton" }, new Owner { CreatedDateTimeUTC = now, ModifiedDateTimeUTC = now, Name = "Lando Norris" } }, Id = financialAccountId.ToString(), IsActive = true, Name = "Credit Card", Number = "123", PartitionKey = userInstitutionId.ToString() }; await _userInstitutionPartitionRepository.AddItemAsync(financialAccount1); financialAccountStopwatch.Stop(); _logger.LogInformation("Add financial account in {elapsed}ms", financialAccountStopwatch.ElapsedMilliseconds); var balanceStopwatch = Stopwatch.StartNew(); var balances = new List <Balance> { new Balance { Amount = new Amount { Currency = CurCodeType.USD, Value = 3.50m }, CreatedDateTimeUTC = now, ModifiedDateTimeUTC = now, PartitionKey = financialAccountId.ToString(), Type = BalType.Avail }, new Balance { Amount = new Amount { Currency = CurCodeType.USD, Value = 3.50m }, CreatedDateTimeUTC = now, ModifiedDateTimeUTC = now, PartitionKey = financialAccountId.ToString(), Type = BalType.Current } }; balanceStopwatch.Stop(); await _financialAccountPartitionRepository.AddItemsAsync(balances).ContinueWith(act => { _logger.LogInformation("Added 2 balances in {elapsed}ms", balanceStopwatch.ElapsedMilliseconds); }); var currentBalances = balances.Select(b => new CurrentBalance { Amount = b.Amount, Type = b.Type }) .ToList(); userInstitution.FinancialAccounts.Add(new FinancialAccountSlim { AcountNumber = financialAccount1.Number, CreatedDateTimeUTC = now, CurrentBalances = currentBalances }); await _userInstitutionPartitionRepository.UpdateItemAsync(userInstitution); var numberOfTransactions = random.Next(1, 8000); var transactions = new List <Transaction>(); var transactionStopwatch = Stopwatch.StartNew(); var userTransactions = new List <UserTransaction>(); var userTransactionStopwatch = Stopwatch.StartNew(); for (int k = 0; k < numberOfTransactions; k++) { var transaction = new Transaction { AggregatorTransactionId = 1, Amount = new Amount { Currency = CurCodeType.USD, Value = 3.50m }, Category = "Test", CreatedDateTimeUTC = now, PartitionKey = financialAccountId.ToString(), Memo = "memo", ModifiedDateTimeUTC = now, PostedDate = now, Subcategory = "Test", Type = TransactionType.DEBIT }; var doc = JsonSerializer.Serialize(transaction); transactions.Add(transaction); var userTransaction = new UserTransaction { AggregatorTransactionId = transaction.AggregatorTransactionId, Amount = transaction.Amount, Category = transaction.Category, CreatedDateTimeUTC = transaction.CreatedDateTimeUTC, FinancialAccountId = financialAccountId, Id = transaction.Id, Memo = transaction.Memo, ModifiedDateTimeUTC = transaction.ModifiedDateTimeUTC, PartitionKey = userId.ToString(), PostedDate = transaction.PostedDate, Subcategory = transaction.Subcategory, Type = transaction.Type, }; userTransactions.Add(userTransaction); } await _userPartitionRepository.AddItemsAsync(userTransactions).ContinueWith(act => { userTransactionStopwatch.Stop(); _logger.LogInformation("Added {count} user transactions in {elapsed}ms", numberOfTransactions, userTransactionStopwatch.ElapsedMilliseconds); }); await _financialAccountPartitionRepository.AddItemsAsync(transactions).ContinueWith(act => { userTransactionStopwatch.Stop(); _logger.LogInformation("Add {count} transactions in {elapsed}ms", numberOfTransactions, transactionStopwatch.ElapsedMilliseconds); }); } } overallStopwatch.Stop(); _logger.LogInformation("Completed add user {userId} in {overall}ms", userId, overallStopwatch.ElapsedMilliseconds); }