Made foreign keys required causing cascade deletion.

Removed completed TODO comments.
This commit is contained in:
Harrison Deng 2021-07-14 01:13:19 -05:00
parent 7e8a398741
commit bad22090a3
9 changed files with 109 additions and 193 deletions

View File

@ -1,14 +1,16 @@
using Props.Models; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using IdentityServer4.EntityFramework.Options; using IdentityServer4.EntityFramework.Options;
using Microsoft.AspNetCore.ApiAuthorization.IdentityServer; using Microsoft.AspNetCore.ApiAuthorization.IdentityServer;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.Extensions.Options;
using Props.Models;
using Props.Shared.Models;
using Props.Shop.Framework;
namespace Props.Data namespace Props.Data
{ {
@ -20,30 +22,42 @@ namespace Props.Data
{ {
} }
protected override void OnModelCreating(ModelBuilder modelBuilder) { protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder); base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ResultsPreferences>() modelBuilder.Entity<ResultsPreferences>()
.Property(e => e.Order) .Property(e => e.Order)
.HasConversion( .HasConversion(
v => JsonSerializer.Serialize(v, null), v => JsonSerializer.Serialize(v, null),
v => JsonSerializer.Deserialize<List<ResultsPreferences.Category>>(v, null), v => JsonSerializer.Deserialize<List<ResultsPreferences.Category>>(v, null),
new ValueComparer<IList<ResultsPreferences.Category>>( new ValueComparer<IList<ResultsPreferences.Category>>(
(a, b) => a.SequenceEqual(b), (a, b) => a.SequenceEqual(b),
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())), c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
c => (IList<ResultsPreferences.Category>) c.ToList() c => (IList<ResultsPreferences.Category>)c.ToList()
) )
); );
modelBuilder.Entity<SearchOutline>() modelBuilder.Entity<SearchOutline>()
.Property(e => e.ShopStates) .Property(e => e.ShopStates)
.HasConversion( .HasConversion(
v => JsonSerializer.Serialize(v, null), v => JsonSerializer.Serialize(v, null),
v => JsonSerializer.Deserialize<SearchOutline.ShopToggler>(v, null), v => JsonSerializer.Deserialize<SearchOutline.ShopToggler>(v, null),
new ValueComparer<SearchOutline.ShopToggler>( new ValueComparer<SearchOutline.ShopToggler>(
(a, b) => a.Equals(b), (a, b) => a.Equals(b),
c => c.GetHashCode(), c => c.GetHashCode(),
c => c.Clone() c => c.Copy()
)
);
modelBuilder.Entity<SearchOutline>()
.Property(e => e.Filters)
.HasConversion(
v => JsonSerializer.Serialize(v, null),
v => JsonSerializer.Deserialize<Filters>(v, null),
new ValueComparer<Filters>(
(a, b) => a.Equals(b),
c => c.GetHashCode(),
c => c.Copy()
) )
); );
} }

View File

@ -9,7 +9,7 @@ using Props.Data;
namespace Props.Data.Migrations namespace Props.Data.Migrations
{ {
[DbContext(typeof(ApplicationDbContext))] [DbContext(typeof(ApplicationDbContext))]
[Migration("20210712080053_InitialCreate")] [Migration("20210714061021_InitialCreate")]
partial class InitialCreate partial class InitialCreate
{ {
protected override void BuildTargetModel(ModelBuilder modelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -324,6 +324,7 @@ namespace Props.Data.Migrations
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<string>("ApplicationUserId") b.Property<string>("ApplicationUserId")
.IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<string>("Order") b.Property<string>("Order")
@ -345,54 +346,19 @@ namespace Props.Data.Migrations
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<string>("ApplicationUserId") b.Property<string>("ApplicationUserId")
.IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int>("Currency") b.Property<string>("Filters")
.HasColumnType("INTEGER"); .HasColumnType("TEXT");
b.Property<bool>("EnableMaxShippingFee")
.HasColumnType("INTEGER");
b.Property<bool>("EnableUpperPrice")
.HasColumnType("INTEGER");
b.Property<bool>("KeepUnknownPurchaseCount")
.HasColumnType("INTEGER");
b.Property<bool>("KeepUnknownRatingCount")
.HasColumnType("INTEGER");
b.Property<bool>("KeepUnknownShipping")
.HasColumnType("INTEGER");
b.Property<bool>("KeepUnrated")
.HasColumnType("INTEGER");
b.Property<int>("LowerPrice")
.HasColumnType("INTEGER");
b.Property<int>("MaxResults") b.Property<int>("MaxResults")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<int>("MaxShippingFee")
.HasColumnType("INTEGER");
b.Property<int>("MinPurchases")
.HasColumnType("INTEGER");
b.Property<float>("MinRating")
.HasColumnType("REAL");
b.Property<int>("MinReviews")
.HasColumnType("INTEGER");
b.Property<string>("ShopStates") b.Property<string>("ShopStates")
.IsRequired() .IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int>("UpperPrice")
.HasColumnType("INTEGER");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("ApplicationUserId") b.HasIndex("ApplicationUserId")
@ -408,6 +374,7 @@ namespace Props.Data.Migrations
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<string>("ApplicationUserId") b.Property<string>("ApplicationUserId")
.IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<bool>("CacheCommonSearches") b.Property<bool>("CacheCommonSearches")
@ -482,21 +449,27 @@ namespace Props.Data.Migrations
{ {
b.HasOne("Props.Models.ApplicationUser", null) b.HasOne("Props.Models.ApplicationUser", null)
.WithOne("ResultsPreferences") .WithOne("ResultsPreferences")
.HasForeignKey("Props.Models.ResultsPreferences", "ApplicationUserId"); .HasForeignKey("Props.Models.ResultsPreferences", "ApplicationUserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Props.Models.SearchOutline", b => modelBuilder.Entity("Props.Models.SearchOutline", b =>
{ {
b.HasOne("Props.Models.ApplicationUser", null) b.HasOne("Props.Models.ApplicationUser", null)
.WithOne("SearchOutline") .WithOne("SearchOutline")
.HasForeignKey("Props.Models.SearchOutline", "ApplicationUserId"); .HasForeignKey("Props.Models.SearchOutline", "ApplicationUserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Props.Shared.Models.ApplicationPreferences", b => modelBuilder.Entity("Props.Shared.Models.ApplicationPreferences", b =>
{ {
b.HasOne("Props.Models.ApplicationUser", null) b.HasOne("Props.Models.ApplicationUser", null)
.WithOne("ApplicationPreferences") .WithOne("ApplicationPreferences")
.HasForeignKey("Props.Shared.Models.ApplicationPreferences", "ApplicationUserId"); .HasForeignKey("Props.Shared.Models.ApplicationPreferences", "ApplicationUserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Props.Models.ApplicationUser", b => modelBuilder.Entity("Props.Models.ApplicationUser", b =>

View File

@ -112,7 +112,7 @@ namespace Props.Data.Migrations
{ {
Id = table.Column<int>(type: "INTEGER", nullable: false) Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true), .Annotation("Sqlite:Autoincrement", true),
ApplicationUserId = table.Column<string>(type: "TEXT", nullable: true), ApplicationUserId = table.Column<string>(type: "TEXT", nullable: false),
DarkMode = table.Column<bool>(type: "INTEGER", nullable: false), DarkMode = table.Column<bool>(type: "INTEGER", nullable: false),
CacheCommonSearches = table.Column<bool>(type: "INTEGER", nullable: false), CacheCommonSearches = table.Column<bool>(type: "INTEGER", nullable: false),
EnableSearchHistory = table.Column<bool>(type: "INTEGER", nullable: false) EnableSearchHistory = table.Column<bool>(type: "INTEGER", nullable: false)
@ -125,7 +125,7 @@ namespace Props.Data.Migrations
column: x => x.ApplicationUserId, column: x => x.ApplicationUserId,
principalTable: "AspNetUsers", principalTable: "AspNetUsers",
principalColumn: "Id", principalColumn: "Id",
onDelete: ReferentialAction.Restrict); onDelete: ReferentialAction.Cascade);
}); });
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
@ -219,7 +219,7 @@ namespace Props.Data.Migrations
{ {
Id = table.Column<int>(type: "INTEGER", nullable: false) Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true), .Annotation("Sqlite:Autoincrement", true),
ApplicationUserId = table.Column<string>(type: "TEXT", nullable: true), ApplicationUserId = table.Column<string>(type: "TEXT", nullable: false),
Order = table.Column<string>(type: "TEXT", nullable: false) Order = table.Column<string>(type: "TEXT", nullable: false)
}, },
constraints: table => constraints: table =>
@ -230,7 +230,7 @@ namespace Props.Data.Migrations
column: x => x.ApplicationUserId, column: x => x.ApplicationUserId,
principalTable: "AspNetUsers", principalTable: "AspNetUsers",
principalColumn: "Id", principalColumn: "Id",
onDelete: ReferentialAction.Restrict); onDelete: ReferentialAction.Cascade);
}); });
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
@ -239,21 +239,9 @@ namespace Props.Data.Migrations
{ {
Id = table.Column<int>(type: "INTEGER", nullable: false) Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true), .Annotation("Sqlite:Autoincrement", true),
ApplicationUserId = table.Column<string>(type: "TEXT", nullable: true), ApplicationUserId = table.Column<string>(type: "TEXT", nullable: false),
Currency = table.Column<int>(type: "INTEGER", nullable: false), Filters = table.Column<string>(type: "TEXT", nullable: true),
MaxResults = table.Column<int>(type: "INTEGER", nullable: false), MaxResults = table.Column<int>(type: "INTEGER", nullable: false),
MinRating = table.Column<float>(type: "REAL", nullable: false),
KeepUnrated = table.Column<bool>(type: "INTEGER", nullable: false),
EnableUpperPrice = table.Column<bool>(type: "INTEGER", nullable: false),
UpperPrice = table.Column<int>(type: "INTEGER", nullable: false),
LowerPrice = table.Column<int>(type: "INTEGER", nullable: false),
MinPurchases = table.Column<int>(type: "INTEGER", nullable: false),
KeepUnknownPurchaseCount = table.Column<bool>(type: "INTEGER", nullable: false),
MinReviews = table.Column<int>(type: "INTEGER", nullable: false),
KeepUnknownRatingCount = table.Column<bool>(type: "INTEGER", nullable: false),
EnableMaxShippingFee = table.Column<bool>(type: "INTEGER", nullable: false),
MaxShippingFee = table.Column<int>(type: "INTEGER", nullable: false),
KeepUnknownShipping = table.Column<bool>(type: "INTEGER", nullable: false),
ShopStates = table.Column<string>(type: "TEXT", nullable: false) ShopStates = table.Column<string>(type: "TEXT", nullable: false)
}, },
constraints: table => constraints: table =>
@ -264,7 +252,7 @@ namespace Props.Data.Migrations
column: x => x.ApplicationUserId, column: x => x.ApplicationUserId,
principalTable: "AspNetUsers", principalTable: "AspNetUsers",
principalColumn: "Id", principalColumn: "Id",
onDelete: ReferentialAction.Restrict); onDelete: ReferentialAction.Cascade);
}); });
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(

View File

@ -322,6 +322,7 @@ namespace Props.Data.Migrations
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<string>("ApplicationUserId") b.Property<string>("ApplicationUserId")
.IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<string>("Order") b.Property<string>("Order")
@ -343,54 +344,19 @@ namespace Props.Data.Migrations
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<string>("ApplicationUserId") b.Property<string>("ApplicationUserId")
.IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int>("Currency") b.Property<string>("Filters")
.HasColumnType("INTEGER"); .HasColumnType("TEXT");
b.Property<bool>("EnableMaxShippingFee")
.HasColumnType("INTEGER");
b.Property<bool>("EnableUpperPrice")
.HasColumnType("INTEGER");
b.Property<bool>("KeepUnknownPurchaseCount")
.HasColumnType("INTEGER");
b.Property<bool>("KeepUnknownRatingCount")
.HasColumnType("INTEGER");
b.Property<bool>("KeepUnknownShipping")
.HasColumnType("INTEGER");
b.Property<bool>("KeepUnrated")
.HasColumnType("INTEGER");
b.Property<int>("LowerPrice")
.HasColumnType("INTEGER");
b.Property<int>("MaxResults") b.Property<int>("MaxResults")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<int>("MaxShippingFee")
.HasColumnType("INTEGER");
b.Property<int>("MinPurchases")
.HasColumnType("INTEGER");
b.Property<float>("MinRating")
.HasColumnType("REAL");
b.Property<int>("MinReviews")
.HasColumnType("INTEGER");
b.Property<string>("ShopStates") b.Property<string>("ShopStates")
.IsRequired() .IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int>("UpperPrice")
.HasColumnType("INTEGER");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("ApplicationUserId") b.HasIndex("ApplicationUserId")
@ -406,6 +372,7 @@ namespace Props.Data.Migrations
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<string>("ApplicationUserId") b.Property<string>("ApplicationUserId")
.IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<bool>("CacheCommonSearches") b.Property<bool>("CacheCommonSearches")
@ -480,21 +447,27 @@ namespace Props.Data.Migrations
{ {
b.HasOne("Props.Models.ApplicationUser", null) b.HasOne("Props.Models.ApplicationUser", null)
.WithOne("ResultsPreferences") .WithOne("ResultsPreferences")
.HasForeignKey("Props.Models.ResultsPreferences", "ApplicationUserId"); .HasForeignKey("Props.Models.ResultsPreferences", "ApplicationUserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Props.Models.SearchOutline", b => modelBuilder.Entity("Props.Models.SearchOutline", b =>
{ {
b.HasOne("Props.Models.ApplicationUser", null) b.HasOne("Props.Models.ApplicationUser", null)
.WithOne("SearchOutline") .WithOne("SearchOutline")
.HasForeignKey("Props.Models.SearchOutline", "ApplicationUserId"); .HasForeignKey("Props.Models.SearchOutline", "ApplicationUserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Props.Shared.Models.ApplicationPreferences", b => modelBuilder.Entity("Props.Shared.Models.ApplicationPreferences", b =>
{ {
b.HasOne("Props.Models.ApplicationUser", null) b.HasOne("Props.Models.ApplicationUser", null)
.WithOne("ApplicationPreferences") .WithOne("ApplicationPreferences")
.HasForeignKey("Props.Shared.Models.ApplicationPreferences", "ApplicationUserId"); .HasForeignKey("Props.Shared.Models.ApplicationPreferences", "ApplicationUserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Props.Models.ApplicationUser", b => modelBuilder.Entity("Props.Models.ApplicationUser", b =>

View File

@ -1,9 +1,12 @@
using System.ComponentModel.DataAnnotations;
namespace Props.Shared.Models namespace Props.Shared.Models
{ {
public class ApplicationPreferences public class ApplicationPreferences
{ {
public int Id { get; set; } public int Id { get; set; }
[Required]
public string ApplicationUserId { get; set; } public string ApplicationUserId { get; set; }
public bool DarkMode { get; set; } public bool DarkMode { get; set; }

View File

@ -1,22 +1,36 @@
using Microsoft.AspNetCore.Identity; using System;
using Props.Shared.Models;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Props.Shared.Models;
namespace Props.Models namespace Props.Models
{ {
public class ApplicationUser : IdentityUser public class ApplicationUser : IdentityUser
{ {
[Required] [Required]
public virtual SearchOutline SearchOutline { get; private set; } = new SearchOutline(); public virtual SearchOutline SearchOutline { get; private set; }
[Required] [Required]
public virtual ResultsPreferences ResultsPreferences { get; private set; } = new ResultsPreferences(); public virtual ResultsPreferences ResultsPreferences { get; private set; }
[Required] [Required]
public virtual ApplicationPreferences ApplicationPreferences {get; private set; } = new ApplicationPreferences(); public virtual ApplicationPreferences ApplicationPreferences { get; private set; }
public ApplicationUser()
{
SearchOutline = new SearchOutline();
ResultsPreferences = new ResultsPreferences();
ApplicationPreferences = new ApplicationPreferences();
}
public ApplicationUser(SearchOutline searchOutline, ResultsPreferences resultsPreferences, ApplicationPreferences applicationPreferences)
{
this.SearchOutline = searchOutline;
this.ResultsPreferences = resultsPreferences;
this.ApplicationPreferences = applicationPreferences;
}
} }
} }

View File

@ -9,8 +9,9 @@ namespace Props.Models
public class ResultsPreferences public class ResultsPreferences
{ {
public int Id { get; set; } public int Id { get; set; }
[Required]
public string ApplicationUserId { get; set; } public string ApplicationUserId { get; set; }
[Required] [Required]
public IList<Category> Order { get; set; } public IList<Category> Order { get; set; }

View File

@ -9,46 +9,11 @@ namespace Props.Models
public class SearchOutline public class SearchOutline
{ {
public int Id { get; set; } public int Id { get; set; }
[Required]
public string ApplicationUserId { get; set; } public string ApplicationUserId { get; set; }
public Currency Currency { get; set; } = Currency.CAD; public Filters Filters { get; set; }
public int MaxResults { get; set; } = 100; public int MaxResults { get; set; } = 100;
public float MinRating { get; set; } = 0.8f;
public bool KeepUnrated { get; set; } = true;
public bool EnableUpperPrice { get; set; } = false;
private int _upperPrice;
public int UpperPrice
{
get
{
return _upperPrice;
}
set
{
if (EnableUpperPrice) _upperPrice = value;
}
}
public int LowerPrice { get; set; }
public int MinPurchases { get; set; }
public bool KeepUnknownPurchaseCount { get; set; } = true;
public int MinReviews { get; set; }
public bool KeepUnknownRatingCount { get; set; } = true;
public bool EnableMaxShippingFee { get; set; }
private int _maxShippingFee;
public int MaxShippingFee
{
get
{
return _maxShippingFee;
}
set
{
if (EnableMaxShippingFee) _maxShippingFee = value;
}
}
public bool KeepUnknownShipping { get; set; } = true;
[Required] [Required]
public ShopToggler ShopStates { get; set; } = new ShopToggler(); public ShopToggler ShopStates { get; set; } = new ShopToggler();
@ -56,11 +21,14 @@ namespace Props.Models
public sealed class ShopToggler : HashSet<string> public sealed class ShopToggler : HashSet<string>
{ {
public int TotalShops { get; set; } public int TotalShops { get; set; }
public bool this[string name] { public bool this[string name]
get { {
get
{
return !this.Contains(name); return !this.Contains(name);
} }
set { set
{
if (value == false && TotalShops - Count <= 1) return; if (value == false && TotalShops - Count <= 1) return;
if (value) if (value)
{ {
@ -73,10 +41,11 @@ namespace Props.Models
} }
} }
public ShopToggler Clone() { public ShopToggler Copy()
ShopToggler clone = new ShopToggler(); {
clone.Union(this); ShopToggler copy = new ShopToggler();
return clone; copy.Union(this);
return copy;
} }
public bool IsShopToggleable(string shop) public bool IsShopToggleable(string shop)
@ -91,35 +60,17 @@ namespace Props.Models
{ {
return false; return false;
} }
SearchOutline other = (SearchOutline) obj; SearchOutline other = (SearchOutline)obj;
return return
Id == other.Id && Id == other.Id &&
Currency == other.Currency && MaxResults == other.MaxResults &&
MaxResults == other.MaxResults && Filters.Equals(other.Filters) &&
MinRating == other.MinRating &&
KeepUnrated == other.KeepUnrated &&
EnableUpperPrice == other.EnableUpperPrice &&
UpperPrice == other.UpperPrice &&
LowerPrice == other.LowerPrice &&
MinPurchases == other.MinPurchases &&
KeepUnknownPurchaseCount == other.KeepUnknownPurchaseCount &&
MinReviews == other.MinReviews &&
KeepUnknownRatingCount == other.KeepUnknownRatingCount &&
EnableMaxShippingFee == other.EnableMaxShippingFee &&
MaxShippingFee == other.MaxShippingFee &&
KeepUnknownShipping == other.KeepUnknownShipping &&
ShopStates.Equals(other.ShopStates); ShopStates.Equals(other.ShopStates);
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return Id; return Id;
} }
public SearchOutline DeepCopy() {
SearchOutline profile = (SearchOutline)MemberwiseClone();
profile.ShopStates = ShopStates.Clone();
return profile;
}
} }
} }

View File

@ -11,7 +11,6 @@ namespace Props
{ {
public class Program public class Program
{ {
// TODO: Rename project to Props
public static void Main(string[] args) public static void Main(string[] args)
{ {
CreateHostBuilder(args).Build().Run(); CreateHostBuilder(args).Build().Run();