Optimize tag persistence
This commit is contained in:
parent
5c81441f83
commit
0683e5f55b
@ -123,14 +123,8 @@ namespace DataLayer
|
|||||||
ArgumentValidator.EnsureEnumerableNotNullOrEmpty(newContributors, nameof(newContributors));
|
ArgumentValidator.EnsureEnumerableNotNullOrEmpty(newContributors, nameof(newContributors));
|
||||||
|
|
||||||
// the edge cases of doing local-loaded vs remote-only got weird. just load it
|
// the edge cases of doing local-loaded vs remote-only got weird. just load it
|
||||||
if (_contributorsLink == null)
|
if (_contributorsLink is null)
|
||||||
{
|
getEntry(context).Collection(s => s.ContributorsLink).Load();
|
||||||
ArgumentValidator.EnsureNotNull(context, nameof(context));
|
|
||||||
if (!context.Entry(this).IsKeySet)
|
|
||||||
throw new InvalidOperationException("Could not add contributors");
|
|
||||||
|
|
||||||
context.Entry(this).Collection(s => s.ContributorsLink).Load();
|
|
||||||
}
|
|
||||||
|
|
||||||
var roleContributions = getContributions(role);
|
var roleContributions = getContributions(role);
|
||||||
var isIdentical = roleContributions.Select(c => c.Contributor).SequenceEqual(newContributors);
|
var isIdentical = roleContributions.Select(c => c.Contributor).SequenceEqual(newContributors);
|
||||||
@ -140,6 +134,7 @@ namespace DataLayer
|
|||||||
_contributorsLink.RemoveWhere(bc => bc.Role == role);
|
_contributorsLink.RemoveWhere(bc => bc.Role == role);
|
||||||
addNewContributors(newContributors, role);
|
addNewContributors(newContributors, role);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addNewContributors(IEnumerable<Contributor> newContributors, Role role)
|
private void addNewContributors(IEnumerable<Contributor> newContributors, Role role)
|
||||||
{
|
{
|
||||||
byte order = 0;
|
byte order = 0;
|
||||||
@ -155,6 +150,18 @@ namespace DataLayer
|
|||||||
.ToList();
|
.ToList();
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
private Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry<Book> getEntry(DbContext context)
|
||||||
|
{
|
||||||
|
ArgumentValidator.EnsureNotNull(context, nameof(context));
|
||||||
|
|
||||||
|
var entry = context.Entry(this);
|
||||||
|
|
||||||
|
if (!entry.IsKeySet)
|
||||||
|
throw new InvalidOperationException("Could not load a valid Book from database");
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
#region series
|
#region series
|
||||||
private HashSet<SeriesBook> _seriesLink;
|
private HashSet<SeriesBook> _seriesLink;
|
||||||
public IEnumerable<SeriesBook> SeriesLink => _seriesLink?.ToList();
|
public IEnumerable<SeriesBook> SeriesLink => _seriesLink?.ToList();
|
||||||
@ -186,14 +193,8 @@ namespace DataLayer
|
|||||||
|
|
||||||
// our add() is conditional upon what's already included in the collection.
|
// our add() is conditional upon what's already included in the collection.
|
||||||
// therefore if not loaded, a trip is required. might as well just load it
|
// therefore if not loaded, a trip is required. might as well just load it
|
||||||
if (_seriesLink == null)
|
if (_seriesLink is null)
|
||||||
{
|
getEntry(context).Collection(s => s.SeriesLink).Load();
|
||||||
ArgumentValidator.EnsureNotNull(context, nameof(context));
|
|
||||||
if (!context.Entry(this).IsKeySet)
|
|
||||||
throw new InvalidOperationException("Could not add series");
|
|
||||||
|
|
||||||
context.Entry(this).Collection(s => s.SeriesLink).Load();
|
|
||||||
}
|
|
||||||
|
|
||||||
var singleSeriesBook = _seriesLink.SingleOrDefault(sb => sb.Series == series);
|
var singleSeriesBook = _seriesLink.SingleOrDefault(sb => sb.Series == series);
|
||||||
if (singleSeriesBook == null)
|
if (singleSeriesBook == null)
|
||||||
@ -211,8 +212,7 @@ namespace DataLayer
|
|||||||
public void AddSupplementDownloadUrl(string url)
|
public void AddSupplementDownloadUrl(string url)
|
||||||
{
|
{
|
||||||
// supplements are owned by Book, so no need to Load():
|
// supplements are owned by Book, so no need to Load():
|
||||||
// OwnsMany: "Can only ever appear on navigation properties of other entity types.
|
// Are automatically loaded, and can only be tracked by a DbContext alongside their owner.
|
||||||
// Are automatically loaded, and can only be tracked by a DbContext alongside their owner."
|
|
||||||
|
|
||||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(url, nameof(url));
|
ArgumentValidator.EnsureNotNullOrWhiteSpace(url, nameof(url));
|
||||||
|
|
||||||
@ -233,17 +233,10 @@ namespace DataLayer
|
|||||||
|
|
||||||
public void UpdateCategory(Category category, DbContext context = null)
|
public void UpdateCategory(Category category, DbContext context = null)
|
||||||
{
|
{
|
||||||
// since category is never null, nullity means it hasn't been loaded. non null means we're correctly loaded. just overwrite
|
// since category is never null, nullity means it hasn't been loaded
|
||||||
if (Category != null)
|
if (Category is null)
|
||||||
{
|
getEntry(context).Reference(s => s.Category).Load();
|
||||||
Category = category;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context == null)
|
|
||||||
throw new Exception("need context");
|
|
||||||
|
|
||||||
context.Entry(this).Reference(s => s.Category).Load();
|
|
||||||
Category = category;
|
Category = category;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,13 +26,13 @@ namespace DataLayer
|
|||||||
|
|
||||||
private static void persistTags(List<EntityEntry> modifiedEntities)
|
private static void persistTags(List<EntityEntry> modifiedEntities)
|
||||||
{
|
{
|
||||||
var tagSets = modifiedEntities
|
var tagsCollection = modifiedEntities
|
||||||
.Select(e => e.Entity as UserDefinedItem)
|
.Select(e => e.Entity as UserDefinedItem)
|
||||||
// filter by null but NOT by blank. blank is the valid way to show the absence of tags
|
// filter by null but NOT by blank. blank is the valid way to show the absence of tags
|
||||||
.Where(a => a != null)
|
.Where(a => a != null)
|
||||||
|
.Select(t => (t.Book.AudibleProductId, t.Tags))
|
||||||
.ToList();
|
.ToList();
|
||||||
foreach (var t in tagSets)
|
FileManager.TagsPersistence.Save(tagsCollection);
|
||||||
FileManager.TagsPersistence.Save(t.Book.AudibleProductId, t.Tags);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,15 +7,22 @@ nuget
|
|||||||
Microsoft.EntityFrameworkCore.Tools (needed for using Package Manager Console)
|
Microsoft.EntityFrameworkCore.Tools (needed for using Package Manager Console)
|
||||||
Microsoft.EntityFrameworkCore.Sqlite
|
Microsoft.EntityFrameworkCore.Sqlite
|
||||||
|
|
||||||
MIGRATIONS require standard, not core
|
MIGRATIONS
|
||||||
using standard instead of core. edit 3 things in csproj
|
require core, not standard
|
||||||
1of3: pluralize xml TargetFramework tag to TargetFrameworks
|
this can be a problem b/c standard and framework can only reference standard, not core
|
||||||
2of2: TargetFrameworks from: netstandard2.1
|
TO USE MIGRATIONS (core and/or standard)
|
||||||
to: netcoreapp3.0;netstandard2.1
|
add to csproj
|
||||||
3of3: add
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
|
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
TO USE MIGRATIONS AS *BOTH* CORE AND STANDARD
|
||||||
|
edit csproj
|
||||||
|
pluralize this xml tag
|
||||||
|
from: TargetFramework
|
||||||
|
to: TargetFrameworks
|
||||||
|
inside of TargetFrameworks
|
||||||
|
from: netstandard2.1
|
||||||
|
to: netcoreapp3.0;netstandard2.1
|
||||||
|
|
||||||
run. error
|
run. error
|
||||||
SQLite Error 1: 'no such table: Blogs'.
|
SQLite Error 1: 'no such table: Blogs'.
|
||||||
|
|||||||
@ -3,7 +3,10 @@
|
|||||||
"LibationContext_sqlserver": "Server=(LocalDb)\\MSSQLLocalDB;Database=DataLayer.LibationContext;Integrated Security=true;",
|
"LibationContext_sqlserver": "Server=(LocalDb)\\MSSQLLocalDB;Database=DataLayer.LibationContext;Integrated Security=true;",
|
||||||
"LibationContext": "Data Source=LibationContext.db;Foreign Keys=False;",
|
"LibationContext": "Data Source=LibationContext.db;Foreign Keys=False;",
|
||||||
|
|
||||||
"// on windows sqlite paths accept windows and/or unix slashes": "",
|
"// sqlite notes": "",
|
||||||
|
"// absolute path example": "Data Source=C:/foo/bar/sample.db",
|
||||||
|
"// relative path example": "Data Source=sample.db",
|
||||||
|
"// on windows: sqlite paths accept windows and/or unix slashes": "",
|
||||||
"MyTestContext": "Data Source=%DESKTOP%/sample.db"
|
"MyTestContext": "Data Source=%DESKTOP%/sample.db"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -27,10 +27,12 @@ namespace FileManager
|
|||||||
= Policy.Handle<Exception>()
|
= Policy.Handle<Exception>()
|
||||||
.WaitAndRetry(new[] { TimeSpan.FromMilliseconds(100) });
|
.WaitAndRetry(new[] { TimeSpan.FromMilliseconds(100) });
|
||||||
|
|
||||||
public static void Save(string productId, string tags)
|
public static void Save(IEnumerable<(string productId, string tags)> tagsCollection)
|
||||||
{
|
{
|
||||||
ensureCache();
|
ensureCache();
|
||||||
|
|
||||||
|
// on initial reload, there's a huge benefit to adding to cache individually then updating the file only once
|
||||||
|
foreach ((string productId, string tags) in tagsCollection)
|
||||||
cache[productId] = tags;
|
cache[productId] = tags;
|
||||||
|
|
||||||
lock (locker)
|
lock (locker)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user