Initial check-in
This commit is contained in:
parent
c080c9e51d
commit
2cc93078d2
282 changed files with 24387 additions and 0 deletions
38
DataLayer/DataLayer.csproj
Normal file
38
DataLayer/DataLayer.csproj
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp3.0;netstandard2.1</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
|
||||
<ApplicationIcon />
|
||||
<OutputType>Library</OutputType>
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.2.6">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Dinah.Core\Dinah.EntityFrameworkCore\Dinah.EntityFrameworkCore.csproj" />
|
||||
<ProjectReference Include="..\FileManager\FileManager.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
19
DataLayer/UNTESTED/Commands/RemoveOrphans.cs
Normal file
19
DataLayer/UNTESTED/Commands/RemoveOrphans.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
public static class RemoveOrphansCommand
|
||||
{
|
||||
public static int RemoveOrphans(this LibationContext context)
|
||||
=> context.Database.ExecuteSqlCommand(@"
|
||||
delete c
|
||||
from Contributors c
|
||||
left join BookContributor bc on c.ContributorId = bc.ContributorId
|
||||
left join Books b on bc.BookId = b.BookId
|
||||
where bc.ContributorId is null
|
||||
");
|
||||
}
|
||||
}
|
||||
62
DataLayer/UNTESTED/Configurations/BookConfig.cs
Normal file
62
DataLayer/UNTESTED/Configurations/BookConfig.cs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace DataLayer.Configurations
|
||||
{
|
||||
internal class BookConfig : IEntityTypeConfiguration<Book>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Book> entity)
|
||||
{
|
||||
entity.HasKey(b => b.BookId);
|
||||
entity.HasIndex(b => b.AudibleProductId);
|
||||
|
||||
entity.OwnsOne(b => b.Rating);
|
||||
|
||||
//
|
||||
// CRUCIAL: ignore unmapped collections, even get-only
|
||||
//
|
||||
entity.Ignore(nameof(Book.Authors));
|
||||
entity.Ignore(nameof(Book.Narrators));
|
||||
//// these don't seem to matter
|
||||
//entity.Ignore(nameof(Book.AuthorNames));
|
||||
//entity.Ignore(nameof(Book.NarratorNames));
|
||||
//entity.Ignore(nameof(Book.HasPdfs));
|
||||
|
||||
// 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."
|
||||
entity.OwnsMany(b => b.Supplements);
|
||||
// even though it's owned, we need to map its backing field
|
||||
entity
|
||||
.Metadata
|
||||
.FindNavigation(nameof(Book.Supplements))
|
||||
.SetPropertyAccessMode(PropertyAccessMode.Field);
|
||||
|
||||
// owns it 1:1, but store in separate table
|
||||
entity
|
||||
.OwnsOne(b => b.UserDefinedItem, b_udi => b_udi.ToTable(nameof(Book.UserDefinedItem)));
|
||||
// UserDefinedItem must link back to book so we know how to log changed tags.
|
||||
// ie: when a tag changes, we need to get the parent book's product id
|
||||
entity
|
||||
.HasOne(b => b.UserDefinedItem)
|
||||
.WithOne(udi => udi.Book)
|
||||
.HasForeignKey<UserDefinedItem>(udi => udi.BookId);
|
||||
|
||||
entity
|
||||
.Metadata
|
||||
.FindNavigation(nameof(Book.ContributorsLink))
|
||||
// PropertyAccessMode.Field : Contributions is a get-only property, not a field, so use its backing field
|
||||
.SetPropertyAccessMode(PropertyAccessMode.Field);
|
||||
|
||||
entity
|
||||
.Metadata
|
||||
.FindNavigation(nameof(Book.SeriesLink))
|
||||
// PropertyAccessMode.Field : Series is a get-only property, not a field, so use its backing field
|
||||
.SetPropertyAccessMode(PropertyAccessMode.Field);
|
||||
|
||||
entity
|
||||
.HasOne(b => b.Category)
|
||||
.WithMany()
|
||||
.HasForeignKey(b => b.CategoryId);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
DataLayer/UNTESTED/Configurations/BookContributorConfig.cs
Normal file
25
DataLayer/UNTESTED/Configurations/BookContributorConfig.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace DataLayer.Configurations
|
||||
{
|
||||
internal class BookContributorConfig : IEntityTypeConfiguration<BookContributor>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<BookContributor> entity)
|
||||
{
|
||||
entity.HasKey(bc => new { bc.BookId, bc.ContributorId, bc.Role });
|
||||
|
||||
entity.HasIndex(b => b.BookId);
|
||||
entity.HasIndex(b => b.ContributorId);
|
||||
|
||||
entity
|
||||
.HasOne(bc => bc.Book)
|
||||
.WithMany(b => b.ContributorsLink)
|
||||
.HasForeignKey(bc => bc.BookId);
|
||||
entity
|
||||
.HasOne(bc => bc.Contributor)
|
||||
.WithMany(c => c.BooksLink)
|
||||
.HasForeignKey(bc => bc.ContributorId);
|
||||
}
|
||||
}
|
||||
}
|
||||
14
DataLayer/UNTESTED/Configurations/CategoryConfig.cs
Normal file
14
DataLayer/UNTESTED/Configurations/CategoryConfig.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace DataLayer.Configurations
|
||||
{
|
||||
internal class CategoryConfig : IEntityTypeConfiguration<Category>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Category> entity)
|
||||
{
|
||||
entity.HasKey(c => c.CategoryId);
|
||||
entity.HasIndex(c => c.AudibleCategoryId);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
DataLayer/UNTESTED/Configurations/ContributorConfig.cs
Normal file
22
DataLayer/UNTESTED/Configurations/ContributorConfig.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace DataLayer.Configurations
|
||||
{
|
||||
internal class ContributorConfig : IEntityTypeConfiguration<Contributor>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Contributor> entity)
|
||||
{
|
||||
entity.HasKey(c => c.ContributorId);
|
||||
entity.HasIndex(c => c.Name);
|
||||
|
||||
//entity.OwnsOne(b => b.AuthorProperty);
|
||||
// ... in separate table
|
||||
|
||||
entity
|
||||
.Metadata
|
||||
.FindNavigation(nameof(Contributor.BooksLink))
|
||||
.SetPropertyAccessMode(PropertyAccessMode.Field);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
DataLayer/UNTESTED/Configurations/LibraryBookConfig.cs
Normal file
18
DataLayer/UNTESTED/Configurations/LibraryBookConfig.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace DataLayer.Configurations
|
||||
{
|
||||
internal class LibraryBookConfig : IEntityTypeConfiguration<LibraryBook>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<LibraryBook> entity)
|
||||
{
|
||||
entity.HasKey(b => b.BookId);
|
||||
|
||||
entity
|
||||
.HasOne(le => le.Book)
|
||||
.WithOne()
|
||||
.HasForeignKey<LibraryBook>(le => le.BookId);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
DataLayer/UNTESTED/Configurations/SeriesBookConfig.cs
Normal file
25
DataLayer/UNTESTED/Configurations/SeriesBookConfig.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace DataLayer.Configurations
|
||||
{
|
||||
internal class SeriesBookConfig : IEntityTypeConfiguration<SeriesBook>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<SeriesBook> entity)
|
||||
{
|
||||
entity.HasKey(bc => new { bc.SeriesId, bc.BookId });
|
||||
|
||||
entity.HasIndex(b => b.SeriesId);
|
||||
entity.HasIndex(b => b.BookId);
|
||||
|
||||
entity
|
||||
.HasOne(sb => sb.Series)
|
||||
.WithMany(s => s.BooksLink)
|
||||
.HasForeignKey(sb => sb.SeriesId);
|
||||
entity
|
||||
.HasOne(sb => sb.Book)
|
||||
.WithMany(b => b.SeriesLink)
|
||||
.HasForeignKey(sb => sb.BookId);
|
||||
}
|
||||
}
|
||||
}
|
||||
19
DataLayer/UNTESTED/Configurations/SeriesConfig.cs
Normal file
19
DataLayer/UNTESTED/Configurations/SeriesConfig.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace DataLayer.Configurations
|
||||
{
|
||||
internal class SeriesConfig : IEntityTypeConfiguration<Series>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Series> entity)
|
||||
{
|
||||
entity.HasKey(b => b.SeriesId);
|
||||
entity.HasIndex(b => b.AudibleSeriesId);
|
||||
|
||||
entity
|
||||
.Metadata
|
||||
.FindNavigation(nameof(Series.BooksLink))
|
||||
.SetPropertyAccessMode(PropertyAccessMode.Field);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
DataLayer/UNTESTED/Configurations/SupplementConfig.cs
Normal file
17
DataLayer/UNTESTED/Configurations/SupplementConfig.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace DataLayer.Configurations
|
||||
{
|
||||
internal class SupplementConfig : IEntityTypeConfiguration<Supplement>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Supplement> entity)
|
||||
{
|
||||
entity.HasKey(s => s.SupplementId);
|
||||
entity
|
||||
.HasOne(s => s.Book)
|
||||
.WithMany(b => b.Supplements)
|
||||
.HasForeignKey(s => s.BookId);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
DataLayer/UNTESTED/Configurations/UserDefinedItemConfig.cs
Normal file
13
DataLayer/UNTESTED/Configurations/UserDefinedItemConfig.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace DataLayer.Configurations
|
||||
{
|
||||
internal class UserDefinedItemConfig : IEntityTypeConfiguration<UserDefinedItem>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<UserDefinedItem> entity)
|
||||
{
|
||||
entity.OwnsOne(p => p.Rating);
|
||||
}
|
||||
}
|
||||
}
|
||||
256
DataLayer/UNTESTED/EfClasses/Book.cs
Normal file
256
DataLayer/UNTESTED/EfClasses/Book.cs
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dinah.Core;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
public class AudibleProductId
|
||||
{
|
||||
public string Id { get; }
|
||||
public AudibleProductId(string id)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(id, nameof(id));
|
||||
Id = id;
|
||||
}
|
||||
}
|
||||
public class Book
|
||||
{
|
||||
// implementation detail. set by db only. only used by data layer
|
||||
internal int BookId { get; private set; }
|
||||
|
||||
// immutable
|
||||
public string AudibleProductId { get; private set; }
|
||||
public string Title { get; private set; }
|
||||
public string Description { get; private set; }
|
||||
public int LengthInMinutes { get; private set; }
|
||||
|
||||
// mutable
|
||||
public string PictureId { get; set; }
|
||||
|
||||
// book details
|
||||
public bool HasBookDetails { get; private set; }
|
||||
public bool IsAbridged { get; private set; }
|
||||
public DateTime? DatePublished { get; private set; }
|
||||
|
||||
// non-null. use "empty pattern"
|
||||
internal int CategoryId { get; private set; }
|
||||
public Category Category { get; private set; }
|
||||
public string[] CategoriesNames
|
||||
=> Category == null ? new string[0]
|
||||
: Category.ParentCategory == null ? new[] { Category.Name }
|
||||
: new[] { Category.ParentCategory.Name, Category.Name };
|
||||
public string[] CategoriesIds
|
||||
=> Category == null ? null
|
||||
: Category.ParentCategory == null ? new[] { Category.AudibleCategoryId }
|
||||
: new[] { Category.ParentCategory.AudibleCategoryId, Category.AudibleCategoryId };
|
||||
|
||||
// is owned, not optional 1:1
|
||||
public UserDefinedItem UserDefinedItem { get; private set; }
|
||||
|
||||
// is owned, not optional 1:1
|
||||
/// <summary>The product's aggregate community rating</summary>
|
||||
public Rating Rating { get; private set; } = new Rating(0, 0, 0);
|
||||
|
||||
// ef-ctor
|
||||
private Book() { }
|
||||
// non-ef ctor
|
||||
/// <param name="audibleProductId">special id class b/c it's too easy to get string order mixed up</param>
|
||||
public Book(
|
||||
AudibleProductId audibleProductId,
|
||||
string title,
|
||||
string description,
|
||||
int lengthInMinutes,
|
||||
IEnumerable<Contributor> authors,
|
||||
IEnumerable<Contributor> narrators)
|
||||
{
|
||||
// validate
|
||||
ArgumentValidator.EnsureNotNull(audibleProductId, nameof(audibleProductId));
|
||||
var productId = audibleProductId.Id;
|
||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(productId, nameof(productId));
|
||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(title, nameof(title));
|
||||
|
||||
// non-ef-ctor init.s
|
||||
UserDefinedItem = new UserDefinedItem(this);
|
||||
_contributorsLink = new HashSet<BookContributor>();
|
||||
_seriesLink = new HashSet<SeriesBook>();
|
||||
_supplements = new HashSet<Supplement>();
|
||||
|
||||
// since category/id is never null, nullity means it hasn't been loaded
|
||||
CategoryId = Category.GetEmpty().CategoryId;
|
||||
|
||||
// simple assigns
|
||||
AudibleProductId = productId;
|
||||
Title = title;
|
||||
Description = description;
|
||||
LengthInMinutes = lengthInMinutes;
|
||||
|
||||
// assigns with biz logic
|
||||
ReplaceAuthors(authors);
|
||||
ReplaceNarrators(narrators);
|
||||
|
||||
// import previously saved tags
|
||||
// do this immediately. any save occurs before reloading tags will overwrite persistent tags with new blank entries; all old persisted tags will be lost
|
||||
// if refactoring, DO NOT use "ProductId" before it's assigned to. to be safe, just use "productId"
|
||||
UserDefinedItem = new UserDefinedItem(this) { Tags = FileManager.TagsPersistence.GetTags(productId) };
|
||||
}
|
||||
|
||||
#region contributors, authors, narrators
|
||||
// use uninitialised backing fields - this means we can detect if the collection was loaded
|
||||
private HashSet<BookContributor> _contributorsLink;
|
||||
// i'd like this to be internal but migration throws this exception when i try:
|
||||
// Value cannot be null.
|
||||
// Parameter name: property
|
||||
public IEnumerable<BookContributor> ContributorsLink
|
||||
=> _contributorsLink?
|
||||
.OrderBy(bc => bc.Order)
|
||||
.ToList();
|
||||
|
||||
public IEnumerable<Contributor> Authors => getContributions(Role.Author).Select(bc => bc.Contributor).ToList();
|
||||
public string AuthorNames => string.Join(", ", Authors.Select(a => a.Name));
|
||||
|
||||
public IEnumerable<Contributor> Narrators => getContributions(Role.Narrator).Select(bc => bc.Contributor).ToList();
|
||||
public string NarratorNames => string.Join(", ", Narrators.Select(n => n.Name));
|
||||
|
||||
public string Publisher => getContributions(Role.Publisher).SingleOrDefault()?.Contributor.Name;
|
||||
|
||||
public void ReplaceAuthors(IEnumerable<Contributor> authors, DbContext context = null)
|
||||
=> replaceContributors(authors, Role.Author, context);
|
||||
public void ReplaceNarrators(IEnumerable<Contributor> narrators, DbContext context = null)
|
||||
=> replaceContributors(narrators, Role.Narrator, context);
|
||||
public void ReplacePublisher(Contributor publisher, DbContext context = null)
|
||||
=> replaceContributors(new List<Contributor> { publisher }, Role.Publisher, context);
|
||||
private void replaceContributors(IEnumerable<Contributor> newContributors, Role role, DbContext context = null)
|
||||
{
|
||||
ArgumentValidator.EnsureEnumerableNotNullOrEmpty(newContributors, nameof(newContributors));
|
||||
|
||||
// the edge cases of doing local-loaded vs remote-only got weird. just load it
|
||||
if (_contributorsLink == null)
|
||||
{
|
||||
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 isIdentical = roleContributions.Select(c => c.Contributor).SequenceEqual(newContributors);
|
||||
if (isIdentical)
|
||||
return;
|
||||
|
||||
_contributorsLink.RemoveWhere(bc => bc.Role == role);
|
||||
addNewContributors(newContributors, role);
|
||||
}
|
||||
private void addNewContributors(IEnumerable<Contributor> newContributors, Role role)
|
||||
{
|
||||
byte order = 0;
|
||||
var newContributionsEnum = newContributors.Select(c => new BookContributor(this, c, role, order++));
|
||||
var newContributions = new HashSet<BookContributor>(newContributionsEnum);
|
||||
_contributorsLink.UnionWith(newContributions);
|
||||
}
|
||||
|
||||
private List<BookContributor> getContributions(Role role)
|
||||
=> ContributorsLink
|
||||
.Where(a => a.Role == role)
|
||||
.OrderBy(a => a.Order)
|
||||
.ToList();
|
||||
#endregion
|
||||
|
||||
#region series
|
||||
private HashSet<SeriesBook> _seriesLink;
|
||||
public IEnumerable<SeriesBook> SeriesLink => _seriesLink?.ToList();
|
||||
public string SeriesNames
|
||||
{
|
||||
get
|
||||
{
|
||||
// first: alphabetical by name
|
||||
var withNames = _seriesLink
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s.Series.Name))
|
||||
.Select(s => s.Series.Name)
|
||||
.OrderBy(a => a)
|
||||
.ToList();
|
||||
// then un-named are alpha by series id
|
||||
var nullNames = _seriesLink
|
||||
.Where(s => string.IsNullOrWhiteSpace(s.Series.Name))
|
||||
.Select(s => s.Series.AudibleSeriesId)
|
||||
.OrderBy(a => a)
|
||||
.ToList();
|
||||
|
||||
var all = withNames.Union(nullNames).ToList();
|
||||
return string.Join(", ", all);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpsertSeries(Series series, float? index = null, DbContext context = null)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNull(series, nameof(series));
|
||||
|
||||
// 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
|
||||
if (_seriesLink == null)
|
||||
{
|
||||
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);
|
||||
if (singleSeriesBook == null)
|
||||
_seriesLink.Add(new SeriesBook(series, this, index));
|
||||
else
|
||||
singleSeriesBook.UpdateIndex(index);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region supplements
|
||||
private HashSet<Supplement> _supplements;
|
||||
public IEnumerable<Supplement> Supplements => _supplements?.ToList();
|
||||
public bool HasPdfs => Supplements.Any();
|
||||
|
||||
public void AddSupplementDownloadUrl(string url)
|
||||
{
|
||||
// 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."
|
||||
|
||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(url, nameof(url));
|
||||
url = FileManager.FileUtility.RestoreDeclawed(url);
|
||||
|
||||
if (!_supplements.Any(s => url.EqualsInsensitive(url)))
|
||||
_supplements.Add(new Supplement(this, url));
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void UpdateProductRating(float overallRating, float performanceRating, float storyRating)
|
||||
=> Rating.Update(overallRating, performanceRating, storyRating);
|
||||
|
||||
public void UpdateBookDetails(bool isAbridged, DateTime? datePublished)
|
||||
{
|
||||
// don't overwrite with default values
|
||||
IsAbridged |= isAbridged;
|
||||
DatePublished = datePublished ?? DatePublished;
|
||||
|
||||
HasBookDetails = true;
|
||||
}
|
||||
|
||||
public void UpdateCategory(Category category, DbContext context = null)
|
||||
{
|
||||
// since category is never null, nullity means it hasn't been loaded
|
||||
if (Category != null || CategoryId == Category.GetEmpty().CategoryId)
|
||||
{
|
||||
Category = category;
|
||||
return;
|
||||
}
|
||||
|
||||
if (context == null)
|
||||
throw new Exception("need context");
|
||||
|
||||
context.Entry(this).Reference(s => s.Category).Load();
|
||||
Category = category;
|
||||
}
|
||||
}
|
||||
}
|
||||
27
DataLayer/UNTESTED/EfClasses/BookContributor.cs
Normal file
27
DataLayer/UNTESTED/EfClasses/BookContributor.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
using Dinah.Core;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
public class BookContributor
|
||||
{
|
||||
internal int BookId { get; private set; }
|
||||
internal int ContributorId { get; private set; }
|
||||
public Role Role { get; private set; }
|
||||
public byte Order { get; private set; }
|
||||
|
||||
public Book Book { get; private set; }
|
||||
public Contributor Contributor { get; private set; }
|
||||
|
||||
private BookContributor() { }
|
||||
internal BookContributor(Book book, Contributor contributor, Role role, byte order)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNull(book, nameof(book));
|
||||
ArgumentValidator.EnsureNotNull(contributor, nameof(contributor));
|
||||
|
||||
Book = book;
|
||||
Contributor = contributor;
|
||||
Role = role;
|
||||
Order = order;
|
||||
}
|
||||
}
|
||||
}
|
||||
52
DataLayer/UNTESTED/EfClasses/Category.cs
Normal file
52
DataLayer/UNTESTED/EfClasses/Category.cs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dinah.Core;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
public class AudibleCategoryId
|
||||
{
|
||||
public string Id { get; }
|
||||
public AudibleCategoryId(string id)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(id, nameof(id));
|
||||
Id = id;
|
||||
}
|
||||
}
|
||||
public class Category
|
||||
{
|
||||
// Empty is a special case. use private ctor w/o validation
|
||||
public static Category GetEmpty() => new Category { CategoryId = -1, AudibleCategoryId = "", Name = "", ParentCategory = null };
|
||||
public bool IsEmpty() => string.IsNullOrWhiteSpace(AudibleCategoryId) || string.IsNullOrWhiteSpace(Name) || ParentCategory == null;
|
||||
|
||||
internal int CategoryId { get; private set; }
|
||||
public string AudibleCategoryId { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
public Category ParentCategory { get; private set; }
|
||||
|
||||
private Category() { }
|
||||
/// <summary>special id class b/c it's too easy to get string order mixed up</summary>
|
||||
public Category(AudibleCategoryId audibleSeriesId, string name, Category parentCategory = null)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNull(audibleSeriesId, nameof(audibleSeriesId));
|
||||
var id = audibleSeriesId.Id;
|
||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(id, nameof(id));
|
||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(name, nameof(name));
|
||||
|
||||
AudibleCategoryId = id;
|
||||
Name = name;
|
||||
|
||||
UpdateParentCategory(parentCategory);
|
||||
}
|
||||
|
||||
public void UpdateParentCategory(Category parentCategory)
|
||||
{
|
||||
// don't overwrite with null but not an error
|
||||
if (parentCategory != null)
|
||||
ParentCategory = parentCategory;
|
||||
}
|
||||
}
|
||||
}
|
||||
82
DataLayer/UNTESTED/EfClasses/Contributor.cs
Normal file
82
DataLayer/UNTESTED/EfClasses/Contributor.cs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dinah.Core;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
public class Contributor
|
||||
{
|
||||
// contributors search links are just name with url-encoding. space can be + or %20
|
||||
// author search link: /search?searchAuthor=Robert+Bevan
|
||||
// narrator search link: /search?searchNarrator=Robert+Bevan
|
||||
// can also search multiples. concat with comma before url encode
|
||||
|
||||
// id.s
|
||||
// ----
|
||||
// https://www.audible.com/author/Neil-Gaiman/B000AQ01G2 == https://www.audible.com/author/B000AQ01G2
|
||||
// goes to summary page
|
||||
// at bottom "See all titles by Neil Gaiman" goes to https://www.audible.com/search?searchAuthor=Neil+Gaiman
|
||||
// some authors have no id. simply goes to https://www.audible.com/search?searchAuthor=Rufus+Fears
|
||||
// all narrators have no id: https://www.audible.com/search?searchNarrator=Neil+Gaiman
|
||||
|
||||
internal int ContributorId { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
|
||||
private HashSet<BookContributor> _booksLink;
|
||||
public IEnumerable<BookContributor> BooksLink => _booksLink?.ToList();
|
||||
|
||||
private Contributor() { }
|
||||
public Contributor(string name)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(name, nameof(name));
|
||||
|
||||
_booksLink = new HashSet<BookContributor>();
|
||||
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public string AudibleAuthorId { get; private set; }
|
||||
public void UpdateAudibleAuthorId(string authorId)
|
||||
{
|
||||
// don't overwrite with null or whitespace but not an error
|
||||
if (!string.IsNullOrWhiteSpace(authorId))
|
||||
AudibleAuthorId = authorId;
|
||||
}
|
||||
|
||||
#region // AudibleAuthorId refactor: separate author-specific info. overkill for a single optional string
|
||||
///// <summary>Most authors in Audible have a unique id</summary>
|
||||
//public AudibleAuthorProperty AudibleAuthorProperty { get; private set; }
|
||||
//public void UpdateAuthorId(string authorId, LibationContext context = null)
|
||||
//{
|
||||
// if (authorId == null)
|
||||
// return;
|
||||
// if (AudibleAuthorProperty != null)
|
||||
// {
|
||||
// AudibleAuthorProperty.UpdateAudibleAuthorId(authorId);
|
||||
// return;
|
||||
// }
|
||||
// if (context == null)
|
||||
// throw new ArgumentNullException(nameof(context), "You must provide a context");
|
||||
// if (context.Contributors.Find(ContributorId) == null)
|
||||
// throw new InvalidOperationException("Could not update audible author id.");
|
||||
// var audibleAuthorProperty = new AudibleAuthorProperty();
|
||||
// audibleAuthorProperty.UpdateAudibleAuthorId(authorId);
|
||||
// context.AuthorProperties.Add(audibleAuthorProperty);
|
||||
//}
|
||||
//public class AudibleAuthorProperty
|
||||
//{
|
||||
// public int ContributorId { get; private set; }
|
||||
// public Contributor Contributor { get; set; }
|
||||
|
||||
// public string AudibleAuthorId { get; private set; }
|
||||
|
||||
// public void UpdateAudibleAuthorId(string authorId)
|
||||
// {
|
||||
// if (!string.IsNullOrWhiteSpace(authorId))
|
||||
// AudibleAuthorId = authorId;
|
||||
// }
|
||||
//}
|
||||
//// ...and create EF table config
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
25
DataLayer/UNTESTED/EfClasses/LibraryBook.cs
Normal file
25
DataLayer/UNTESTED/EfClasses/LibraryBook.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using Dinah.Core;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
public class LibraryBook
|
||||
{
|
||||
internal int BookId { get; private set; }
|
||||
public Book Book { get; private set; }
|
||||
|
||||
public DateTime DateAdded { get; private set; }
|
||||
|
||||
/// <summary>For downloading AAX file</summary>
|
||||
public string DownloadBookLink { get; private set; }
|
||||
|
||||
private LibraryBook() { }
|
||||
public LibraryBook(Book book, DateTime dateAdded, string downloadBookLink)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNull(book, nameof(book));
|
||||
Book = book;
|
||||
DateAdded = dateAdded;
|
||||
DownloadBookLink = downloadBookLink;
|
||||
}
|
||||
}
|
||||
}
|
||||
76
DataLayer/UNTESTED/EfClasses/Rating.cs
Normal file
76
DataLayer/UNTESTED/EfClasses/Rating.cs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Dinah.Core;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
/// <summary>Parameterless ctor and setters should be used by EF only. Everything else should treat it as immutable</summary>
|
||||
public class Rating : ValueObject_Static<Rating>
|
||||
{
|
||||
public float OverallRating { get; private set; }
|
||||
public float PerformanceRating { get; private set; }
|
||||
public float StoryRating { get; private set; }
|
||||
|
||||
private Rating() { }
|
||||
internal Rating(float overallRating, float performanceRating, float storyRating)
|
||||
{
|
||||
OverallRating = overallRating;
|
||||
PerformanceRating = performanceRating;
|
||||
StoryRating = storyRating;
|
||||
}
|
||||
|
||||
// EF magically tracks this owned object. by replacing it with a new() immutable object, stuff gets weird. update instead
|
||||
internal void Update(float overallRating, float performanceRating, float storyRating)
|
||||
{
|
||||
// don't overwrite with all 0
|
||||
if (overallRating + performanceRating + storyRating == 0)
|
||||
return;
|
||||
|
||||
OverallRating = overallRating;
|
||||
PerformanceRating = performanceRating;
|
||||
StoryRating = storyRating;
|
||||
}
|
||||
|
||||
protected override IEnumerable<object> GetEqualityComponents()
|
||||
{
|
||||
yield return OverallRating;
|
||||
yield return PerformanceRating;
|
||||
yield return StoryRating;
|
||||
}
|
||||
|
||||
public float FirstScore
|
||||
=> OverallRating > 0 ? OverallRating
|
||||
: PerformanceRating > 0 ? PerformanceRating
|
||||
: StoryRating;
|
||||
|
||||
/// <summary>character: ★</summary>
|
||||
const char STAR = '\u2605';
|
||||
/// <summary>character: ½</summary>
|
||||
const char HALF = '\u00BD';
|
||||
string getStars(float score)
|
||||
{
|
||||
var fullStars = (int)Math.Floor(score);
|
||||
|
||||
var starString = "".PadLeft(fullStars, STAR);
|
||||
|
||||
if (score - fullStars == 0.5f)
|
||||
starString += HALF;
|
||||
|
||||
return starString;
|
||||
}
|
||||
|
||||
public string ToStarString()
|
||||
{
|
||||
var items = new List<string>();
|
||||
|
||||
if (OverallRating > 0)
|
||||
items.Add($"Overall: {getStars(OverallRating)}");
|
||||
if (PerformanceRating > 0)
|
||||
items.Add($"Perform: {getStars(PerformanceRating)}");
|
||||
if (StoryRating > 0)
|
||||
items.Add($"Story: {getStars(StoryRating)}");
|
||||
|
||||
return string.Join("\r\n", items);
|
||||
}
|
||||
}
|
||||
}
|
||||
4
DataLayer/UNTESTED/EfClasses/Role.cs
Normal file
4
DataLayer/UNTESTED/EfClasses/Role.cs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
namespace DataLayer
|
||||
{
|
||||
public enum Role { Author = 1, Narrator = 2, Publisher = 3 }
|
||||
}
|
||||
70
DataLayer/UNTESTED/EfClasses/Series.cs
Normal file
70
DataLayer/UNTESTED/EfClasses/Series.cs
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dinah.Core;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
public class AudibleSeriesId
|
||||
{
|
||||
public string Id { get; }
|
||||
public AudibleSeriesId(string id)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(id, nameof(id));
|
||||
Id = id;
|
||||
}
|
||||
}
|
||||
public class Series
|
||||
{
|
||||
internal int SeriesId { get; private set; }
|
||||
public string AudibleSeriesId { get; private set; }
|
||||
|
||||
/// <summary>optional</summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
private HashSet<SeriesBook> _booksLink;
|
||||
public IEnumerable<SeriesBook> BooksLink
|
||||
=> _booksLink?
|
||||
.OrderBy(sb => sb.Index)
|
||||
.ToList();
|
||||
|
||||
private Series() { }
|
||||
/// <summary>special id class b/c it's too easy to get string order mixed up</summary>
|
||||
public Series(AudibleSeriesId audibleSeriesId, string name = null)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNull(audibleSeriesId, nameof(audibleSeriesId));
|
||||
var id = audibleSeriesId.Id;
|
||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(id, nameof(id));
|
||||
AudibleSeriesId = id;
|
||||
_booksLink = new HashSet<SeriesBook>();
|
||||
UpdateName(name);
|
||||
}
|
||||
|
||||
public void UpdateName(string name)
|
||||
{
|
||||
// don't overwrite with null or whitespace but not an error
|
||||
if (!string.IsNullOrWhiteSpace(name))
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public void AddBook(Book book, float? index = null, DbContext context = null)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNull(book, nameof(book));
|
||||
|
||||
// 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
|
||||
if (_booksLink == null)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNull(context, nameof(context));
|
||||
if (!context.Entry(this).IsKeySet)
|
||||
throw new InvalidOperationException("Could not add series");
|
||||
|
||||
context.Entry(this).Collection(s => s.BooksLink).Load();
|
||||
}
|
||||
|
||||
if (_booksLink.SingleOrDefault(sb => sb.Book == book) == null)
|
||||
_booksLink.Add(new SeriesBook(this, book, index));
|
||||
}
|
||||
}
|
||||
}
|
||||
38
DataLayer/UNTESTED/EfClasses/SeriesBook.cs
Normal file
38
DataLayer/UNTESTED/EfClasses/SeriesBook.cs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
using Dinah.Core;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
public class SeriesBook
|
||||
{
|
||||
internal int SeriesId { get; private set; }
|
||||
internal int BookId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>"index" not "order". This is both for sequence and display</para>
|
||||
/// <para>Float allows for in-between books. eg: 2.5</para>
|
||||
/// <para>To show 2 editions as the same book in a series, give them the same index</para>
|
||||
/// <para>null IS NOT the same as 0. Some series call a book "book 0"</para>
|
||||
/// </summary>
|
||||
public float? Index { get; private set; }
|
||||
|
||||
public Series Series { get; private set; }
|
||||
public Book Book { get; private set; }
|
||||
|
||||
private SeriesBook() { }
|
||||
internal SeriesBook(Series series, Book book, float? index = null)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNull(series, nameof(series));
|
||||
ArgumentValidator.EnsureNotNull(book, nameof(book));
|
||||
|
||||
Series = series;
|
||||
Book = book;
|
||||
Index = index;
|
||||
}
|
||||
|
||||
public void UpdateIndex(float? index)
|
||||
{
|
||||
if (index.HasValue)
|
||||
Index = index.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
24
DataLayer/UNTESTED/EfClasses/Supplement.cs
Normal file
24
DataLayer/UNTESTED/EfClasses/Supplement.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
using Dinah.Core;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
/// <summary>PDF/ZIP files only. Although book download info could be the same format, they're substantially different and subject to change</summary>
|
||||
public class Supplement
|
||||
{
|
||||
internal int SupplementId { get; private set; }
|
||||
internal int BookId { get; private set; }
|
||||
|
||||
public Book Book { get; private set; }
|
||||
public string Url { get; private set; }
|
||||
|
||||
private Supplement() { }
|
||||
public Supplement(Book book, string url)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNull(book, nameof(book));
|
||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(url, nameof(url));
|
||||
|
||||
Book = book;
|
||||
Url = url;
|
||||
}
|
||||
}
|
||||
}
|
||||
77
DataLayer/UNTESTED/EfClasses/UserDefinedItem.cs
Normal file
77
DataLayer/UNTESTED/EfClasses/UserDefinedItem.cs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Dinah.Core;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
public class UserDefinedItem
|
||||
{
|
||||
internal int BookId { get; private set; }
|
||||
public Book Book { get; private set; }
|
||||
|
||||
private UserDefinedItem() { }
|
||||
internal UserDefinedItem(Book book)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNull(book, nameof(book));
|
||||
Book = book;
|
||||
}
|
||||
|
||||
private string _tags = "";
|
||||
public string Tags
|
||||
{
|
||||
get => _tags;
|
||||
set => _tags = sanitize(value);
|
||||
}
|
||||
#region sanitize tags: space delimited. Inline/denormalized. Lower case. Alpha numeric and hyphen
|
||||
// only legal chars are letters numbers underscores and separating whitespace
|
||||
//
|
||||
// technically, the only char.s which aren't easily supported are \ [ ]
|
||||
// however, whitelisting is far safer than blacklisting (eg: new lines, non-printable character)
|
||||
// it's easy to expand whitelist as needed
|
||||
// for lucene, ToLower() isn't needed because search is case-inspecific. for here, it prevents duplicates
|
||||
//
|
||||
// there are also other allowed but misleading characters. eg: the ^ operator defines a 'boost' score
|
||||
// full list of characters which must be escaped:
|
||||
// + - && || ! ( ) { } [ ] ^ " ~ * ? : \
|
||||
static Regex regex = new Regex(@"[^\w\d\s_]", RegexOptions.Compiled);
|
||||
private static string sanitize(string input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
return "";
|
||||
|
||||
var str = input
|
||||
.Trim()
|
||||
.ToLowerInvariant()
|
||||
// assume a hyphen is supposed to be an underscore
|
||||
.Replace("-", "_");
|
||||
|
||||
var unique = regex
|
||||
// turn illegal characters into a space. this will also take care of turning new lines into spaces
|
||||
.Replace(str, " ")
|
||||
// split and remove excess spaces
|
||||
.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
// de-dup
|
||||
.Distinct()
|
||||
// this will prevent order from being relevant
|
||||
.OrderBy(a => a);
|
||||
|
||||
// currently, the string is the canonical set. if we later make the collection into the canonical set:
|
||||
// var tags = new Hashset<string>(list); // de-dup, order doesn't matter but can seem random due to hashing algo
|
||||
// var isEqual = tagsNew.SetEquals(tagsOld);
|
||||
|
||||
return string.Join(" ", unique);
|
||||
}
|
||||
|
||||
public IEnumerable<string> TagsEnumerated => Tags == "" ? new string[0] : Tags.Split(null as char[], StringSplitOptions.RemoveEmptyEntries);
|
||||
#endregion
|
||||
|
||||
// owned: not an optional one-to-one
|
||||
/// <summary>The user's individual book rating</summary>
|
||||
public Rating Rating { get; private set; } = new Rating(0, 0, 0);
|
||||
|
||||
public void UpdateRating(float overallRating, float performanceRating, float storyRating)
|
||||
=> Rating.Update(overallRating, performanceRating, storyRating);
|
||||
}
|
||||
}
|
||||
69
DataLayer/UNTESTED/LibationContext.cs
Normal file
69
DataLayer/UNTESTED/LibationContext.cs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
using DataLayer.Configurations;
|
||||
using Dinah.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
public class LibationContext : InterceptableDbContext
|
||||
{
|
||||
// IMPORTANT: USING DbSet<>
|
||||
// ========================
|
||||
// these run against the db. linq queries against these MUST be translatable to sql. primatives only. no POCOs or this error occurs:
|
||||
// Unable to create a constant value of type 'DataLayer.Contributor'. Only primitive types or enumeration types are supported in this context.
|
||||
// to use full object-linq, load and use local
|
||||
// load full table:
|
||||
// List<Contributor> contributors = ...;
|
||||
// Contributors.Load();
|
||||
// Contributors.Local.Where(a => contributors.Contains(a));
|
||||
// load only those in object:
|
||||
// // overwrite collection
|
||||
// Entry(product).Collection(x => x.Narrators).Load();
|
||||
// product.Narrators = narrators;
|
||||
public DbSet<LibraryBook> Library { get; private set; }
|
||||
public DbSet<Book> Books { get; private set; }
|
||||
public DbSet<Contributor> Contributors { get; private set; }
|
||||
public DbSet<Series> Series { get; private set; }
|
||||
public DbSet<Category> Categories { get; private set; }
|
||||
|
||||
public static LibationContext Create()
|
||||
{
|
||||
var factory = new LibationContextFactory();
|
||||
var context = factory.Create();
|
||||
return context;
|
||||
}
|
||||
|
||||
// see DesignTimeDbContextFactoryBase for info about ctors and connection strings/OnConfiguring()
|
||||
internal LibationContext(DbContextOptions options) : base(options) { }
|
||||
|
||||
// called on each instantiation
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
AddInterceptor(new TagPersistenceInterceptor());
|
||||
|
||||
base.OnConfiguring(optionsBuilder);
|
||||
}
|
||||
|
||||
// typically only called once per execution; NOT once per instantiation
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
modelBuilder.ApplyConfiguration(new BookConfig());
|
||||
modelBuilder.ApplyConfiguration(new ContributorConfig());
|
||||
modelBuilder.ApplyConfiguration(new BookContributorConfig());
|
||||
modelBuilder.ApplyConfiguration(new SupplementConfig());
|
||||
modelBuilder.ApplyConfiguration(new UserDefinedItemConfig());
|
||||
modelBuilder.ApplyConfiguration(new LibraryBookConfig());
|
||||
modelBuilder.ApplyConfiguration(new SeriesConfig());
|
||||
modelBuilder.ApplyConfiguration(new SeriesBookConfig());
|
||||
modelBuilder.ApplyConfiguration(new CategoryConfig());
|
||||
|
||||
// seeds go here. examples in scratch pad
|
||||
modelBuilder
|
||||
.Entity<Category>()
|
||||
.HasData(Category.GetEmpty());
|
||||
|
||||
// views are now supported via "query types" (instead of "entity types"): https://docs.microsoft.com/en-us/ef/core/modeling/query-types
|
||||
}
|
||||
}
|
||||
}
|
||||
11
DataLayer/UNTESTED/LibationContextFactory.cs
Normal file
11
DataLayer/UNTESTED/LibationContextFactory.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
using Dinah.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
public class LibationContextFactory : DesignTimeDbContextFactoryBase<LibationContext>
|
||||
{
|
||||
protected override LibationContext CreateNewInstance(DbContextOptions<LibationContext> options) => new LibationContext(options);
|
||||
protected override void UseDatabaseEngine(DbContextOptionsBuilder optionsBuilder, string connectionString) => optionsBuilder.UseSqlServer(connectionString);
|
||||
}
|
||||
}
|
||||
294
DataLayer/UNTESTED/Migrations/20190114190811_Initial.Designer.cs
generated
Normal file
294
DataLayer/UNTESTED/Migrations/20190114190811_Initial.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using DataLayer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace DataLayer.Migrations
|
||||
{
|
||||
[DbContext(typeof(LibationContext))]
|
||||
[Migration("20190114190811_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.2.0-rtm-35687")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("DataLayer.Book", b =>
|
||||
{
|
||||
b.Property<int>("BookId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleProductId");
|
||||
|
||||
b.Property<int>("CategoryId");
|
||||
|
||||
b.Property<DateTime?>("DatePublished");
|
||||
|
||||
b.Property<string>("Description");
|
||||
|
||||
b.Property<bool>("HasBookDetails");
|
||||
|
||||
b.Property<bool>("IsAbridged");
|
||||
|
||||
b.Property<int>("LengthInMinutes");
|
||||
|
||||
b.Property<string>("PictureId");
|
||||
|
||||
b.Property<string>("Publisher");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.HasKey("BookId");
|
||||
|
||||
b.HasIndex("AudibleProductId");
|
||||
|
||||
b.HasIndex("CategoryId");
|
||||
|
||||
b.ToTable("Books");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||
{
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<int>("ContributorId");
|
||||
|
||||
b.Property<int>("Role");
|
||||
|
||||
b.Property<byte>("Order");
|
||||
|
||||
b.HasKey("BookId", "ContributorId", "Role");
|
||||
|
||||
b.HasIndex("BookId");
|
||||
|
||||
b.HasIndex("ContributorId");
|
||||
|
||||
b.ToTable("BookContributor");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Category", b =>
|
||||
{
|
||||
b.Property<int>("CategoryId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleCategoryId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<int?>("ParentCategoryCategoryId");
|
||||
|
||||
b.HasKey("CategoryId");
|
||||
|
||||
b.HasIndex("AudibleCategoryId");
|
||||
|
||||
b.HasIndex("ParentCategoryCategoryId");
|
||||
|
||||
b.ToTable("Categories");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Contributor", b =>
|
||||
{
|
||||
b.Property<int>("ContributorId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleAuthorId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.HasKey("ContributorId");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("Contributors");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||
{
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<DateTime>("DateAdded");
|
||||
|
||||
b.Property<string>("DownloadBookLink");
|
||||
|
||||
b.HasKey("BookId");
|
||||
|
||||
b.ToTable("Library");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Series", b =>
|
||||
{
|
||||
b.Property<int>("SeriesId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleSeriesId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.HasKey("SeriesId");
|
||||
|
||||
b.HasIndex("AudibleSeriesId");
|
||||
|
||||
b.ToTable("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||
{
|
||||
b.Property<int>("SeriesId");
|
||||
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<float?>("Index");
|
||||
|
||||
b.HasKey("SeriesId", "BookId");
|
||||
|
||||
b.HasIndex("BookId");
|
||||
|
||||
b.HasIndex("SeriesId");
|
||||
|
||||
b.ToTable("SeriesBook");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Book", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Category", "Category")
|
||||
.WithMany()
|
||||
.HasForeignKey("CategoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.OwnsMany("DataLayer.Supplement", "Supplements", b1 =>
|
||||
{
|
||||
b1.Property<int>("SupplementId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b1.Property<int>("BookId");
|
||||
|
||||
b1.Property<string>("Url");
|
||||
|
||||
b1.HasKey("SupplementId");
|
||||
|
||||
b1.HasIndex("BookId");
|
||||
|
||||
b1.ToTable("Supplement");
|
||||
|
||||
b1.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("Supplements")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
b.OwnsOne("DataLayer.UserDefinedItem", "UserDefinedItem", b1 =>
|
||||
{
|
||||
b1.Property<int>("BookId");
|
||||
|
||||
b1.Property<string>("Tags");
|
||||
|
||||
b1.HasKey("BookId");
|
||||
|
||||
b1.ToTable("UserDefinedItem");
|
||||
|
||||
b1.HasOne("DataLayer.Book", "Book")
|
||||
.WithOne("UserDefinedItem")
|
||||
.HasForeignKey("DataLayer.UserDefinedItem", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b1.OwnsOne("DataLayer.Rating", "Rating", b2 =>
|
||||
{
|
||||
b2.Property<int>("UserDefinedItemBookId");
|
||||
|
||||
b2.Property<float>("OverallRating");
|
||||
|
||||
b2.Property<float>("PerformanceRating");
|
||||
|
||||
b2.Property<float>("StoryRating");
|
||||
|
||||
b2.HasKey("UserDefinedItemBookId");
|
||||
|
||||
b2.ToTable("UserDefinedItem");
|
||||
|
||||
b2.HasOne("DataLayer.UserDefinedItem")
|
||||
.WithOne("Rating")
|
||||
.HasForeignKey("DataLayer.Rating", "UserDefinedItemBookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
});
|
||||
|
||||
b.OwnsOne("DataLayer.Rating", "Rating", b1 =>
|
||||
{
|
||||
b1.Property<int>("BookId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b1.Property<float>("OverallRating");
|
||||
|
||||
b1.Property<float>("PerformanceRating");
|
||||
|
||||
b1.Property<float>("StoryRating");
|
||||
|
||||
b1.HasKey("BookId");
|
||||
|
||||
b1.ToTable("Books");
|
||||
|
||||
b1.HasOne("DataLayer.Book")
|
||||
.WithOne("Rating")
|
||||
.HasForeignKey("DataLayer.Rating", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("ContributorsLink")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("DataLayer.Contributor", "Contributor")
|
||||
.WithMany("BooksLink")
|
||||
.HasForeignKey("ContributorId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Category", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Category", "ParentCategory")
|
||||
.WithMany()
|
||||
.HasForeignKey("ParentCategoryCategoryId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithOne()
|
||||
.HasForeignKey("DataLayer.LibraryBook", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("SeriesLink")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("DataLayer.Series", "Series")
|
||||
.WithMany("BooksLink")
|
||||
.HasForeignKey("SeriesId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
288
DataLayer/UNTESTED/Migrations/20190114190811_Initial.cs
Normal file
288
DataLayer/UNTESTED/Migrations/20190114190811_Initial.cs
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace DataLayer.Migrations
|
||||
{
|
||||
public partial class Initial : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Categories",
|
||||
columns: table => new
|
||||
{
|
||||
CategoryId = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
AudibleCategoryId = table.Column<string>(nullable: true),
|
||||
Name = table.Column<string>(nullable: true),
|
||||
ParentCategoryCategoryId = table.Column<int>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Categories", x => x.CategoryId);
|
||||
table.ForeignKey(
|
||||
name: "FK_Categories_Categories_ParentCategoryCategoryId",
|
||||
column: x => x.ParentCategoryCategoryId,
|
||||
principalTable: "Categories",
|
||||
principalColumn: "CategoryId",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Contributors",
|
||||
columns: table => new
|
||||
{
|
||||
ContributorId = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
Name = table.Column<string>(nullable: true),
|
||||
AudibleAuthorId = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Contributors", x => x.ContributorId);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Series",
|
||||
columns: table => new
|
||||
{
|
||||
SeriesId = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
AudibleSeriesId = table.Column<string>(nullable: true),
|
||||
Name = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Series", x => x.SeriesId);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Books",
|
||||
columns: table => new
|
||||
{
|
||||
BookId = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
AudibleProductId = table.Column<string>(nullable: true),
|
||||
Title = table.Column<string>(nullable: true),
|
||||
Description = table.Column<string>(nullable: true),
|
||||
LengthInMinutes = table.Column<int>(nullable: false),
|
||||
PictureId = table.Column<string>(nullable: true),
|
||||
HasBookDetails = table.Column<bool>(nullable: false),
|
||||
IsAbridged = table.Column<bool>(nullable: false),
|
||||
Publisher = table.Column<string>(nullable: true),
|
||||
DatePublished = table.Column<DateTime>(nullable: true),
|
||||
CategoryId = table.Column<int>(nullable: false),
|
||||
Rating_OverallRating = table.Column<float>(nullable: false),
|
||||
Rating_PerformanceRating = table.Column<float>(nullable: false),
|
||||
Rating_StoryRating = table.Column<float>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Books", x => x.BookId);
|
||||
table.ForeignKey(
|
||||
name: "FK_Books_Categories_CategoryId",
|
||||
column: x => x.CategoryId,
|
||||
principalTable: "Categories",
|
||||
principalColumn: "CategoryId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "BookContributor",
|
||||
columns: table => new
|
||||
{
|
||||
BookId = table.Column<int>(nullable: false),
|
||||
ContributorId = table.Column<int>(nullable: false),
|
||||
Role = table.Column<int>(nullable: false),
|
||||
Order = table.Column<byte>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_BookContributor", x => new { x.BookId, x.ContributorId, x.Role });
|
||||
table.ForeignKey(
|
||||
name: "FK_BookContributor_Books_BookId",
|
||||
column: x => x.BookId,
|
||||
principalTable: "Books",
|
||||
principalColumn: "BookId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_BookContributor_Contributors_ContributorId",
|
||||
column: x => x.ContributorId,
|
||||
principalTable: "Contributors",
|
||||
principalColumn: "ContributorId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Library",
|
||||
columns: table => new
|
||||
{
|
||||
BookId = table.Column<int>(nullable: false),
|
||||
DateAdded = table.Column<DateTime>(nullable: false),
|
||||
DownloadBookLink = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Library", x => x.BookId);
|
||||
table.ForeignKey(
|
||||
name: "FK_Library_Books_BookId",
|
||||
column: x => x.BookId,
|
||||
principalTable: "Books",
|
||||
principalColumn: "BookId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "SeriesBook",
|
||||
columns: table => new
|
||||
{
|
||||
SeriesId = table.Column<int>(nullable: false),
|
||||
BookId = table.Column<int>(nullable: false),
|
||||
Index = table.Column<float>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_SeriesBook", x => new { x.SeriesId, x.BookId });
|
||||
table.ForeignKey(
|
||||
name: "FK_SeriesBook_Books_BookId",
|
||||
column: x => x.BookId,
|
||||
principalTable: "Books",
|
||||
principalColumn: "BookId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_SeriesBook_Series_SeriesId",
|
||||
column: x => x.SeriesId,
|
||||
principalTable: "Series",
|
||||
principalColumn: "SeriesId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Supplement",
|
||||
columns: table => new
|
||||
{
|
||||
SupplementId = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||
BookId = table.Column<int>(nullable: false),
|
||||
Url = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Supplement", x => x.SupplementId);
|
||||
table.ForeignKey(
|
||||
name: "FK_Supplement_Books_BookId",
|
||||
column: x => x.BookId,
|
||||
principalTable: "Books",
|
||||
principalColumn: "BookId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "UserDefinedItem",
|
||||
columns: table => new
|
||||
{
|
||||
BookId = table.Column<int>(nullable: false),
|
||||
Tags = table.Column<string>(nullable: true),
|
||||
Rating_OverallRating = table.Column<float>(nullable: false),
|
||||
Rating_PerformanceRating = table.Column<float>(nullable: false),
|
||||
Rating_StoryRating = table.Column<float>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_UserDefinedItem", x => x.BookId);
|
||||
table.ForeignKey(
|
||||
name: "FK_UserDefinedItem_Books_BookId",
|
||||
column: x => x.BookId,
|
||||
principalTable: "Books",
|
||||
principalColumn: "BookId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BookContributor_BookId",
|
||||
table: "BookContributor",
|
||||
column: "BookId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BookContributor_ContributorId",
|
||||
table: "BookContributor",
|
||||
column: "ContributorId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Books_AudibleProductId",
|
||||
table: "Books",
|
||||
column: "AudibleProductId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Books_CategoryId",
|
||||
table: "Books",
|
||||
column: "CategoryId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Categories_AudibleCategoryId",
|
||||
table: "Categories",
|
||||
column: "AudibleCategoryId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Categories_ParentCategoryCategoryId",
|
||||
table: "Categories",
|
||||
column: "ParentCategoryCategoryId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Contributors_Name",
|
||||
table: "Contributors",
|
||||
column: "Name");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Series_AudibleSeriesId",
|
||||
table: "Series",
|
||||
column: "AudibleSeriesId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_SeriesBook_BookId",
|
||||
table: "SeriesBook",
|
||||
column: "BookId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_SeriesBook_SeriesId",
|
||||
table: "SeriesBook",
|
||||
column: "SeriesId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Supplement_BookId",
|
||||
table: "Supplement",
|
||||
column: "BookId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "BookContributor");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Library");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "SeriesBook");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Supplement");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "UserDefinedItem");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Contributors");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Series");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Books");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Categories");
|
||||
}
|
||||
}
|
||||
}
|
||||
293
DataLayer/UNTESTED/Migrations/20190114191724_NullableCategory.Designer.cs
generated
Normal file
293
DataLayer/UNTESTED/Migrations/20190114191724_NullableCategory.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using DataLayer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace DataLayer.Migrations
|
||||
{
|
||||
[DbContext(typeof(LibationContext))]
|
||||
[Migration("20190114191724_NullableCategory")]
|
||||
partial class NullableCategory
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.2.0-rtm-35687")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("DataLayer.Book", b =>
|
||||
{
|
||||
b.Property<int>("BookId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleProductId");
|
||||
|
||||
b.Property<int?>("CategoryId");
|
||||
|
||||
b.Property<DateTime?>("DatePublished");
|
||||
|
||||
b.Property<string>("Description");
|
||||
|
||||
b.Property<bool>("HasBookDetails");
|
||||
|
||||
b.Property<bool>("IsAbridged");
|
||||
|
||||
b.Property<int>("LengthInMinutes");
|
||||
|
||||
b.Property<string>("PictureId");
|
||||
|
||||
b.Property<string>("Publisher");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.HasKey("BookId");
|
||||
|
||||
b.HasIndex("AudibleProductId");
|
||||
|
||||
b.HasIndex("CategoryId");
|
||||
|
||||
b.ToTable("Books");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||
{
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<int>("ContributorId");
|
||||
|
||||
b.Property<int>("Role");
|
||||
|
||||
b.Property<byte>("Order");
|
||||
|
||||
b.HasKey("BookId", "ContributorId", "Role");
|
||||
|
||||
b.HasIndex("BookId");
|
||||
|
||||
b.HasIndex("ContributorId");
|
||||
|
||||
b.ToTable("BookContributor");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Category", b =>
|
||||
{
|
||||
b.Property<int>("CategoryId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleCategoryId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<int?>("ParentCategoryCategoryId");
|
||||
|
||||
b.HasKey("CategoryId");
|
||||
|
||||
b.HasIndex("AudibleCategoryId");
|
||||
|
||||
b.HasIndex("ParentCategoryCategoryId");
|
||||
|
||||
b.ToTable("Categories");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Contributor", b =>
|
||||
{
|
||||
b.Property<int>("ContributorId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleAuthorId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.HasKey("ContributorId");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("Contributors");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||
{
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<DateTime>("DateAdded");
|
||||
|
||||
b.Property<string>("DownloadBookLink");
|
||||
|
||||
b.HasKey("BookId");
|
||||
|
||||
b.ToTable("Library");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Series", b =>
|
||||
{
|
||||
b.Property<int>("SeriesId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleSeriesId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.HasKey("SeriesId");
|
||||
|
||||
b.HasIndex("AudibleSeriesId");
|
||||
|
||||
b.ToTable("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||
{
|
||||
b.Property<int>("SeriesId");
|
||||
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<float?>("Index");
|
||||
|
||||
b.HasKey("SeriesId", "BookId");
|
||||
|
||||
b.HasIndex("BookId");
|
||||
|
||||
b.HasIndex("SeriesId");
|
||||
|
||||
b.ToTable("SeriesBook");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Book", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Category", "Category")
|
||||
.WithMany()
|
||||
.HasForeignKey("CategoryId");
|
||||
|
||||
b.OwnsMany("DataLayer.Supplement", "Supplements", b1 =>
|
||||
{
|
||||
b1.Property<int>("SupplementId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b1.Property<int>("BookId");
|
||||
|
||||
b1.Property<string>("Url");
|
||||
|
||||
b1.HasKey("SupplementId");
|
||||
|
||||
b1.HasIndex("BookId");
|
||||
|
||||
b1.ToTable("Supplement");
|
||||
|
||||
b1.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("Supplements")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
b.OwnsOne("DataLayer.UserDefinedItem", "UserDefinedItem", b1 =>
|
||||
{
|
||||
b1.Property<int>("BookId");
|
||||
|
||||
b1.Property<string>("Tags");
|
||||
|
||||
b1.HasKey("BookId");
|
||||
|
||||
b1.ToTable("UserDefinedItem");
|
||||
|
||||
b1.HasOne("DataLayer.Book", "Book")
|
||||
.WithOne("UserDefinedItem")
|
||||
.HasForeignKey("DataLayer.UserDefinedItem", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b1.OwnsOne("DataLayer.Rating", "Rating", b2 =>
|
||||
{
|
||||
b2.Property<int>("UserDefinedItemBookId");
|
||||
|
||||
b2.Property<float>("OverallRating");
|
||||
|
||||
b2.Property<float>("PerformanceRating");
|
||||
|
||||
b2.Property<float>("StoryRating");
|
||||
|
||||
b2.HasKey("UserDefinedItemBookId");
|
||||
|
||||
b2.ToTable("UserDefinedItem");
|
||||
|
||||
b2.HasOne("DataLayer.UserDefinedItem")
|
||||
.WithOne("Rating")
|
||||
.HasForeignKey("DataLayer.Rating", "UserDefinedItemBookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
});
|
||||
|
||||
b.OwnsOne("DataLayer.Rating", "Rating", b1 =>
|
||||
{
|
||||
b1.Property<int>("BookId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b1.Property<float>("OverallRating");
|
||||
|
||||
b1.Property<float>("PerformanceRating");
|
||||
|
||||
b1.Property<float>("StoryRating");
|
||||
|
||||
b1.HasKey("BookId");
|
||||
|
||||
b1.ToTable("Books");
|
||||
|
||||
b1.HasOne("DataLayer.Book")
|
||||
.WithOne("Rating")
|
||||
.HasForeignKey("DataLayer.Rating", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("ContributorsLink")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("DataLayer.Contributor", "Contributor")
|
||||
.WithMany("BooksLink")
|
||||
.HasForeignKey("ContributorId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Category", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Category", "ParentCategory")
|
||||
.WithMany()
|
||||
.HasForeignKey("ParentCategoryCategoryId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithOne()
|
||||
.HasForeignKey("DataLayer.LibraryBook", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("SeriesLink")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("DataLayer.Series", "Series")
|
||||
.WithMany("BooksLink")
|
||||
.HasForeignKey("SeriesId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace DataLayer.Migrations
|
||||
{
|
||||
public partial class NullableCategory : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Books_Categories_CategoryId",
|
||||
table: "Books");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "CategoryId",
|
||||
table: "Books",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int));
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Books_Categories_CategoryId",
|
||||
table: "Books",
|
||||
column: "CategoryId",
|
||||
principalTable: "Categories",
|
||||
principalColumn: "CategoryId",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Books_Categories_CategoryId",
|
||||
table: "Books");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "CategoryId",
|
||||
table: "Books",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Books_Categories_CategoryId",
|
||||
table: "Books",
|
||||
column: "CategoryId",
|
||||
principalTable: "Categories",
|
||||
principalColumn: "CategoryId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
||||
293
DataLayer/UNTESTED/Migrations/20190124190012_NullCategory.Designer.cs
generated
Normal file
293
DataLayer/UNTESTED/Migrations/20190124190012_NullCategory.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using DataLayer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace DataLayer.Migrations
|
||||
{
|
||||
[DbContext(typeof(LibationContext))]
|
||||
[Migration("20190124190012_NullCategory")]
|
||||
partial class NullCategory
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.2.0-rtm-35687")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("DataLayer.Book", b =>
|
||||
{
|
||||
b.Property<int>("BookId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleProductId");
|
||||
|
||||
b.Property<int?>("CategoryId");
|
||||
|
||||
b.Property<DateTime?>("DatePublished");
|
||||
|
||||
b.Property<string>("Description");
|
||||
|
||||
b.Property<bool>("HasBookDetails");
|
||||
|
||||
b.Property<bool>("IsAbridged");
|
||||
|
||||
b.Property<int>("LengthInMinutes");
|
||||
|
||||
b.Property<string>("PictureId");
|
||||
|
||||
b.Property<string>("Publisher");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.HasKey("BookId");
|
||||
|
||||
b.HasIndex("AudibleProductId");
|
||||
|
||||
b.HasIndex("CategoryId");
|
||||
|
||||
b.ToTable("Books");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||
{
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<int>("ContributorId");
|
||||
|
||||
b.Property<int>("Role");
|
||||
|
||||
b.Property<byte>("Order");
|
||||
|
||||
b.HasKey("BookId", "ContributorId", "Role");
|
||||
|
||||
b.HasIndex("BookId");
|
||||
|
||||
b.HasIndex("ContributorId");
|
||||
|
||||
b.ToTable("BookContributor");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Category", b =>
|
||||
{
|
||||
b.Property<int>("CategoryId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleCategoryId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<int?>("ParentCategoryCategoryId");
|
||||
|
||||
b.HasKey("CategoryId");
|
||||
|
||||
b.HasIndex("AudibleCategoryId");
|
||||
|
||||
b.HasIndex("ParentCategoryCategoryId");
|
||||
|
||||
b.ToTable("Categories");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Contributor", b =>
|
||||
{
|
||||
b.Property<int>("ContributorId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleAuthorId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.HasKey("ContributorId");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("Contributors");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||
{
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<DateTime>("DateAdded");
|
||||
|
||||
b.Property<string>("DownloadBookLink");
|
||||
|
||||
b.HasKey("BookId");
|
||||
|
||||
b.ToTable("Library");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Series", b =>
|
||||
{
|
||||
b.Property<int>("SeriesId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleSeriesId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.HasKey("SeriesId");
|
||||
|
||||
b.HasIndex("AudibleSeriesId");
|
||||
|
||||
b.ToTable("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||
{
|
||||
b.Property<int>("SeriesId");
|
||||
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<float?>("Index");
|
||||
|
||||
b.HasKey("SeriesId", "BookId");
|
||||
|
||||
b.HasIndex("BookId");
|
||||
|
||||
b.HasIndex("SeriesId");
|
||||
|
||||
b.ToTable("SeriesBook");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Book", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Category", "Category")
|
||||
.WithMany()
|
||||
.HasForeignKey("CategoryId");
|
||||
|
||||
b.OwnsMany("DataLayer.Supplement", "Supplements", b1 =>
|
||||
{
|
||||
b1.Property<int>("SupplementId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b1.Property<int>("BookId");
|
||||
|
||||
b1.Property<string>("Url");
|
||||
|
||||
b1.HasKey("SupplementId");
|
||||
|
||||
b1.HasIndex("BookId");
|
||||
|
||||
b1.ToTable("Supplement");
|
||||
|
||||
b1.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("Supplements")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
b.OwnsOne("DataLayer.UserDefinedItem", "UserDefinedItem", b1 =>
|
||||
{
|
||||
b1.Property<int>("BookId");
|
||||
|
||||
b1.Property<string>("Tags");
|
||||
|
||||
b1.HasKey("BookId");
|
||||
|
||||
b1.ToTable("UserDefinedItem");
|
||||
|
||||
b1.HasOne("DataLayer.Book", "Book")
|
||||
.WithOne("UserDefinedItem")
|
||||
.HasForeignKey("DataLayer.UserDefinedItem", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b1.OwnsOne("DataLayer.Rating", "Rating", b2 =>
|
||||
{
|
||||
b2.Property<int>("UserDefinedItemBookId");
|
||||
|
||||
b2.Property<float>("OverallRating");
|
||||
|
||||
b2.Property<float>("PerformanceRating");
|
||||
|
||||
b2.Property<float>("StoryRating");
|
||||
|
||||
b2.HasKey("UserDefinedItemBookId");
|
||||
|
||||
b2.ToTable("UserDefinedItem");
|
||||
|
||||
b2.HasOne("DataLayer.UserDefinedItem")
|
||||
.WithOne("Rating")
|
||||
.HasForeignKey("DataLayer.Rating", "UserDefinedItemBookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
});
|
||||
|
||||
b.OwnsOne("DataLayer.Rating", "Rating", b1 =>
|
||||
{
|
||||
b1.Property<int>("BookId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b1.Property<float>("OverallRating");
|
||||
|
||||
b1.Property<float>("PerformanceRating");
|
||||
|
||||
b1.Property<float>("StoryRating");
|
||||
|
||||
b1.HasKey("BookId");
|
||||
|
||||
b1.ToTable("Books");
|
||||
|
||||
b1.HasOne("DataLayer.Book")
|
||||
.WithOne("Rating")
|
||||
.HasForeignKey("DataLayer.Rating", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("ContributorsLink")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("DataLayer.Contributor", "Contributor")
|
||||
.WithMany("BooksLink")
|
||||
.HasForeignKey("ContributorId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Category", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Category", "ParentCategory")
|
||||
.WithMany()
|
||||
.HasForeignKey("ParentCategoryCategoryId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithOne()
|
||||
.HasForeignKey("DataLayer.LibraryBook", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("SeriesLink")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("DataLayer.Series", "Series")
|
||||
.WithMany("BooksLink")
|
||||
.HasForeignKey("SeriesId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
17
DataLayer/UNTESTED/Migrations/20190124190012_NullCategory.cs
Normal file
17
DataLayer/UNTESTED/Migrations/20190124190012_NullCategory.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace DataLayer.Migrations
|
||||
{
|
||||
public partial class NullCategory : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
294
DataLayer/UNTESTED/Migrations/20190124190057_NonNullCategory.Designer.cs
generated
Normal file
294
DataLayer/UNTESTED/Migrations/20190124190057_NonNullCategory.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using DataLayer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace DataLayer.Migrations
|
||||
{
|
||||
[DbContext(typeof(LibationContext))]
|
||||
[Migration("20190124190057_NonNullCategory")]
|
||||
partial class NonNullCategory
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.2.0-rtm-35687")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("DataLayer.Book", b =>
|
||||
{
|
||||
b.Property<int>("BookId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleProductId");
|
||||
|
||||
b.Property<int>("CategoryId");
|
||||
|
||||
b.Property<DateTime?>("DatePublished");
|
||||
|
||||
b.Property<string>("Description");
|
||||
|
||||
b.Property<bool>("HasBookDetails");
|
||||
|
||||
b.Property<bool>("IsAbridged");
|
||||
|
||||
b.Property<int>("LengthInMinutes");
|
||||
|
||||
b.Property<string>("PictureId");
|
||||
|
||||
b.Property<string>("Publisher");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.HasKey("BookId");
|
||||
|
||||
b.HasIndex("AudibleProductId");
|
||||
|
||||
b.HasIndex("CategoryId");
|
||||
|
||||
b.ToTable("Books");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||
{
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<int>("ContributorId");
|
||||
|
||||
b.Property<int>("Role");
|
||||
|
||||
b.Property<byte>("Order");
|
||||
|
||||
b.HasKey("BookId", "ContributorId", "Role");
|
||||
|
||||
b.HasIndex("BookId");
|
||||
|
||||
b.HasIndex("ContributorId");
|
||||
|
||||
b.ToTable("BookContributor");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Category", b =>
|
||||
{
|
||||
b.Property<int>("CategoryId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleCategoryId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<int?>("ParentCategoryCategoryId");
|
||||
|
||||
b.HasKey("CategoryId");
|
||||
|
||||
b.HasIndex("AudibleCategoryId");
|
||||
|
||||
b.HasIndex("ParentCategoryCategoryId");
|
||||
|
||||
b.ToTable("Categories");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Contributor", b =>
|
||||
{
|
||||
b.Property<int>("ContributorId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleAuthorId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.HasKey("ContributorId");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("Contributors");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||
{
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<DateTime>("DateAdded");
|
||||
|
||||
b.Property<string>("DownloadBookLink");
|
||||
|
||||
b.HasKey("BookId");
|
||||
|
||||
b.ToTable("Library");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Series", b =>
|
||||
{
|
||||
b.Property<int>("SeriesId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleSeriesId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.HasKey("SeriesId");
|
||||
|
||||
b.HasIndex("AudibleSeriesId");
|
||||
|
||||
b.ToTable("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||
{
|
||||
b.Property<int>("SeriesId");
|
||||
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<float?>("Index");
|
||||
|
||||
b.HasKey("SeriesId", "BookId");
|
||||
|
||||
b.HasIndex("BookId");
|
||||
|
||||
b.HasIndex("SeriesId");
|
||||
|
||||
b.ToTable("SeriesBook");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Book", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Category", "Category")
|
||||
.WithMany()
|
||||
.HasForeignKey("CategoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.OwnsMany("DataLayer.Supplement", "Supplements", b1 =>
|
||||
{
|
||||
b1.Property<int>("SupplementId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b1.Property<int>("BookId");
|
||||
|
||||
b1.Property<string>("Url");
|
||||
|
||||
b1.HasKey("SupplementId");
|
||||
|
||||
b1.HasIndex("BookId");
|
||||
|
||||
b1.ToTable("Supplement");
|
||||
|
||||
b1.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("Supplements")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
b.OwnsOne("DataLayer.UserDefinedItem", "UserDefinedItem", b1 =>
|
||||
{
|
||||
b1.Property<int>("BookId");
|
||||
|
||||
b1.Property<string>("Tags");
|
||||
|
||||
b1.HasKey("BookId");
|
||||
|
||||
b1.ToTable("UserDefinedItem");
|
||||
|
||||
b1.HasOne("DataLayer.Book", "Book")
|
||||
.WithOne("UserDefinedItem")
|
||||
.HasForeignKey("DataLayer.UserDefinedItem", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b1.OwnsOne("DataLayer.Rating", "Rating", b2 =>
|
||||
{
|
||||
b2.Property<int>("UserDefinedItemBookId");
|
||||
|
||||
b2.Property<float>("OverallRating");
|
||||
|
||||
b2.Property<float>("PerformanceRating");
|
||||
|
||||
b2.Property<float>("StoryRating");
|
||||
|
||||
b2.HasKey("UserDefinedItemBookId");
|
||||
|
||||
b2.ToTable("UserDefinedItem");
|
||||
|
||||
b2.HasOne("DataLayer.UserDefinedItem")
|
||||
.WithOne("Rating")
|
||||
.HasForeignKey("DataLayer.Rating", "UserDefinedItemBookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
});
|
||||
|
||||
b.OwnsOne("DataLayer.Rating", "Rating", b1 =>
|
||||
{
|
||||
b1.Property<int>("BookId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b1.Property<float>("OverallRating");
|
||||
|
||||
b1.Property<float>("PerformanceRating");
|
||||
|
||||
b1.Property<float>("StoryRating");
|
||||
|
||||
b1.HasKey("BookId");
|
||||
|
||||
b1.ToTable("Books");
|
||||
|
||||
b1.HasOne("DataLayer.Book")
|
||||
.WithOne("Rating")
|
||||
.HasForeignKey("DataLayer.Rating", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("ContributorsLink")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("DataLayer.Contributor", "Contributor")
|
||||
.WithMany("BooksLink")
|
||||
.HasForeignKey("ContributorId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Category", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Category", "ParentCategory")
|
||||
.WithMany()
|
||||
.HasForeignKey("ParentCategoryCategoryId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithOne()
|
||||
.HasForeignKey("DataLayer.LibraryBook", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("SeriesLink")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("DataLayer.Series", "Series")
|
||||
.WithMany("BooksLink")
|
||||
.HasForeignKey("SeriesId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace DataLayer.Migrations
|
||||
{
|
||||
public partial class NonNullCategory : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Books_Categories_CategoryId",
|
||||
table: "Books");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "CategoryId",
|
||||
table: "Books",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Books_Categories_CategoryId",
|
||||
table: "Books",
|
||||
column: "CategoryId",
|
||||
principalTable: "Categories",
|
||||
principalColumn: "CategoryId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Books_Categories_CategoryId",
|
||||
table: "Books");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "CategoryId",
|
||||
table: "Books",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int));
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Books_Categories_CategoryId",
|
||||
table: "Books",
|
||||
column: "CategoryId",
|
||||
principalTable: "Categories",
|
||||
principalColumn: "CategoryId",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
}
|
||||
}
|
||||
}
|
||||
302
DataLayer/UNTESTED/Migrations/20190124192324_EmptyCategorySeed.Designer.cs
generated
Normal file
302
DataLayer/UNTESTED/Migrations/20190124192324_EmptyCategorySeed.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using DataLayer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace DataLayer.Migrations
|
||||
{
|
||||
[DbContext(typeof(LibationContext))]
|
||||
[Migration("20190124192324_EmptyCategorySeed")]
|
||||
partial class EmptyCategorySeed
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.2.0-rtm-35687")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("DataLayer.Book", b =>
|
||||
{
|
||||
b.Property<int>("BookId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleProductId");
|
||||
|
||||
b.Property<int>("CategoryId");
|
||||
|
||||
b.Property<DateTime?>("DatePublished");
|
||||
|
||||
b.Property<string>("Description");
|
||||
|
||||
b.Property<bool>("HasBookDetails");
|
||||
|
||||
b.Property<bool>("IsAbridged");
|
||||
|
||||
b.Property<int>("LengthInMinutes");
|
||||
|
||||
b.Property<string>("PictureId");
|
||||
|
||||
b.Property<string>("Publisher");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.HasKey("BookId");
|
||||
|
||||
b.HasIndex("AudibleProductId");
|
||||
|
||||
b.HasIndex("CategoryId");
|
||||
|
||||
b.ToTable("Books");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||
{
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<int>("ContributorId");
|
||||
|
||||
b.Property<int>("Role");
|
||||
|
||||
b.Property<byte>("Order");
|
||||
|
||||
b.HasKey("BookId", "ContributorId", "Role");
|
||||
|
||||
b.HasIndex("BookId");
|
||||
|
||||
b.HasIndex("ContributorId");
|
||||
|
||||
b.ToTable("BookContributor");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Category", b =>
|
||||
{
|
||||
b.Property<int>("CategoryId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleCategoryId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<int?>("ParentCategoryCategoryId");
|
||||
|
||||
b.HasKey("CategoryId");
|
||||
|
||||
b.HasIndex("AudibleCategoryId");
|
||||
|
||||
b.HasIndex("ParentCategoryCategoryId");
|
||||
|
||||
b.ToTable("Categories");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
CategoryId = -1,
|
||||
AudibleCategoryId = "",
|
||||
Name = ""
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Contributor", b =>
|
||||
{
|
||||
b.Property<int>("ContributorId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleAuthorId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.HasKey("ContributorId");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("Contributors");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||
{
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<DateTime>("DateAdded");
|
||||
|
||||
b.Property<string>("DownloadBookLink");
|
||||
|
||||
b.HasKey("BookId");
|
||||
|
||||
b.ToTable("Library");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Series", b =>
|
||||
{
|
||||
b.Property<int>("SeriesId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleSeriesId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.HasKey("SeriesId");
|
||||
|
||||
b.HasIndex("AudibleSeriesId");
|
||||
|
||||
b.ToTable("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||
{
|
||||
b.Property<int>("SeriesId");
|
||||
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<float?>("Index");
|
||||
|
||||
b.HasKey("SeriesId", "BookId");
|
||||
|
||||
b.HasIndex("BookId");
|
||||
|
||||
b.HasIndex("SeriesId");
|
||||
|
||||
b.ToTable("SeriesBook");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Book", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Category", "Category")
|
||||
.WithMany()
|
||||
.HasForeignKey("CategoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.OwnsMany("DataLayer.Supplement", "Supplements", b1 =>
|
||||
{
|
||||
b1.Property<int>("SupplementId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b1.Property<int>("BookId");
|
||||
|
||||
b1.Property<string>("Url");
|
||||
|
||||
b1.HasKey("SupplementId");
|
||||
|
||||
b1.HasIndex("BookId");
|
||||
|
||||
b1.ToTable("Supplement");
|
||||
|
||||
b1.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("Supplements")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
b.OwnsOne("DataLayer.UserDefinedItem", "UserDefinedItem", b1 =>
|
||||
{
|
||||
b1.Property<int>("BookId");
|
||||
|
||||
b1.Property<string>("Tags");
|
||||
|
||||
b1.HasKey("BookId");
|
||||
|
||||
b1.ToTable("UserDefinedItem");
|
||||
|
||||
b1.HasOne("DataLayer.Book", "Book")
|
||||
.WithOne("UserDefinedItem")
|
||||
.HasForeignKey("DataLayer.UserDefinedItem", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b1.OwnsOne("DataLayer.Rating", "Rating", b2 =>
|
||||
{
|
||||
b2.Property<int>("UserDefinedItemBookId");
|
||||
|
||||
b2.Property<float>("OverallRating");
|
||||
|
||||
b2.Property<float>("PerformanceRating");
|
||||
|
||||
b2.Property<float>("StoryRating");
|
||||
|
||||
b2.HasKey("UserDefinedItemBookId");
|
||||
|
||||
b2.ToTable("UserDefinedItem");
|
||||
|
||||
b2.HasOne("DataLayer.UserDefinedItem")
|
||||
.WithOne("Rating")
|
||||
.HasForeignKey("DataLayer.Rating", "UserDefinedItemBookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
});
|
||||
|
||||
b.OwnsOne("DataLayer.Rating", "Rating", b1 =>
|
||||
{
|
||||
b1.Property<int>("BookId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b1.Property<float>("OverallRating");
|
||||
|
||||
b1.Property<float>("PerformanceRating");
|
||||
|
||||
b1.Property<float>("StoryRating");
|
||||
|
||||
b1.HasKey("BookId");
|
||||
|
||||
b1.ToTable("Books");
|
||||
|
||||
b1.HasOne("DataLayer.Book")
|
||||
.WithOne("Rating")
|
||||
.HasForeignKey("DataLayer.Rating", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("ContributorsLink")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("DataLayer.Contributor", "Contributor")
|
||||
.WithMany("BooksLink")
|
||||
.HasForeignKey("ContributorId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Category", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Category", "ParentCategory")
|
||||
.WithMany()
|
||||
.HasForeignKey("ParentCategoryCategoryId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithOne()
|
||||
.HasForeignKey("DataLayer.LibraryBook", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("SeriesLink")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("DataLayer.Series", "Series")
|
||||
.WithMany("BooksLink")
|
||||
.HasForeignKey("SeriesId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace DataLayer.Migrations
|
||||
{
|
||||
public partial class EmptyCategorySeed : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.InsertData(
|
||||
table: "Categories",
|
||||
columns: new[] { "CategoryId", "AudibleCategoryId", "Name", "ParentCategoryCategoryId" },
|
||||
values: new object[] { -1, "", "", null });
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DeleteData(
|
||||
table: "Categories",
|
||||
keyColumn: "CategoryId",
|
||||
keyValue: -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
300
DataLayer/UNTESTED/Migrations/20190124214317_PublisherContrib.Designer.cs
generated
Normal file
300
DataLayer/UNTESTED/Migrations/20190124214317_PublisherContrib.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using DataLayer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace DataLayer.Migrations
|
||||
{
|
||||
[DbContext(typeof(LibationContext))]
|
||||
[Migration("20190124214317_PublisherContrib")]
|
||||
partial class PublisherContrib
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.2.0-rtm-35687")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("DataLayer.Book", b =>
|
||||
{
|
||||
b.Property<int>("BookId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleProductId");
|
||||
|
||||
b.Property<int>("CategoryId");
|
||||
|
||||
b.Property<DateTime?>("DatePublished");
|
||||
|
||||
b.Property<string>("Description");
|
||||
|
||||
b.Property<bool>("HasBookDetails");
|
||||
|
||||
b.Property<bool>("IsAbridged");
|
||||
|
||||
b.Property<int>("LengthInMinutes");
|
||||
|
||||
b.Property<string>("PictureId");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.HasKey("BookId");
|
||||
|
||||
b.HasIndex("AudibleProductId");
|
||||
|
||||
b.HasIndex("CategoryId");
|
||||
|
||||
b.ToTable("Books");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||
{
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<int>("ContributorId");
|
||||
|
||||
b.Property<int>("Role");
|
||||
|
||||
b.Property<byte>("Order");
|
||||
|
||||
b.HasKey("BookId", "ContributorId", "Role");
|
||||
|
||||
b.HasIndex("BookId");
|
||||
|
||||
b.HasIndex("ContributorId");
|
||||
|
||||
b.ToTable("BookContributor");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Category", b =>
|
||||
{
|
||||
b.Property<int>("CategoryId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleCategoryId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<int?>("ParentCategoryCategoryId");
|
||||
|
||||
b.HasKey("CategoryId");
|
||||
|
||||
b.HasIndex("AudibleCategoryId");
|
||||
|
||||
b.HasIndex("ParentCategoryCategoryId");
|
||||
|
||||
b.ToTable("Categories");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
CategoryId = -1,
|
||||
AudibleCategoryId = "",
|
||||
Name = ""
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Contributor", b =>
|
||||
{
|
||||
b.Property<int>("ContributorId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleAuthorId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.HasKey("ContributorId");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("Contributors");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||
{
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<DateTime>("DateAdded");
|
||||
|
||||
b.Property<string>("DownloadBookLink");
|
||||
|
||||
b.HasKey("BookId");
|
||||
|
||||
b.ToTable("Library");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Series", b =>
|
||||
{
|
||||
b.Property<int>("SeriesId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleSeriesId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.HasKey("SeriesId");
|
||||
|
||||
b.HasIndex("AudibleSeriesId");
|
||||
|
||||
b.ToTable("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||
{
|
||||
b.Property<int>("SeriesId");
|
||||
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<float?>("Index");
|
||||
|
||||
b.HasKey("SeriesId", "BookId");
|
||||
|
||||
b.HasIndex("BookId");
|
||||
|
||||
b.HasIndex("SeriesId");
|
||||
|
||||
b.ToTable("SeriesBook");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Book", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Category", "Category")
|
||||
.WithMany()
|
||||
.HasForeignKey("CategoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.OwnsMany("DataLayer.Supplement", "Supplements", b1 =>
|
||||
{
|
||||
b1.Property<int>("SupplementId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b1.Property<int>("BookId");
|
||||
|
||||
b1.Property<string>("Url");
|
||||
|
||||
b1.HasKey("SupplementId");
|
||||
|
||||
b1.HasIndex("BookId");
|
||||
|
||||
b1.ToTable("Supplement");
|
||||
|
||||
b1.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("Supplements")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
b.OwnsOne("DataLayer.UserDefinedItem", "UserDefinedItem", b1 =>
|
||||
{
|
||||
b1.Property<int>("BookId");
|
||||
|
||||
b1.Property<string>("Tags");
|
||||
|
||||
b1.HasKey("BookId");
|
||||
|
||||
b1.ToTable("UserDefinedItem");
|
||||
|
||||
b1.HasOne("DataLayer.Book", "Book")
|
||||
.WithOne("UserDefinedItem")
|
||||
.HasForeignKey("DataLayer.UserDefinedItem", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b1.OwnsOne("DataLayer.Rating", "Rating", b2 =>
|
||||
{
|
||||
b2.Property<int>("UserDefinedItemBookId");
|
||||
|
||||
b2.Property<float>("OverallRating");
|
||||
|
||||
b2.Property<float>("PerformanceRating");
|
||||
|
||||
b2.Property<float>("StoryRating");
|
||||
|
||||
b2.HasKey("UserDefinedItemBookId");
|
||||
|
||||
b2.ToTable("UserDefinedItem");
|
||||
|
||||
b2.HasOne("DataLayer.UserDefinedItem")
|
||||
.WithOne("Rating")
|
||||
.HasForeignKey("DataLayer.Rating", "UserDefinedItemBookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
});
|
||||
|
||||
b.OwnsOne("DataLayer.Rating", "Rating", b1 =>
|
||||
{
|
||||
b1.Property<int>("BookId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b1.Property<float>("OverallRating");
|
||||
|
||||
b1.Property<float>("PerformanceRating");
|
||||
|
||||
b1.Property<float>("StoryRating");
|
||||
|
||||
b1.HasKey("BookId");
|
||||
|
||||
b1.ToTable("Books");
|
||||
|
||||
b1.HasOne("DataLayer.Book")
|
||||
.WithOne("Rating")
|
||||
.HasForeignKey("DataLayer.Rating", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("ContributorsLink")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("DataLayer.Contributor", "Contributor")
|
||||
.WithMany("BooksLink")
|
||||
.HasForeignKey("ContributorId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Category", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Category", "ParentCategory")
|
||||
.WithMany()
|
||||
.HasForeignKey("ParentCategoryCategoryId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithOne()
|
||||
.HasForeignKey("DataLayer.LibraryBook", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("SeriesLink")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("DataLayer.Series", "Series")
|
||||
.WithMany("BooksLink")
|
||||
.HasForeignKey("SeriesId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace DataLayer.Migrations
|
||||
{
|
||||
public partial class PublisherContrib : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Publisher",
|
||||
table: "Books");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Publisher",
|
||||
table: "Books",
|
||||
nullable: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
298
DataLayer/UNTESTED/Migrations/LibationContextModelSnapshot.cs
Normal file
298
DataLayer/UNTESTED/Migrations/LibationContextModelSnapshot.cs
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using DataLayer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace DataLayer.Migrations
|
||||
{
|
||||
[DbContext(typeof(LibationContext))]
|
||||
partial class LibationContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.2.0-rtm-35687")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("DataLayer.Book", b =>
|
||||
{
|
||||
b.Property<int>("BookId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleProductId");
|
||||
|
||||
b.Property<int>("CategoryId");
|
||||
|
||||
b.Property<DateTime?>("DatePublished");
|
||||
|
||||
b.Property<string>("Description");
|
||||
|
||||
b.Property<bool>("HasBookDetails");
|
||||
|
||||
b.Property<bool>("IsAbridged");
|
||||
|
||||
b.Property<int>("LengthInMinutes");
|
||||
|
||||
b.Property<string>("PictureId");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.HasKey("BookId");
|
||||
|
||||
b.HasIndex("AudibleProductId");
|
||||
|
||||
b.HasIndex("CategoryId");
|
||||
|
||||
b.ToTable("Books");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||
{
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<int>("ContributorId");
|
||||
|
||||
b.Property<int>("Role");
|
||||
|
||||
b.Property<byte>("Order");
|
||||
|
||||
b.HasKey("BookId", "ContributorId", "Role");
|
||||
|
||||
b.HasIndex("BookId");
|
||||
|
||||
b.HasIndex("ContributorId");
|
||||
|
||||
b.ToTable("BookContributor");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Category", b =>
|
||||
{
|
||||
b.Property<int>("CategoryId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleCategoryId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<int?>("ParentCategoryCategoryId");
|
||||
|
||||
b.HasKey("CategoryId");
|
||||
|
||||
b.HasIndex("AudibleCategoryId");
|
||||
|
||||
b.HasIndex("ParentCategoryCategoryId");
|
||||
|
||||
b.ToTable("Categories");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
CategoryId = -1,
|
||||
AudibleCategoryId = "",
|
||||
Name = ""
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Contributor", b =>
|
||||
{
|
||||
b.Property<int>("ContributorId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleAuthorId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.HasKey("ContributorId");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("Contributors");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||
{
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<DateTime>("DateAdded");
|
||||
|
||||
b.Property<string>("DownloadBookLink");
|
||||
|
||||
b.HasKey("BookId");
|
||||
|
||||
b.ToTable("Library");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Series", b =>
|
||||
{
|
||||
b.Property<int>("SeriesId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AudibleSeriesId");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.HasKey("SeriesId");
|
||||
|
||||
b.HasIndex("AudibleSeriesId");
|
||||
|
||||
b.ToTable("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||
{
|
||||
b.Property<int>("SeriesId");
|
||||
|
||||
b.Property<int>("BookId");
|
||||
|
||||
b.Property<float?>("Index");
|
||||
|
||||
b.HasKey("SeriesId", "BookId");
|
||||
|
||||
b.HasIndex("BookId");
|
||||
|
||||
b.HasIndex("SeriesId");
|
||||
|
||||
b.ToTable("SeriesBook");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Book", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Category", "Category")
|
||||
.WithMany()
|
||||
.HasForeignKey("CategoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.OwnsMany("DataLayer.Supplement", "Supplements", b1 =>
|
||||
{
|
||||
b1.Property<int>("SupplementId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b1.Property<int>("BookId");
|
||||
|
||||
b1.Property<string>("Url");
|
||||
|
||||
b1.HasKey("SupplementId");
|
||||
|
||||
b1.HasIndex("BookId");
|
||||
|
||||
b1.ToTable("Supplement");
|
||||
|
||||
b1.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("Supplements")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
b.OwnsOne("DataLayer.UserDefinedItem", "UserDefinedItem", b1 =>
|
||||
{
|
||||
b1.Property<int>("BookId");
|
||||
|
||||
b1.Property<string>("Tags");
|
||||
|
||||
b1.HasKey("BookId");
|
||||
|
||||
b1.ToTable("UserDefinedItem");
|
||||
|
||||
b1.HasOne("DataLayer.Book", "Book")
|
||||
.WithOne("UserDefinedItem")
|
||||
.HasForeignKey("DataLayer.UserDefinedItem", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b1.OwnsOne("DataLayer.Rating", "Rating", b2 =>
|
||||
{
|
||||
b2.Property<int>("UserDefinedItemBookId");
|
||||
|
||||
b2.Property<float>("OverallRating");
|
||||
|
||||
b2.Property<float>("PerformanceRating");
|
||||
|
||||
b2.Property<float>("StoryRating");
|
||||
|
||||
b2.HasKey("UserDefinedItemBookId");
|
||||
|
||||
b2.ToTable("UserDefinedItem");
|
||||
|
||||
b2.HasOne("DataLayer.UserDefinedItem")
|
||||
.WithOne("Rating")
|
||||
.HasForeignKey("DataLayer.Rating", "UserDefinedItemBookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
});
|
||||
|
||||
b.OwnsOne("DataLayer.Rating", "Rating", b1 =>
|
||||
{
|
||||
b1.Property<int>("BookId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b1.Property<float>("OverallRating");
|
||||
|
||||
b1.Property<float>("PerformanceRating");
|
||||
|
||||
b1.Property<float>("StoryRating");
|
||||
|
||||
b1.HasKey("BookId");
|
||||
|
||||
b1.ToTable("Books");
|
||||
|
||||
b1.HasOne("DataLayer.Book")
|
||||
.WithOne("Rating")
|
||||
.HasForeignKey("DataLayer.Rating", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("ContributorsLink")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("DataLayer.Contributor", "Contributor")
|
||||
.WithMany("BooksLink")
|
||||
.HasForeignKey("ContributorId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Category", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Category", "ParentCategory")
|
||||
.WithMany()
|
||||
.HasForeignKey("ParentCategoryCategoryId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithOne()
|
||||
.HasForeignKey("DataLayer.LibraryBook", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("SeriesLink")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("DataLayer.Series", "Series")
|
||||
.WithMany("BooksLink")
|
||||
.HasForeignKey("SeriesId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
46
DataLayer/UNTESTED/QueryObjects/BookQueries.cs
Normal file
46
DataLayer/UNTESTED/QueryObjects/BookQueries.cs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
public static class BookQueries
|
||||
{
|
||||
public static int BooksWithoutDetailsCount()
|
||||
{
|
||||
using (var context = LibationContext.Create())
|
||||
return context
|
||||
.Books
|
||||
.Count(b => !b.HasBookDetails);
|
||||
}
|
||||
|
||||
public static Book GetBook_Flat_NoTracking(string productId)
|
||||
{
|
||||
using (var context = LibationContext.Create())
|
||||
return context
|
||||
.Books
|
||||
.AsNoTracking()
|
||||
.GetBook(productId);
|
||||
}
|
||||
|
||||
public static Book GetBook(this IQueryable<Book> books, string productId)
|
||||
=> books
|
||||
.GetBooks()
|
||||
.SingleOrDefault(b => b.AudibleProductId == productId);
|
||||
|
||||
/// <summary>This is still IQueryable. YOU MUST CALL ToList() YOURSELF</summary>
|
||||
public static IQueryable<Book> GetBooks(this IQueryable<Book> books, Expression<Func<Book, bool>> predicate)
|
||||
=> books
|
||||
.GetBooks()
|
||||
.Where(predicate);
|
||||
|
||||
public static IQueryable<Book> GetBooks(this IQueryable<Book> books)
|
||||
=> books
|
||||
// owned items are always loaded. eg: book.UserDefinedItem, book.Supplements
|
||||
.Include(b => b.SeriesLink).ThenInclude(sb => sb.Series)
|
||||
.Include(b => b.ContributorsLink).ThenInclude(c => c.Contributor)
|
||||
.Include(b => b.Category).ThenInclude(c => c.ParentCategory);
|
||||
}
|
||||
}
|
||||
19
DataLayer/UNTESTED/QueryObjects/GenericPaging.cs
Normal file
19
DataLayer/UNTESTED/QueryObjects/GenericPaging.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
public static class GenericPaging
|
||||
{
|
||||
public static IQueryable<T> Page<T>(this IQueryable<T> query, int pageNumZeroStart, int pageSize)
|
||||
{
|
||||
if (pageSize < 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(pageSize), "pageSize must be at least 1");
|
||||
|
||||
if (pageNumZeroStart > 0)
|
||||
query = query.Skip(pageNumZeroStart * pageSize);
|
||||
|
||||
return query.Take(pageSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
41
DataLayer/UNTESTED/QueryObjects/LibraryQueries.cs
Normal file
41
DataLayer/UNTESTED/QueryObjects/LibraryQueries.cs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
public static class LibraryQueries
|
||||
{
|
||||
public static List<LibraryBook> GetLibrary_Flat_NoTracking()
|
||||
{
|
||||
using (var context = LibationContext.Create())
|
||||
return context
|
||||
.Library
|
||||
.AsNoTracking()
|
||||
.GetLibrary()
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static LibraryBook GetLibraryBook_Flat_NoTracking(string productId)
|
||||
{
|
||||
using (var context = LibationContext.Create())
|
||||
return context
|
||||
.Library
|
||||
.AsNoTracking()
|
||||
.GetLibraryBook(productId);
|
||||
}
|
||||
|
||||
/// <summary>This is still IQueryable. YOU MUST CALL ToList() YOURSELF</summary>
|
||||
public static IQueryable<LibraryBook> GetLibrary(this IQueryable<LibraryBook> library)
|
||||
=> library
|
||||
// owned items are always loaded. eg: book.UserDefinedItem, book.Supplements
|
||||
.Include(le => le.Book).ThenInclude(b => b.SeriesLink).ThenInclude(sb => sb.Series)
|
||||
.Include(le => le.Book).ThenInclude(b => b.ContributorsLink).ThenInclude(c => c.Contributor)
|
||||
.Include(le => le.Book).ThenInclude(b => b.Category).ThenInclude(c => c.ParentCategory);
|
||||
|
||||
public static LibraryBook GetLibraryBook(this IQueryable<LibraryBook> library, string productId)
|
||||
=> library
|
||||
.GetLibrary()
|
||||
.SingleOrDefault(le => le.Book.AudibleProductId == productId);
|
||||
}
|
||||
}
|
||||
60
DataLayer/UNTESTED/TagPersistenceInterceptor.cs
Normal file
60
DataLayer/UNTESTED/TagPersistenceInterceptor.cs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dinah.Core.Collections.Generic;
|
||||
using Dinah.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
internal class TagPersistenceInterceptor : IDbInterceptor
|
||||
{
|
||||
public void Executing(DbContext context)
|
||||
{
|
||||
doWork__EFCore(context);
|
||||
}
|
||||
|
||||
public void Executed(DbContext context) { }
|
||||
|
||||
static void doWork__EFCore(DbContext context)
|
||||
{
|
||||
// persist tags:
|
||||
var modifiedEntities = context.ChangeTracker.Entries().Where(p => p.State.In(EntityState.Modified, EntityState.Added)).ToList();
|
||||
var tagSets = modifiedEntities.Select(e => e.Entity as UserDefinedItem).Where(a => a != null).ToList();
|
||||
foreach (var t in tagSets)
|
||||
FileManager.TagsPersistence.Save(t.Book.AudibleProductId, t.Tags);
|
||||
}
|
||||
|
||||
#region // notes: working with proxies, esp EF 6
|
||||
// EF 6: entities are proxied with lazy loading when collections are virtual
|
||||
// EF Core: lazy loading is supported in 2.1 (there is a version of lazy loading with proxy-wrapping and a proxy-less version with DI) but not on by default and are not supported here
|
||||
|
||||
//static void doWork_EF6(DbContext context)
|
||||
//{
|
||||
// var modifiedEntities = context.ChangeTracker.Entries().Where(p => p.State == EntityState.Modified).ToList();
|
||||
// var unproxiedEntities = modifiedEntities.Select(me => UnProxy(context, me.Entity)).ToList();
|
||||
|
||||
// // persist tags
|
||||
// var tagSets = unproxiedEntities.Select(ue => ue as UserDefinedItem).Where(a => a != null).ToList();
|
||||
// foreach (var t in tagSets)
|
||||
// FileManager.TagsPersistence.Save(t.ProductId, t.TagsRaw);
|
||||
//}
|
||||
|
||||
//// https://stackoverflow.com/a/25774651
|
||||
//private static T UnProxy<T>(DbContext context, T proxyObject) where T : class
|
||||
//{
|
||||
// // alternative: https://docs.microsoft.com/en-us/ef/ef6/fundamentals/proxies
|
||||
// var proxyCreationEnabled = context.Configuration.ProxyCreationEnabled;
|
||||
// try
|
||||
// {
|
||||
// context.Configuration.ProxyCreationEnabled = false;
|
||||
// return context.Entry(proxyObject).CurrentValues.ToObject() as T;
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// context.Configuration.ProxyCreationEnabled = proxyCreationEnabled;
|
||||
// }
|
||||
//}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
124
DataLayer/UNTESTED/_scratch pad/ScratchPad.cs
Normal file
124
DataLayer/UNTESTED/_scratch pad/ScratchPad.cs
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
using System;
|
||||
using Dinah.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace _scratch_pad
|
||||
{
|
||||
////// to use this as a console, open properties and change from class library => console
|
||||
//// DON'T FORGET TO REVERT IT
|
||||
//public class Program
|
||||
//{
|
||||
// public static void Main(string[] args)
|
||||
// {
|
||||
// var user = new Student() { Name = "Dinah Cheshire" };
|
||||
// var udi = new UserDef { UserDefId = 1, TagsRaw = "my,tags" };
|
||||
|
||||
// using (var context = new MyTestContextDesignTimeDbContextFactory().Create())
|
||||
// {
|
||||
// context.Add(user);
|
||||
// //context.Add(udi);
|
||||
// context.Update(udi);
|
||||
// context.SaveChanges();
|
||||
// }
|
||||
|
||||
// Console.WriteLine($"Student was saved in the database with id: {user.Id}");
|
||||
// }
|
||||
//}
|
||||
|
||||
public class MyTestContextDesignTimeDbContextFactory : DesignTimeDbContextFactoryBase<MyTestContext>
|
||||
{
|
||||
protected override MyTestContext CreateNewInstance(DbContextOptions<MyTestContext> options) => new MyTestContext(options);
|
||||
protected override void UseDatabaseEngine(DbContextOptionsBuilder optionsBuilder, string connectionString) => optionsBuilder.UseSqlite(connectionString);
|
||||
}
|
||||
|
||||
public class MyTestContext : DbContext
|
||||
{
|
||||
// see DesignTimeDbContextFactoryBase for info about ctors and connection strings/OnConfiguring()
|
||||
public MyTestContext(DbContextOptions<MyTestContext> options) : base(options) { }
|
||||
|
||||
#region classes for OnModelCreating() seed example
|
||||
class Blog
|
||||
{
|
||||
public int BlogId { get; set; }
|
||||
public string Url { get; set; }
|
||||
public System.Collections.Generic.ICollection<Post> Posts { get; set; }
|
||||
}
|
||||
class Post
|
||||
{
|
||||
public int PostId { get; set; }
|
||||
public string Content { get; set; }
|
||||
public string Title { get; set; }
|
||||
public int BlogId { get; set; }
|
||||
public Blog Blog { get; set; }
|
||||
public Name AuthorName { get; set; }
|
||||
}
|
||||
class Name
|
||||
{
|
||||
public string First { get; set; }
|
||||
public string Last { get; set; }
|
||||
}
|
||||
#endregion
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
// config
|
||||
modelBuilder.Entity<Blog>(entity => entity.Property(e => e.Url).IsRequired());
|
||||
modelBuilder.Entity<Order>().OwnsOne(p => p.OrderDetails, cb =>
|
||||
{
|
||||
cb.OwnsOne(c => c.BillingAddress);
|
||||
cb.OwnsOne(c => c.ShippingAddress);
|
||||
});
|
||||
modelBuilder.Entity<Post>(entity =>
|
||||
entity
|
||||
.HasOne(d => d.Blog)
|
||||
.WithMany(p => p.Posts)
|
||||
.HasForeignKey("BlogId"));
|
||||
|
||||
// BlogSeed
|
||||
modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = 1, Url = "http://sample.com" });
|
||||
|
||||
// PostSeed
|
||||
modelBuilder.Entity<Post>().HasData(new Post() { BlogId = 1, PostId = 1, Title = "First post", Content = "Test 1" });
|
||||
|
||||
// AnonymousPostSeed
|
||||
modelBuilder.Entity<Post>().HasData(new { BlogId = 1, PostId = 2, Title = "Second post", Content = "Test 2" });
|
||||
|
||||
// OwnedTypeSeed
|
||||
modelBuilder.Entity<Post>().OwnsOne(p => p.AuthorName).HasData(
|
||||
new { PostId = 1, First = "Andriy", Last = "Svyryd" },
|
||||
new { PostId = 2, First = "Diego", Last = "Vega" });
|
||||
}
|
||||
|
||||
public DbSet<Student> Students { get; set; }
|
||||
public DbSet<UserDef> UserDefs { get; set; }
|
||||
public DbSet<Order> Orders { get; set; }
|
||||
}
|
||||
|
||||
public class Student
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
public class UserDef
|
||||
{
|
||||
public int UserDefId { get; set; }
|
||||
public string TagsRaw { get; set; }
|
||||
}
|
||||
|
||||
public class Order
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public OrderDetails OrderDetails { get; set; }
|
||||
}
|
||||
public class OrderDetails
|
||||
{
|
||||
public StreetAddress BillingAddress { get; set; }
|
||||
public StreetAddress ShippingAddress { get; set; }
|
||||
}
|
||||
public class StreetAddress
|
||||
{
|
||||
public string Street { get; set; }
|
||||
public string City { get; set; }
|
||||
}
|
||||
}
|
||||
48
DataLayer/_HowTo- EF Core.txt
Normal file
48
DataLayer/_HowTo- EF Core.txt
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
HOW TO CREATE: EF CORE PROJECT
|
||||
==============================
|
||||
easiest with .NET Core but there's also a work-around for .NET Standard
|
||||
example is for sqlite but the same works with MsSql
|
||||
|
||||
|
||||
nuget
|
||||
Microsoft.EntityFrameworkCore.Tools (needed for using Package Manager Console)
|
||||
Microsoft.EntityFrameworkCore.Sqlite
|
||||
|
||||
MIGRATIONS require standard, not core
|
||||
using standard instead of core. edit 3 things in csproj
|
||||
1of3: pluralize xml TargetFramework tag to TargetFrameworks
|
||||
2of2: TargetFrameworks from: netstandard2.0
|
||||
to: netcoreapp2.1;netstandard2.0
|
||||
3of3: add
|
||||
<PropertyGroup>
|
||||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
run. error
|
||||
SQLite Error 1: 'no such table: Blogs'.
|
||||
|
||||
set project "Set as StartUp Project"
|
||||
|
||||
Tools >> Nuget Package Manager >> Package Manager Console
|
||||
default project: Examples\SQLite_NETCore2_0
|
||||
|
||||
note: in EFCore, Enable-Migrations is no longer used. start with add-migration
|
||||
PM> add-migration InitialCreate
|
||||
PM> Update-Database
|
||||
|
||||
if add-migration xyz throws and error, don't take the error msg at face value. try again with add-migration xyz -verbose
|
||||
|
||||
new sqlite .db file created: Copy always/Copy if newer
|
||||
or copy .db file to destination
|
||||
|
||||
relative:
|
||||
optionsBuilder.UseSqlite("Data Source=blogging.db");
|
||||
absolute (use fwd slashes):
|
||||
optionsBuilder.UseSqlite("Data Source=C:/foo/bar/blogging.db");
|
||||
|
||||
|
||||
REFERENCE ARTICLES
|
||||
------------------
|
||||
https://docs.microsoft.com/en-us/ef/core/get-started/netcore/new-db-sqlite
|
||||
https://carlos.mendible.com/2016/07/11/step-by-step-dotnet-core-and-entity-framework-core/
|
||||
https://www.benday.com/2017/12/19/ef-core-2-0-migrations-without-hard-coded-connection-strings/
|
||||
55
DataLayer/_big db refactor.txt
Normal file
55
DataLayer/_big db refactor.txt
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
proposed extensible schema to generalize beyond audible
|
||||
|
||||
problems
|
||||
0) reeks of premature optimization
|
||||
- i'm currently only doing audible audiobooks. this adds several layers of abstraction for the sake of possible expansion
|
||||
- there's a good chance that supporting another platform may not conform to this schema, in which case i'd have done this for nothing. genres are one likely pain point
|
||||
- libation is currently single-user. hopefully the below would suffice for adding users, but if i'm wrong it might be all pain and no gain
|
||||
1) very thorough == very complex
|
||||
2) there are some books which would still be difficult to taxonimize
|
||||
- joy of cooking. has become more of a brand
|
||||
- the bible. has different versions that aren't just editions
|
||||
- dictionary. authored by a publisher
|
||||
3) "books" vs "editions" is a confusing problem waiting to happen
|
||||
|
||||
[AIPK=auto increm PK]
|
||||
|
||||
(libation) users [AIPK id, name, join date]
|
||||
audible users [AIPK id, AUDIBLE-PK username]
|
||||
libation audible users [PK user id, PK audible user id -- cluster PK across all FKs]
|
||||
- potential danger in multi-user environment. wouldn't want one libation user getting access to a different libation user's audible info
|
||||
contributors [AIPK id, name]. prev people. incl publishers
|
||||
audible authors [PK/FK contributor id, AUDIBLE-PK author id]
|
||||
roles [AIPK id, name]. seeded: author, narrator, publisher. could expand (eg: translator, editor) without each needing a new table
|
||||
books [AIPK id, title, desc]
|
||||
book contributors [FK book id, FK contributor id, FK role id, order -- cluster PK across all FKs]
|
||||
- likely only authors
|
||||
editions [AIPK id, FK book id, title]. could expand to include year, is first edition, is abridged
|
||||
- reasons for optional different title: "Ender's Game: Special 20th Anniversary Edition", "Harry Potter and the Sorcerer's Stone" vs "Harry Potter and the Philosopher's Stone" vs "Harry Potter y la piedra filosofal", "Midnight Riot" vs "Rivers of London"
|
||||
edition contributors [FK edition id, FK contributor id, FK role id, order -- cluster PK across all FKs]
|
||||
- likely everything except authors. eg narrators, publisher
|
||||
audiobooks [PK/FK edition id, lengthInMinutes]
|
||||
- could expand to other formats by adding other similar tables. eg: print with #pages and isbn, ebook with mb
|
||||
audible origins [AIPK id, name]. seeded: library. detail. json. series
|
||||
audible books [PK/FK edition id, AUDIBLE-PK product id, picture id, sku, 3 ratings, audible category id, audible origin id]
|
||||
- could expand to other vendors by adding other similar tables
|
||||
audible user ratings [PK/FK edition id, audible user id, 3 ratings]
|
||||
audible supplements [AIPK id, FK edition id, download url]
|
||||
- pdfs only. although book download info could be the same format, they're substantially different and subject to change
|
||||
audible book downloads [PK/FK edition id, audible user id, bookdownloadlink]
|
||||
pictures [AIPK id, FK edition id, filename (xyz.jpg -- not incl path)]
|
||||
audible categories [AIPK id, AUDIBLE-PK category id, name, parent]. may only nest 1 deep
|
||||
(libation) library [FK libation user id, FK edition id, date added -- cluster PK across all FKs]
|
||||
(libation) user defined [FK libation user id, FK edition id, tagsRaw (, notes...) -- cluster PK across all FKs]
|
||||
- there's no reason to restrict tags to library items, so don't combine/link this table with library
|
||||
series [AIPK id, name]
|
||||
audible series [FK series id, AUDIBLE-PK series id/asin, audible origin id]
|
||||
- could also include a 'name' field for what audible calls this series
|
||||
series books [FK series id, FK book id (NOT edition id), index -- cluster PK across all FKs]
|
||||
- "index" not "order". display this number; don't just put in this sequence
|
||||
- index is float instead of int to allow for in-between books. eg 2.5
|
||||
- if only using "editions" (ie: getting rid of the "books" table), to show 2 editions as the same book in a series, give them the same index
|
||||
(libation) user shelves [AIPK id, FK libation user id, name, desc]
|
||||
- custom shelf. similar to library but very different in philosophy. likely different in evolving details
|
||||
(libation) shelf books [AIPK id, FK user shelf id, date added, order]
|
||||
- technically, it's no violation to list a book more than once so use AIPK
|
||||
76
DataLayer/_schema and patterns.txt
Normal file
76
DataLayer/_schema and patterns.txt
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
ignore for now:
|
||||
authorProperties [PK/FK contributor id, AUDIBLE-PK author id]
|
||||
notes in Contributor.cs for later refactoring
|
||||
|
||||
c# enum only, not their own tables:
|
||||
roles [AIPK id, name]. seeded: author, narrator, publisher. could expand (eg: translator, editor) without each needing a new table
|
||||
origins [AIPK id, name]. seeded: library. detail. json. series
|
||||
|
||||
|
||||
-- begin SCHEMA ---------------------------------------------------------------------------------------------------------------------
|
||||
any audible keys should be indexed
|
||||
|
||||
SCHEMA
|
||||
======
|
||||
contributors [AIPK id, name]. people and publishers
|
||||
books [AIPK id, AUDIBLE-PK product id, title, desc, lengthInMinutes, picture id, 3 ratings, category id, origin id]
|
||||
- product instances. each edition and version is discrete: unique and disconnected from different editions of the same book
|
||||
- on book re-import
|
||||
update:
|
||||
update book origin and series origin with the new source type
|
||||
overwrite simple fields
|
||||
invoke complex contributor updates
|
||||
details page gets
|
||||
un/abridged
|
||||
release date
|
||||
language
|
||||
publisher
|
||||
series info incl name
|
||||
categories
|
||||
if new == series: ignore. do update series info. do not update book info
|
||||
else if old == json: update (incl if new == json)
|
||||
else if old == library && new == detail: update
|
||||
else: ignore
|
||||
book contributors [FK book id, FK contributor id, FK role id, order -- cluster PK across all FKs]
|
||||
supplements [AIPK id, FK book id, download url]
|
||||
categories [AIPK id, AUDIBLE-PK category id, name, parent]. may only nest 1 deep
|
||||
user defined [PK/FK book id, 3 ratings, tagsRaw]
|
||||
series [AIPK id, AUDIBLE-PK series id/asin, name, origin id]
|
||||
series books [FK series id, FK book id, index -- cluster PK across all FKs]
|
||||
- "index" not "order". display this number; don't just put in this sequence
|
||||
- index is float instead of int to allow for in-between books. eg 2.5
|
||||
- to show 2 editions as the same book in a series, give them the same index
|
||||
- re-import using series page, there will need to be a re-eval of import logic
|
||||
library [PK/FK book id, date added, bookdownloadlink]
|
||||
-- end SCHEMA ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- begin SIMPLIFIED DDD ---------------------------------------------------------------------------------------------------------------------
|
||||
combine domain and persistence (C(r)UD). no repository pattern. encapsulated in domain objects; direct calls to EF Core
|
||||
https://www.thereformedprogrammer.net/creating-domain-driven-design-entity-classes-with-entity-framework-core/
|
||||
// pattern for x-to-many
|
||||
public void AddReview(int numStars, DbContext context = null)
|
||||
{
|
||||
if (_reviews != null) _reviews.Add(new Review(numStars));
|
||||
else if (context == null) throw new Exception("need context");
|
||||
else if (context.Entry(this).IsKeySet) context.Add(new Review(numStars, BookId));
|
||||
else throw new Exception("Could not add");
|
||||
}
|
||||
|
||||
// pattern for optional one-to-one
|
||||
MyPropClass MyProps { get; private set; }
|
||||
public void AddMyProps(string s, int i, DbContext context = null)
|
||||
{
|
||||
// avoid a trip to the db
|
||||
if (MyProps != null) { MyProps.Update(s, i); return; }
|
||||
if (BookId == 0) { MyProps = new MyPropClass(s, i); return; }
|
||||
if (context == null) throw new Exception("need context");
|
||||
// per Jon P Smith, this single trip to db loads the property if there is one
|
||||
// note: .Reference() is for single object references. for collections use .Collection()
|
||||
context.Entry(this).Reference(s => s.MyProps).Load();
|
||||
if (MyProps != null) MyProps.Update(s, i);
|
||||
else MyProps = new MyPropClass(s, i);
|
||||
}
|
||||
|
||||
repository reads are 'query object'-like extension methods
|
||||
https://www.thereformedprogrammer.net/is-the-repository-pattern-useful-with-entity-framework-core/#1-query-objects-a-way-to-isolate-and-hide-database-read-code
|
||||
-- and SIMPLIFIED DDD ---------------------------------------------------------------------------------------------------------------------
|
||||
8
DataLayer/appsettings.json
Normal file
8
DataLayer/appsettings.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"ConnectionStrings": {
|
||||
"LibationContext": "Server=(LocalDb)\\MSSQLLocalDB;Database=DataLayer.LibationContext;Integrated Security=true;",
|
||||
|
||||
"// on windows sqlite paths accept windows and/or unix slashes": "",
|
||||
"MyTestContext": "Data Source=%DESKTOP%/sample.db"
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue