using System; using System.Collections.Generic; using System.Linq; using AudibleApi.Common; using AudibleUtilities; using DataLayer; namespace DtoImporterService { public class ContributorImporter : ItemsImporterBase { public ContributorImporter(LibationContext context) : base(context) { } public override IEnumerable Validate(IEnumerable importItems) => new ContributorValidator().Validate(importItems.Select(i => i.DtoItem)); protected override int DoImport(IEnumerable importItems) { // get distinct var authors = importItems .Select(i => i.DtoItem) .GetAuthorsDistinct() .ToList(); var narrators = importItems .Select(i => i.DtoItem) .GetNarratorsDistinct() .ToList(); var publishers = importItems .Select(i => i.DtoItem) .GetPublishersDistinct() .ToList(); // load db existing => .Local var allNames = publishers .Union(authors.Select(n => n.Name)) .Union(narrators.Select(n => n.Name)) .Where(name => !string.IsNullOrWhiteSpace(name)) .ToList(); loadLocal_contributors(allNames); // upsert var qtyNew = 0; qtyNew += upsertPeople(authors); qtyNew += upsertPeople(narrators); qtyNew += upsertPublishers(publishers); return qtyNew; } private void loadLocal_contributors(List contributorNames) { // must include default/empty/missing contributorNames.Add(Contributor.GetEmpty().Name); //// BAD: very inefficient // var x = context.Contributors.Local.Where(c => !contribNames.Contains(c.Name)); // GOOD: Except() is efficient. Due to hashing, it's close to O(n) var localNames = DbContext.Contributors.Local.Select(c => c.Name).ToList(); var remainingContribNames = contributorNames .Distinct() .Except(localNames) .ToList(); // load existing => local if (remainingContribNames.Any()) DbContext.Contributors.Where(c => remainingContribNames.Contains(c.Name)).ToList(); } // only use after loading contributors => local private int upsertPeople(List people) { var localNames = DbContext.Contributors.Local.Select(c => c.Name).ToList(); var newPeople = people .Select(p => p.Name) .Distinct() .Except(localNames) .ToList(); var groupby = people.GroupBy( p => p.Name, p => p, (key, g) => new { Name = key, People = g.ToList() } ); foreach (var name in newPeople) { var p = groupby.Single(g => g.Name == name).People.First(); try { DbContext.Contributors.Add(new Contributor(p.Name, p.Asin)); } catch (Exception ex) { Serilog.Log.Logger.Error(ex, "Error adding person. {@DebugInfo}", new { p?.Name, p?.Asin }); throw; } } return newPeople.Count; } // only use after loading contributors => local private int upsertPublishers(List publishers) { var localNames = DbContext.Contributors.Local.Select(c => c.Name).ToList(); var newPublishers = publishers .Distinct() .Except(localNames) .ToList(); foreach (var pub in newPublishers) { try { DbContext.Contributors.Add(new Contributor(pub)); } catch (Exception ex) { Serilog.Log.Logger.Error(ex, "Error adding publisher. {@DebugInfo}", new { pub }); throw; } } return newPublishers.Count; } } }