cross thread issue. add temp time logging in ImportAccountAsync
This commit is contained in:
parent
dccb2d73d6
commit
57fa1bd763
@ -65,20 +65,39 @@ namespace ApplicationServices
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
|
||||||
|
record timeLogEntry(string msg, long totalElapsed, long delta);
|
||||||
|
static List<timeLogEntry> __log { get; } = new List<timeLogEntry> { new("begin", 0, 0) };
|
||||||
|
static void logTime(string s)
|
||||||
|
{
|
||||||
|
var totalElapsed = sw.ElapsedMilliseconds;
|
||||||
|
|
||||||
|
var prev = __log.Last().totalElapsed;
|
||||||
|
var delta = totalElapsed - prev;
|
||||||
|
|
||||||
|
__log.Add(new(s, totalElapsed, delta));
|
||||||
|
}
|
||||||
|
|
||||||
#region FULL LIBRARY scan and import
|
#region FULL LIBRARY scan and import
|
||||||
public static async Task<(int totalCount, int newCount)> ImportAccountAsync(Func<Account, ILoginCallback> loginCallbackFactoryFunc, params Account[] accounts)
|
public static async Task<(int totalCount, int newCount)> ImportAccountAsync(Func<Account, ILoginCallback> loginCallbackFactoryFunc, params Account[] accounts)
|
||||||
{
|
{
|
||||||
|
sw.Start();
|
||||||
|
|
||||||
if (accounts is null || accounts.Length == 0)
|
if (accounts is null || accounts.Length == 0)
|
||||||
return (0, 0);
|
return (0, 0);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
logTime($"pre {nameof(scanAccountsAsync)} all");
|
||||||
var importItems = await scanAccountsAsync(loginCallbackFactoryFunc, accounts);
|
var importItems = await scanAccountsAsync(loginCallbackFactoryFunc, accounts);
|
||||||
|
logTime($"post {nameof(scanAccountsAsync)} all");
|
||||||
|
|
||||||
var totalCount = importItems.Count;
|
var totalCount = importItems.Count;
|
||||||
Log.Logger.Information($"GetAllLibraryItems: Total count {totalCount}");
|
Log.Logger.Information($"GetAllLibraryItems: Total count {totalCount}");
|
||||||
|
|
||||||
|
logTime($"pre {nameof(importIntoDbAsync)}");
|
||||||
var newCount = await importIntoDbAsync(importItems);
|
var newCount = await importIntoDbAsync(importItems);
|
||||||
|
logTime($"post {nameof(importIntoDbAsync)}");
|
||||||
Log.Logger.Information($"Import: New count {newCount}");
|
Log.Logger.Information($"Import: New count {newCount}");
|
||||||
|
|
||||||
return (totalCount, newCount);
|
return (totalCount, newCount);
|
||||||
@ -106,6 +125,14 @@ namespace ApplicationServices
|
|||||||
Log.Logger.Error(ex, "Error importing library");
|
Log.Logger.Error(ex, "Error importing library");
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
sw.Stop();
|
||||||
|
var logOutput
|
||||||
|
= $"{nameof(timeLogEntry.msg)}\t{nameof(timeLogEntry.totalElapsed)}\t{nameof(timeLogEntry.delta)}\r\n"
|
||||||
|
+ __log.Select(t => $"{t.msg}\t{t.totalElapsed}\t{t.delta}").Aggregate((a, b) => $"{a}\r\n{b}");
|
||||||
|
var putBreakPointHere = logOutput;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<List<ImportItem>> scanAccountsAsync(Func<Account, ILoginCallback> loginCallbackFactoryFunc, Account[] accounts)
|
private static async Task<List<ImportItem>> scanAccountsAsync(Func<Account, ILoginCallback> loginCallbackFactoryFunc, Account[] accounts)
|
||||||
@ -137,19 +164,28 @@ namespace ApplicationServices
|
|||||||
Account = account?.MaskedLogEntry ?? "[null]"
|
Account = account?.MaskedLogEntry ?? "[null]"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
logTime($"pre scanAccountAsync {account.AccountName}");
|
||||||
|
|
||||||
var dtoItems = await AudibleApiActions.GetLibraryValidatedAsync(api, LibraryResponseGroups);
|
var dtoItems = await AudibleApiActions.GetLibraryValidatedAsync(api, LibraryResponseGroups);
|
||||||
|
|
||||||
|
logTime($"post scanAccountAsync {account.AccountName} qty: {dtoItems.Count}");
|
||||||
|
|
||||||
return dtoItems.Select(d => new ImportItem { DtoItem = d, AccountId = account.AccountId, LocaleName = account.Locale?.Name }).ToList();
|
return dtoItems.Select(d => new ImportItem { DtoItem = d, AccountId = account.AccountId, LocaleName = account.Locale?.Name }).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<int> importIntoDbAsync(List<ImportItem> importItems)
|
private static async Task<int> importIntoDbAsync(List<ImportItem> importItems)
|
||||||
{
|
{
|
||||||
|
logTime("importIntoDbAsync -- pre db");
|
||||||
using var context = DbContexts.GetContext();
|
using var context = DbContexts.GetContext();
|
||||||
var libraryImporter = new LibraryImporter(context);
|
var libraryImporter = new LibraryBookImporter(context);
|
||||||
var newCount = await Task.Run(() => libraryImporter.Import(importItems));
|
var newCount = await Task.Run(() => libraryImporter.Import(importItems));
|
||||||
|
logTime("importIntoDbAsync -- post Import()");
|
||||||
var qtyChanges = context.SaveChanges();
|
var qtyChanges = context.SaveChanges();
|
||||||
|
logTime("importIntoDbAsync -- post SaveChanges");
|
||||||
|
|
||||||
if (qtyChanges > 0)
|
if (qtyChanges > 0)
|
||||||
await Task.Run(() => finalizeLibrarySizeChange());
|
await Task.Run(() => finalizeLibrarySizeChange());
|
||||||
|
logTime("importIntoDbAsync -- post finalizeLibrarySizeChange");
|
||||||
|
|
||||||
return newCount;
|
return newCount;
|
||||||
}
|
}
|
||||||
@ -215,15 +251,14 @@ namespace ApplicationServices
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
// must be here instead of in db layer due to AaxcExists
|
||||||
public static LiberatedStatus Liberated_Status(Book book)
|
public static LiberatedStatus Liberated_Status(Book book)
|
||||||
=> book.Audio_Exists ? LiberatedStatus.Liberated
|
=> book.Audio_Exists ? book.UserDefinedItem.BookStatus
|
||||||
: FileManager.AudibleFileStorage.AaxcExists(book.AudibleProductId) ? LiberatedStatus.PartialDownload
|
: FileManager.AudibleFileStorage.AaxcExists(book.AudibleProductId) ? LiberatedStatus.PartialDownload
|
||||||
: LiberatedStatus.NotLiberated;
|
: LiberatedStatus.NotLiberated;
|
||||||
|
|
||||||
public static LiberatedStatus? Pdf_Status(Book book)
|
// exists here for feature predictability. It makes sense for this to be where Liberated_Status is
|
||||||
=> !book.HasPdf ? null
|
public static LiberatedStatus? Pdf_Status(Book book) => book.UserDefinedItem.PdfStatus;
|
||||||
: book.PDF_Exists ? LiberatedStatus.Liberated
|
|
||||||
: LiberatedStatus.NotLiberated;
|
|
||||||
|
|
||||||
// below are queries, not commands. maybe I should make a LibraryQueries. except there's already one of those...
|
// below are queries, not commands. maybe I should make a LibraryQueries. except there's already one of those...
|
||||||
|
|
||||||
|
|||||||
@ -57,22 +57,10 @@ namespace DataLayer
|
|||||||
public UserDefinedItem UserDefinedItem { get; private set; }
|
public UserDefinedItem UserDefinedItem { get; private set; }
|
||||||
|
|
||||||
// UserDefinedItem convenience properties
|
// UserDefinedItem convenience properties
|
||||||
public bool Audio_Exists
|
/// <summary>True if IsLiberated or Error. False if NotLiberated</summary>
|
||||||
{
|
public bool Audio_Exists => UserDefinedItem.BookStatus != LiberatedStatus.NotLiberated;
|
||||||
get
|
/// <summary>True if exists and IsLiberated. Else false</summary>
|
||||||
{
|
public bool PDF_Exists => UserDefinedItem.PdfStatus == LiberatedStatus.Liberated;
|
||||||
var status = UserDefinedItem?.BookStatus;
|
|
||||||
return status.HasValue && status.Value != LiberatedStatus.NotLiberated;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public bool PDF_Exists
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var status = UserDefinedItem?.PdfStatus;
|
|
||||||
return (status.HasValue && status.Value == LiberatedStatus.Liberated);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// is owned, not optional 1:1
|
// is owned, not optional 1:1
|
||||||
/// <summary>The product's aggregate community rating</summary>
|
/// <summary>The product's aggregate community rating</summary>
|
||||||
|
|||||||
@ -39,6 +39,24 @@ namespace DtoImporterService
|
|||||||
.Except(localProductIds)
|
.Except(localProductIds)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
#region // explanation of DbContext.Books.GetBooks(b => remainingProductIds.Contains(b.AudibleProductId)).ToList();
|
||||||
|
/*
|
||||||
|
articles suggest loading to Local with
|
||||||
|
context.Books.Load();
|
||||||
|
we want Books and associated fields
|
||||||
|
context.Books.GetBooks(b => remainingProductIds.Contains(b.AudibleProductId)).ToList();
|
||||||
|
this is emulating Load() but with also getting associated fields
|
||||||
|
|
||||||
|
from: Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions
|
||||||
|
// Summary:
|
||||||
|
// Enumerates the query. When using Entity Framework, this causes the results of
|
||||||
|
// the query to be loaded into the associated context. This is equivalent to calling
|
||||||
|
// ToList and then throwing away the list (without the overhead of actually creating
|
||||||
|
// the list).
|
||||||
|
public static void Load<TSource>([NotNullAttribute] this IQueryable<TSource> source);
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
// GetBooks() eager loads Series, category, et al
|
// GetBooks() eager loads Series, category, et al
|
||||||
if (remainingProductIds.Any())
|
if (remainingProductIds.Any())
|
||||||
DbContext.Books.GetBooks(b => remainingProductIds.Contains(b.AudibleProductId)).ToList();
|
DbContext.Books.GetBooks(b => remainingProductIds.Contains(b.AudibleProductId)).ToList();
|
||||||
|
|||||||
@ -7,9 +7,9 @@ using InternalUtilities;
|
|||||||
|
|
||||||
namespace DtoImporterService
|
namespace DtoImporterService
|
||||||
{
|
{
|
||||||
public class LibraryImporter : ItemsImporterBase
|
public class LibraryBookImporter : ItemsImporterBase
|
||||||
{
|
{
|
||||||
public LibraryImporter(LibationContext context) : base(context) { }
|
public LibraryBookImporter(LibationContext context) : base(context) { }
|
||||||
|
|
||||||
public override IEnumerable<Exception> Validate(IEnumerable<ImportItem> importItems) => new LibraryValidator().Validate(importItems.Select(i => i.DtoItem));
|
public override IEnumerable<Exception> Validate(IEnumerable<ImportItem> importItems) => new LibraryValidator().Validate(importItems.Select(i => i.DtoItem));
|
||||||
|
|
||||||
@ -13,7 +13,7 @@
|
|||||||
<!-- <PublishSingleFile>true</PublishSingleFile> -->
|
<!-- <PublishSingleFile>true</PublishSingleFile> -->
|
||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
|
|
||||||
<Version>5.6.3.1</Version>
|
<Version>5.6.3.6</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
|||||||
@ -138,7 +138,7 @@ namespace LibationWinForms
|
|||||||
isProcessingGridSelect = prev_isProcessingGridSelect;
|
isProcessingGridSelect = prev_isProcessingGridSelect;
|
||||||
|
|
||||||
// UI init complete. now we can apply filter
|
// UI init complete. now we can apply filter
|
||||||
doFilter(lastGoodFilter);
|
this.UIThread(() => doFilter(lastGoodFilter));
|
||||||
|
|
||||||
setBackupCounts(null, null);
|
setBackupCounts(null, null);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,7 +80,10 @@ namespace LibationWinForms
|
|||||||
{
|
{
|
||||||
var filePath = FileManager.AudibleFileStorage.Audio.GetPath(libraryBook.Book.AudibleProductId);
|
var filePath = FileManager.AudibleFileStorage.Audio.GetPath(libraryBook.Book.AudibleProductId);
|
||||||
if (!Go.To.File(filePath))
|
if (!Go.To.File(filePath))
|
||||||
MessageBox.Show($"File not found:\r\n{filePath}");
|
{
|
||||||
|
var suffix = string.IsNullOrWhiteSpace(filePath) ? "" : $":\r\n{filePath}";
|
||||||
|
MessageBox.Show($"File not found" + suffix);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user