Forráskód Böngészése

Merge pull request #5623 from barronpm/ef-cleanup2

EF Core Cleanup 2
Bill Thornton 4 éve
szülő
commit
77f0f89e45
47 módosított fájl, 1056 hozzáadás és 271 törlés
  1. 4 4
      Jellyfin.Data/Entities/AccessSchedule.cs
  2. 3 4
      Jellyfin.Data/Entities/ActivityLog.cs
  3. 2 2
      Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs
  4. 5 7
      Jellyfin.Data/Entities/DisplayPreferences.cs
  5. 7 9
      Jellyfin.Data/Entities/Group.cs
  6. 2 2
      Jellyfin.Data/Entities/HomeSection.cs
  7. 4 4
      Jellyfin.Data/Entities/ImageInfo.cs
  8. 2 2
      Jellyfin.Data/Entities/ItemDisplayPreferences.cs
  9. 3 5
      Jellyfin.Data/Entities/Libraries/Artwork.cs
  10. 3 5
      Jellyfin.Data/Entities/Libraries/Book.cs
  11. 2 4
      Jellyfin.Data/Entities/Libraries/BookMetadata.cs
  12. 3 5
      Jellyfin.Data/Entities/Libraries/Chapter.cs
  13. 5 6
      Jellyfin.Data/Entities/Libraries/Collection.cs
  14. 1 1
      Jellyfin.Data/Entities/Libraries/CollectionItem.cs
  15. 7 9
      Jellyfin.Data/Entities/Libraries/Company.cs
  16. 3 5
      Jellyfin.Data/Entities/Libraries/CustomItem.cs
  17. 3 5
      Jellyfin.Data/Entities/Libraries/Episode.cs
  18. 3 3
      Jellyfin.Data/Entities/Libraries/Genre.cs
  19. 15 22
      Jellyfin.Data/Entities/Libraries/ItemMetadata.cs
  20. 3 3
      Jellyfin.Data/Entities/Libraries/Library.cs
  21. 5 5
      Jellyfin.Data/Entities/Libraries/LibraryItem.cs
  22. 5 7
      Jellyfin.Data/Entities/Libraries/MediaFile.cs
  23. 3 3
      Jellyfin.Data/Entities/Libraries/MediaFileStream.cs
  24. 3 3
      Jellyfin.Data/Entities/Libraries/MetadataProvider.cs
  25. 3 3
      Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs
  26. 3 5
      Jellyfin.Data/Entities/Libraries/Movie.cs
  27. 2 4
      Jellyfin.Data/Entities/Libraries/MovieMetadata.cs
  28. 4 6
      Jellyfin.Data/Entities/Libraries/MusicAlbum.cs
  29. 2 4
      Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs
  30. 7 9
      Jellyfin.Data/Entities/Libraries/Person.cs
  31. 6 8
      Jellyfin.Data/Entities/Libraries/PersonRole.cs
  32. 3 5
      Jellyfin.Data/Entities/Libraries/Photo.cs
  33. 3 3
      Jellyfin.Data/Entities/Libraries/Rating.cs
  34. 3 3
      Jellyfin.Data/Entities/Libraries/RatingSource.cs
  35. 7 9
      Jellyfin.Data/Entities/Libraries/Release.cs
  36. 4 6
      Jellyfin.Data/Entities/Libraries/Season.cs
  37. 4 7
      Jellyfin.Data/Entities/Libraries/Series.cs
  38. 2 4
      Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs
  39. 3 5
      Jellyfin.Data/Entities/Libraries/Track.cs
  40. 11 5
      Jellyfin.Data/Entities/Permission.cs
  41. 10 5
      Jellyfin.Data/Entities/Preference.cs
  42. 16 28
      Jellyfin.Data/Entities/User.cs
  43. 62 6
      Jellyfin.Server.Implementations/JellyfinDb.cs
  44. 535 0
      Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.Designer.cs
  45. 240 0
      Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.cs
  46. 23 10
      Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
  47. 7 11
      Jellyfin.Server.Implementations/Users/UserManager.cs

+ 4 - 4
Jellyfin.Data/Entities/AccessSchedule.cs

@@ -26,20 +26,20 @@ namespace Jellyfin.Data.Entities
         }
 
         /// <summary>
-        /// Gets or sets the id of this instance.
+        /// Gets the id of this instance.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [XmlIgnore]
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
-        /// Gets or sets the id of the associated user.
+        /// Gets the id of the associated user.
         /// </summary>
         [XmlIgnore]
-        public Guid UserId { get; protected set; }
+        public Guid UserId { get; private set; }
 
         /// <summary>
         /// Gets or sets the day of week.

+ 3 - 4
Jellyfin.Data/Entities/ActivityLog.cs

@@ -38,11 +38,10 @@ namespace Jellyfin.Data.Entities
         }
 
         /// <summary>
-        /// Gets or sets the identity of this instance.
-        /// This is the key in the backing database.
+        /// Gets the identity of this instance.
         /// </summary>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the name.
@@ -120,7 +119,7 @@ namespace Jellyfin.Data.Entities
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; set; }
+        public uint RowVersion { get; private set; }
 
         /// <inheritdoc />
         public void OnSavingChanges()

+ 2 - 2
Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs

@@ -27,13 +27,13 @@ namespace Jellyfin.Data.Entities
         }
 
         /// <summary>
-        /// Gets or sets the Id.
+        /// Gets the Id.
         /// </summary>
         /// <remarks>
         /// Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the user Id.

+ 5 - 7
Jellyfin.Data/Entities/DisplayPreferences.cs

@@ -1,6 +1,4 @@
-#pragma warning disable CA2227
-
-using System;
+using System;
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
 using System.ComponentModel.DataAnnotations.Schema;
@@ -35,13 +33,13 @@ namespace Jellyfin.Data.Entities
         }
 
         /// <summary>
-        /// Gets or sets the Id.
+        /// Gets the Id.
         /// </summary>
         /// <remarks>
         /// Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the user Id.
@@ -145,8 +143,8 @@ namespace Jellyfin.Data.Entities
         public string? TvHome { get; set; }
 
         /// <summary>
-        /// Gets or sets the home sections.
+        /// Gets the home sections.
         /// </summary>
-        public virtual ICollection<HomeSection> HomeSections { get; protected set; }
+        public virtual ICollection<HomeSection> HomeSections { get; private set; }
     }
 }

+ 7 - 9
Jellyfin.Data/Entities/Group.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System;
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
@@ -33,12 +31,12 @@ namespace Jellyfin.Data.Entities
         }
 
         /// <summary>
-        /// Gets or sets the id of this group.
+        /// Gets the id of this group.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
-        public Guid Id { get; protected set; }
+        public Guid Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the group's name.
@@ -52,17 +50,17 @@ namespace Jellyfin.Data.Entities
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; set; }
+        public uint RowVersion { get; private set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the group's permissions.
+        /// Gets a collection containing the group's permissions.
         /// </summary>
-        public virtual ICollection<Permission> Permissions { get; protected set; }
+        public virtual ICollection<Permission> Permissions { get; private set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the group's preferences.
+        /// Gets a collection containing the group's preferences.
         /// </summary>
-        public virtual ICollection<Preference> Preferences { get; protected set; }
+        public virtual ICollection<Preference> Preferences { get; private set; }
 
         /// <inheritdoc/>
         public bool HasPermission(PermissionKind kind)

+ 2 - 2
Jellyfin.Data/Entities/HomeSection.cs

@@ -9,13 +9,13 @@ namespace Jellyfin.Data.Entities
     public class HomeSection
     {
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Identity. Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the Id of the associated display preferences.

+ 4 - 4
Jellyfin.Data/Entities/ImageInfo.cs

@@ -20,18 +20,18 @@ namespace Jellyfin.Data.Entities
         }
 
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
-        /// Gets or sets the user id.
+        /// Gets the user id.
         /// </summary>
-        public Guid? UserId { get; protected set; }
+        public Guid? UserId { get; private set; }
 
         /// <summary>
         /// Gets or sets the path of the image.

+ 2 - 2
Jellyfin.Data/Entities/ItemDisplayPreferences.cs

@@ -29,13 +29,13 @@ namespace Jellyfin.Data.Entities
         }
 
         /// <summary>
-        /// Gets or sets the Id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the user Id.

+ 3 - 5
Jellyfin.Data/Entities/Libraries/Artwork.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System;
 using System.ComponentModel.DataAnnotations;
 using System.ComponentModel.DataAnnotations.Schema;
@@ -30,13 +28,13 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the path.
@@ -58,7 +56,7 @@ namespace Jellyfin.Data.Entities.Libraries
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; set; }
+        public uint RowVersion { get; private set; }
 
         /// <inheritdoc />
         public void OnSavingChanges()

+ 3 - 5
Jellyfin.Data/Entities/Libraries/Book.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System.Collections.Generic;
 using Jellyfin.Data.Interfaces;
 
@@ -21,11 +19,11 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets a collection containing the metadata for this book.
+        /// Gets a collection containing the metadata for this book.
         /// </summary>
-        public virtual ICollection<BookMetadata> BookMetadata { get; protected set; }
+        public virtual ICollection<BookMetadata> BookMetadata { get; private set; }
 
         /// <inheritdoc />
-        public virtual ICollection<Release> Releases { get; protected set; }
+        public virtual ICollection<Release> Releases { get; private set; }
     }
 }

+ 2 - 4
Jellyfin.Data/Entities/Libraries/BookMetadata.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System.Collections.Generic;
 using Jellyfin.Data.Interfaces;
 
@@ -26,9 +24,9 @@ namespace Jellyfin.Data.Entities.Libraries
         public long? Isbn { get; set; }
 
         /// <summary>
-        /// Gets or sets a collection of the publishers for this book.
+        /// Gets a collection of the publishers for this book.
         /// </summary>
-        public virtual ICollection<Company> Publishers { get; protected set; }
+        public virtual ICollection<Company> Publishers { get; private set; }
 
         /// <inheritdoc />
         public ICollection<Company> Companies => Publishers;

+ 3 - 5
Jellyfin.Data/Entities/Libraries/Chapter.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System;
 using System.ComponentModel.DataAnnotations;
 using System.ComponentModel.DataAnnotations.Schema;
@@ -29,13 +27,13 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the name.
@@ -74,7 +72,7 @@ namespace Jellyfin.Data.Entities.Libraries
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; protected set; }
+        public uint RowVersion { get; private set; }
 
         /// <inheritdoc />
         public void OnSavingChanges()

+ 5 - 6
Jellyfin.Data/Entities/Libraries/Collection.cs

@@ -1,5 +1,4 @@
 #pragma warning disable CA1711 // Identifiers should not have incorrect suffix
-#pragma warning disable CA2227
 
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
@@ -22,13 +21,13 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the name.
@@ -42,12 +41,12 @@ namespace Jellyfin.Data.Entities.Libraries
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; set; }
+        public uint RowVersion { get; private set; }
 
         /// <summary>
-        /// Gets or sets a collection containing this collection's items.
+        /// Gets a collection containing this collection's items.
         /// </summary>
-        public virtual ICollection<CollectionItem> Items { get; protected set; }
+        public virtual ICollection<CollectionItem> Items { get; private set; }
 
         /// <inheritdoc />
         public void OnSavingChanges()

+ 1 - 1
Jellyfin.Data/Entities/Libraries/CollectionItem.cs

@@ -29,7 +29,7 @@ namespace Jellyfin.Data.Entities.Libraries
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; set; }
+        public uint RowVersion { get; private set; }
 
         /// <summary>
         /// Gets or sets the library item.

+ 7 - 9
Jellyfin.Data/Entities/Libraries/Company.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
 using System.ComponentModel.DataAnnotations.Schema;
@@ -22,27 +20,27 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; set; }
+        public uint RowVersion { get; private set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the metadata.
+        /// Gets a collection containing the metadata.
         /// </summary>
-        public virtual ICollection<CompanyMetadata> CompanyMetadata { get; protected set; }
+        public virtual ICollection<CompanyMetadata> CompanyMetadata { get; private set; }
 
         /// <summary>
-        /// Gets or sets a collection containing this company's child companies.
+        /// Gets a collection containing this company's child companies.
         /// </summary>
-        public virtual ICollection<Company> ChildCompanies { get; protected set; }
+        public virtual ICollection<Company> ChildCompanies { get; private set; }
 
         /// <inheritdoc />
         public ICollection<Company> Companies => ChildCompanies;

+ 3 - 5
Jellyfin.Data/Entities/Libraries/CustomItem.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System.Collections.Generic;
 using Jellyfin.Data.Interfaces;
 
@@ -21,11 +19,11 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets a collection containing the metadata for this item.
+        /// Gets a collection containing the metadata for this item.
         /// </summary>
-        public virtual ICollection<CustomItemMetadata> CustomItemMetadata { get; protected set; }
+        public virtual ICollection<CustomItemMetadata> CustomItemMetadata { get; private set; }
 
         /// <inheritdoc />
-        public virtual ICollection<Release> Releases { get; protected set; }
+        public virtual ICollection<Release> Releases { get; private set; }
     }
 }

+ 3 - 5
Jellyfin.Data/Entities/Libraries/Episode.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System.Collections.Generic;
 using Jellyfin.Data.Interfaces;
 
@@ -26,11 +24,11 @@ namespace Jellyfin.Data.Entities.Libraries
         public int? EpisodeNumber { get; set; }
 
         /// <inheritdoc />
-        public virtual ICollection<Release> Releases { get; protected set; }
+        public virtual ICollection<Release> Releases { get; private set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the metadata for this episode.
+        /// Gets a collection containing the metadata for this episode.
         /// </summary>
-        public virtual ICollection<EpisodeMetadata> EpisodeMetadata { get; protected set; }
+        public virtual ICollection<EpisodeMetadata> EpisodeMetadata { get; private set; }
     }
 }

+ 3 - 3
Jellyfin.Data/Entities/Libraries/Genre.cs

@@ -19,13 +19,13 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the name.
@@ -39,7 +39,7 @@ namespace Jellyfin.Data.Entities.Libraries
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; protected set; }
+        public uint RowVersion { get; private set; }
 
         /// <inheritdoc />
         public void OnSavingChanges()

+ 15 - 22
Jellyfin.Data/Entities/Libraries/ItemMetadata.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System;
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
@@ -43,13 +41,13 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the title.
@@ -99,12 +97,12 @@ namespace Jellyfin.Data.Entities.Libraries
         public DateTimeOffset? ReleaseDate { get; set; }
 
         /// <summary>
-        /// Gets or sets the date added.
+        /// Gets the date added.
         /// </summary>
         /// <remarks>
         /// Required.
         /// </remarks>
-        public DateTime DateAdded { get; protected set; }
+        public DateTime DateAdded { get; private set; }
 
         /// <summary>
         /// Gets or sets the date modified.
@@ -114,37 +112,32 @@ namespace Jellyfin.Data.Entities.Libraries
         /// </remarks>
         public DateTime DateModified { get; set; }
 
-        /// <summary>
-        /// Gets or sets the row version.
-        /// </summary>
-        /// <remarks>
-        /// Required, ConcurrencyToken.
-        /// </remarks>
+        /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; set; }
+        public uint RowVersion { get; private set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the person roles for this item.
+        /// Gets a collection containing the person roles for this item.
         /// </summary>
-        public virtual ICollection<PersonRole> PersonRoles { get; protected set; }
+        public virtual ICollection<PersonRole> PersonRoles { get; private set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the genres for this item.
+        /// Gets a collection containing the genres for this item.
         /// </summary>
-        public virtual ICollection<Genre> Genres { get; protected set; }
+        public virtual ICollection<Genre> Genres { get; private set; }
 
         /// <inheritdoc />
-        public virtual ICollection<Artwork> Artwork { get; protected set; }
+        public virtual ICollection<Artwork> Artwork { get; private set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the ratings for this item.
+        /// Gets a collection containing the ratings for this item.
         /// </summary>
-        public virtual ICollection<Rating> Ratings { get; protected set; }
+        public virtual ICollection<Rating> Ratings { get; private set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the metadata sources for this item.
+        /// Gets a collection containing the metadata sources for this item.
         /// </summary>
-        public virtual ICollection<MetadataProviderId> Sources { get; protected set; }
+        public virtual ICollection<MetadataProviderId> Sources { get; private set; }
 
         /// <inheritdoc />
         public void OnSavingChanges()

+ 3 - 3
Jellyfin.Data/Entities/Libraries/Library.cs

@@ -21,13 +21,13 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the name.
@@ -49,7 +49,7 @@ namespace Jellyfin.Data.Entities.Libraries
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; set; }
+        public uint RowVersion { get; private set; }
 
         /// <inheritdoc />
         public void OnSavingChanges()

+ 5 - 5
Jellyfin.Data/Entities/Libraries/LibraryItem.cs

@@ -21,22 +21,22 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
-        /// Gets or sets the date this library item was added.
+        /// Gets the date this library item was added.
         /// </summary>
-        public DateTime DateAdded { get; protected set; }
+        public DateTime DateAdded { get; private set; }
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; protected set; }
+        public uint RowVersion { get; private set; }
 
         /// <summary>
         /// Gets or sets the library of this item.

+ 5 - 7
Jellyfin.Data/Entities/Libraries/MediaFile.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System;
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
@@ -33,13 +31,13 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the path relative to the library root.
@@ -61,12 +59,12 @@ namespace Jellyfin.Data.Entities.Libraries
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; set; }
+        public uint RowVersion { get; private set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the streams in this file.
+        /// Gets a collection containing the streams in this file.
         /// </summary>
-        public virtual ICollection<MediaFileStream> MediaFileStreams { get; protected set; }
+        public virtual ICollection<MediaFileStream> MediaFileStreams { get; private set; }
 
         /// <inheritdoc />
         public void OnSavingChanges()

+ 3 - 3
Jellyfin.Data/Entities/Libraries/MediaFileStream.cs

@@ -21,13 +21,13 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the stream number.
@@ -39,7 +39,7 @@ namespace Jellyfin.Data.Entities.Libraries
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; set; }
+        public uint RowVersion { get; private set; }
 
         /// <inheritdoc />
         public void OnSavingChanges()

+ 3 - 3
Jellyfin.Data/Entities/Libraries/MetadataProvider.cs

@@ -25,13 +25,13 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the name.
@@ -45,7 +45,7 @@ namespace Jellyfin.Data.Entities.Libraries
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; set; }
+        public uint RowVersion { get; private set; }
 
         /// <inheritdoc />
         public void OnSavingChanges()

+ 3 - 3
Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs

@@ -27,13 +27,13 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the provider id.
@@ -47,7 +47,7 @@ namespace Jellyfin.Data.Entities.Libraries
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; set; }
+        public uint RowVersion { get; private set; }
 
         /// <summary>
         /// Gets or sets the metadata provider.

+ 3 - 5
Jellyfin.Data/Entities/Libraries/Movie.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System.Collections.Generic;
 using Jellyfin.Data.Interfaces;
 
@@ -21,11 +19,11 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <inheritdoc />
-        public virtual ICollection<Release> Releases { get; protected set; }
+        public virtual ICollection<Release> Releases { get; private set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the metadata for this movie.
+        /// Gets a collection containing the metadata for this movie.
         /// </summary>
-        public virtual ICollection<MovieMetadata> MovieMetadata { get; protected set; }
+        public virtual ICollection<MovieMetadata> MovieMetadata { get; private set; }
     }
 }

+ 2 - 4
Jellyfin.Data/Entities/Libraries/MovieMetadata.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
 using Jellyfin.Data.Interfaces;
@@ -62,9 +60,9 @@ namespace Jellyfin.Data.Entities.Libraries
         public string? Country { get; set; }
 
         /// <summary>
-        /// Gets or sets the studios that produced this movie.
+        /// Gets the studios that produced this movie.
         /// </summary>
-        public virtual ICollection<Company> Studios { get; protected set; }
+        public virtual ICollection<Company> Studios { get; private set; }
 
         /// <inheritdoc />
         public ICollection<Company> Companies => Studios;

+ 4 - 6
Jellyfin.Data/Entities/Libraries/MusicAlbum.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System.Collections.Generic;
 
 namespace Jellyfin.Data.Entities.Libraries
@@ -20,13 +18,13 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets a collection containing the album metadata.
+        /// Gets a collection containing the album metadata.
         /// </summary>
-        public virtual ICollection<MusicAlbumMetadata> MusicAlbumMetadata { get; protected set; }
+        public virtual ICollection<MusicAlbumMetadata> MusicAlbumMetadata { get; private set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the tracks.
+        /// Gets a collection containing the tracks.
         /// </summary>
-        public virtual ICollection<Track> Tracks { get; protected set; }
+        public virtual ICollection<Track> Tracks { get; private set; }
     }
 }

+ 2 - 4
Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
 
@@ -51,8 +49,8 @@ namespace Jellyfin.Data.Entities.Libraries
         public string? Country { get; set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the labels.
+        /// Gets a collection containing the labels.
         /// </summary>
-        public virtual ICollection<Company> Labels { get; protected set; }
+        public virtual ICollection<Company> Labels { get; private set; }
     }
 }

+ 7 - 9
Jellyfin.Data/Entities/Libraries/Person.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System;
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
@@ -32,13 +30,13 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the name.
@@ -61,12 +59,12 @@ namespace Jellyfin.Data.Entities.Libraries
         public string? SourceId { get; set; }
 
         /// <summary>
-        /// Gets or sets the date added.
+        /// Gets the date added.
         /// </summary>
         /// <remarks>
         /// Required.
         /// </remarks>
-        public DateTime DateAdded { get; protected set; }
+        public DateTime DateAdded { get; private set; }
 
         /// <summary>
         /// Gets or sets the date modified.
@@ -78,12 +76,12 @@ namespace Jellyfin.Data.Entities.Libraries
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; set; }
+        public uint RowVersion { get; private set; }
 
         /// <summary>
-        /// Gets or sets a list of metadata sources for this person.
+        /// Gets a list of metadata sources for this person.
         /// </summary>
-        public virtual ICollection<MetadataProviderId> Sources { get; protected set; }
+        public virtual ICollection<MetadataProviderId> Sources { get; private set; }
 
         /// <inheritdoc />
         public void OnSavingChanges()

+ 6 - 8
Jellyfin.Data/Entities/Libraries/PersonRole.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
 using System.ComponentModel.DataAnnotations.Schema;
@@ -27,13 +25,13 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the name of the person's role.
@@ -55,7 +53,7 @@ namespace Jellyfin.Data.Entities.Libraries
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; protected set; }
+        public uint RowVersion { get; private set; }
 
         /// <summary>
         /// Gets or sets the person.
@@ -66,12 +64,12 @@ namespace Jellyfin.Data.Entities.Libraries
         public virtual Person Person { get; set; }
 
         /// <inheritdoc />
-        public virtual ICollection<Artwork> Artwork { get; protected set; }
+        public virtual ICollection<Artwork> Artwork { get; private set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the metadata sources for this person role.
+        /// Gets a collection containing the metadata sources for this person role.
         /// </summary>
-        public virtual ICollection<MetadataProviderId> Sources { get; protected set; }
+        public virtual ICollection<MetadataProviderId> Sources { get; private set; }
 
         /// <inheritdoc />
         public void OnSavingChanges()

+ 3 - 5
Jellyfin.Data/Entities/Libraries/Photo.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System.Collections.Generic;
 using Jellyfin.Data.Interfaces;
 
@@ -21,11 +19,11 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets a collection containing the photo metadata.
+        /// Gets a collection containing the photo metadata.
         /// </summary>
-        public virtual ICollection<PhotoMetadata> PhotoMetadata { get; protected set; }
+        public virtual ICollection<PhotoMetadata> PhotoMetadata { get; private set; }
 
         /// <inheritdoc />
-        public virtual ICollection<Release> Releases { get; protected set; }
+        public virtual ICollection<Release> Releases { get; private set; }
     }
 }

+ 3 - 3
Jellyfin.Data/Entities/Libraries/Rating.cs

@@ -19,13 +19,13 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the value.
@@ -42,7 +42,7 @@ namespace Jellyfin.Data.Entities.Libraries
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; set; }
+        public uint RowVersion { get; private set; }
 
         /// <summary>
         /// Gets or sets the rating type.

+ 3 - 3
Jellyfin.Data/Entities/Libraries/RatingSource.cs

@@ -21,13 +21,13 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the name.
@@ -57,7 +57,7 @@ namespace Jellyfin.Data.Entities.Libraries
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; set; }
+        public uint RowVersion { get; private set; }
 
         /// <summary>
         /// Gets or sets the metadata source.

+ 7 - 9
Jellyfin.Data/Entities/Libraries/Release.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System;
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
@@ -31,13 +29,13 @@ namespace Jellyfin.Data.Entities.Libraries
         }
 
         /// <summary>
-        /// Gets or sets the id.
+        /// Gets the id.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
         /// Gets or sets the name.
@@ -51,17 +49,17 @@ namespace Jellyfin.Data.Entities.Libraries
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; set; }
+        public uint RowVersion { get; private set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the media files for this release.
+        /// Gets a collection containing the media files for this release.
         /// </summary>
-        public virtual ICollection<MediaFile> MediaFiles { get; protected set; }
+        public virtual ICollection<MediaFile> MediaFiles { get; private set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the chapters for this release.
+        /// Gets a collection containing the chapters for this release.
         /// </summary>
-        public virtual ICollection<Chapter> Chapters { get; protected set; }
+        public virtual ICollection<Chapter> Chapters { get; private set; }
 
         /// <inheritdoc />
         public void OnSavingChanges()

+ 4 - 6
Jellyfin.Data/Entities/Libraries/Season.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System.Collections.Generic;
 
 namespace Jellyfin.Data.Entities.Libraries
@@ -25,13 +23,13 @@ namespace Jellyfin.Data.Entities.Libraries
         public int? SeasonNumber { get; set; }
 
         /// <summary>
-        /// Gets or sets the season metadata.
+        /// Gets the season metadata.
         /// </summary>
-        public virtual ICollection<SeasonMetadata> SeasonMetadata { get; protected set; }
+        public virtual ICollection<SeasonMetadata> SeasonMetadata { get; private set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the number of episodes.
+        /// Gets a collection containing the number of episodes.
         /// </summary>
-        public virtual ICollection<Episode> Episodes { get; protected set; }
+        public virtual ICollection<Episode> Episodes { get; private set; }
     }
 }

+ 4 - 7
Jellyfin.Data/Entities/Libraries/Series.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System;
 using System.Collections.Generic;
 
@@ -16,7 +14,6 @@ namespace Jellyfin.Data.Entities.Libraries
         /// <param name="library">The library.</param>
         public Series(Library library) : base(library)
         {
-            DateAdded = DateTime.UtcNow;
             Seasons = new HashSet<Season>();
             SeriesMetadata = new HashSet<SeriesMetadata>();
         }
@@ -37,13 +34,13 @@ namespace Jellyfin.Data.Entities.Libraries
         public DateTime? FirstAired { get; set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the series metadata.
+        /// Gets a collection containing the series metadata.
         /// </summary>
-        public virtual ICollection<SeriesMetadata> SeriesMetadata { get; protected set; }
+        public virtual ICollection<SeriesMetadata> SeriesMetadata { get; private set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the seasons.
+        /// Gets a collection containing the seasons.
         /// </summary>
-        public virtual ICollection<Season> Seasons { get; protected set; }
+        public virtual ICollection<Season> Seasons { get; private set; }
     }
 }

+ 2 - 4
Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
 using Jellyfin.Data.Interfaces;
@@ -62,9 +60,9 @@ namespace Jellyfin.Data.Entities.Libraries
         public string? Country { get; set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the networks.
+        /// Gets a collection containing the networks.
         /// </summary>
-        public virtual ICollection<Company> Networks { get; protected set; }
+        public virtual ICollection<Company> Networks { get; private set; }
 
         /// <inheritdoc />
         public ICollection<Company> Companies => Networks;

+ 3 - 5
Jellyfin.Data/Entities/Libraries/Track.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System.Collections.Generic;
 using Jellyfin.Data.Interfaces;
 
@@ -26,11 +24,11 @@ namespace Jellyfin.Data.Entities.Libraries
         public int? TrackNumber { get; set; }
 
         /// <inheritdoc />
-        public virtual ICollection<Release> Releases { get; protected set; }
+        public virtual ICollection<Release> Releases { get; private set; }
 
         /// <summary>
-        /// Gets or sets a collection containing the track metadata.
+        /// Gets a collection containing the track metadata.
         /// </summary>
-        public virtual ICollection<TrackMetadata> TrackMetadata { get; protected set; }
+        public virtual ICollection<TrackMetadata> TrackMetadata { get; private set; }
     }
 }

+ 11 - 5
Jellyfin.Data/Entities/Permission.cs

@@ -1,5 +1,6 @@
 #pragma warning disable CA1711 // Identifiers should not have incorrect suffix
 
+using System;
 using System.ComponentModel.DataAnnotations;
 using System.ComponentModel.DataAnnotations.Schema;
 using Jellyfin.Data.Enums;
@@ -25,21 +26,26 @@ namespace Jellyfin.Data.Entities
         }
 
         /// <summary>
-        /// Gets or sets the id of this permission.
+        /// Gets the id of this permission.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
-        /// Gets or sets the type of this permission.
+        /// Gets or sets the id of the associated user.
+        /// </summary>
+        public Guid? UserId { get; set; }
+
+        /// <summary>
+        /// Gets the type of this permission.
         /// </summary>
         /// <remarks>
         /// Required.
         /// </remarks>
-        public PermissionKind Kind { get; protected set; }
+        public PermissionKind Kind { get; private set; }
 
         /// <summary>
         /// Gets or sets a value indicating whether the associated user has this permission.
@@ -51,7 +57,7 @@ namespace Jellyfin.Data.Entities
 
         /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; set; }
+        public uint RowVersion { get; private set; }
 
         /// <inheritdoc/>
         public void OnSavingChanges()

+ 10 - 5
Jellyfin.Data/Entities/Preference.cs

@@ -24,21 +24,26 @@ namespace Jellyfin.Data.Entities
         }
 
         /// <summary>
-        /// Gets or sets the id of this preference.
+        /// Gets the id of this preference.
         /// </summary>
         /// <remarks>
         /// Identity, Indexed, Required.
         /// </remarks>
         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
-        public int Id { get; protected set; }
+        public int Id { get; private set; }
 
         /// <summary>
-        /// Gets or sets the type of this preference.
+        /// Gets or sets the id of the associated user.
+        /// </summary>
+        public Guid? UserId { get; set; }
+
+        /// <summary>
+        /// Gets the type of this preference.
         /// </summary>
         /// <remarks>
         /// Required.
         /// </remarks>
-        public PreferenceKind Kind { get; protected set; }
+        public PreferenceKind Kind { get; private set; }
 
         /// <summary>
         /// Gets or sets the value of this preference.
@@ -52,7 +57,7 @@ namespace Jellyfin.Data.Entities
 
         /// <inheritdoc/>
         [ConcurrencyCheck]
-        public uint RowVersion { get; set; }
+        public uint RowVersion { get; private set; }
 
         /// <inheritdoc/>
         public void OnSavingChanges()

+ 16 - 28
Jellyfin.Data/Entities/User.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CA2227
-
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
@@ -302,64 +300,54 @@ namespace Jellyfin.Data.Entities
         public virtual ImageInfo? ProfileImage { get; set; }
 
         /// <summary>
-        /// Gets or sets the user's display preferences.
+        /// Gets the user's display preferences.
         /// </summary>
-        /// <remarks>
-        /// Required.
-        /// </remarks>
-        public virtual ICollection<DisplayPreferences> DisplayPreferences { get; set; }
+        public virtual ICollection<DisplayPreferences> DisplayPreferences { get; private set; }
 
         /// <summary>
         /// Gets or sets the level of sync play permissions this user has.
         /// </summary>
         public SyncPlayUserAccessType SyncPlayAccess { get; set; }
 
-        /// <summary>
-        /// Gets or sets the row version.
-        /// </summary>
-        /// <remarks>
-        /// Required, Concurrency Token.
-        /// </remarks>
+        /// <inheritdoc />
         [ConcurrencyCheck]
-        public uint RowVersion { get; set; }
+        public uint RowVersion { get; private set; }
 
         /// <summary>
-        /// Gets or sets the list of access schedules this user has.
+        /// Gets the list of access schedules this user has.
         /// </summary>
-        public virtual ICollection<AccessSchedule> AccessSchedules { get; protected set; }
+        public virtual ICollection<AccessSchedule> AccessSchedules { get; private set; }
 
         /// <summary>
-        /// Gets or sets the list of item display preferences.
+        /// Gets the list of item display preferences.
         /// </summary>
-        public virtual ICollection<ItemDisplayPreferences> ItemDisplayPreferences { get; protected set; }
+        public virtual ICollection<ItemDisplayPreferences> ItemDisplayPreferences { get; private set; }
 
         /*
         /// <summary>
-        /// Gets or sets the list of groups this user is a member of.
+        /// Gets the list of groups this user is a member of.
         /// </summary>
-        [ForeignKey("Group_Groups_Guid")]
-        public virtual ICollection<Group> Groups { get; protected set; }
+        public virtual ICollection<Group> Groups { get; private set; }
         */
 
         /// <summary>
-        /// Gets or sets the list of permissions this user has.
+        /// Gets the list of permissions this user has.
         /// </summary>
         [ForeignKey("Permission_Permissions_Guid")]
-        public virtual ICollection<Permission> Permissions { get; protected set; }
+        public virtual ICollection<Permission> Permissions { get; private set; }
 
         /*
         /// <summary>
-        /// Gets or sets the list of provider mappings this user has.
+        /// Gets the list of provider mappings this user has.
         /// </summary>
-        [ForeignKey("ProviderMapping_ProviderMappings_Id")]
-        public virtual ICollection<ProviderMapping> ProviderMappings { get; protected set; }
+        public virtual ICollection<ProviderMapping> ProviderMappings { get; private set; }
         */
 
         /// <summary>
-        /// Gets or sets the list of preferences this user has.
+        /// Gets the list of preferences this user has.
         /// </summary>
         [ForeignKey("Preference_Preferences_Guid")]
-        public virtual ICollection<Preference> Preferences { get; protected set; }
+        public virtual ICollection<Preference> Preferences { get; private set; }
 
         /// <inheritdoc/>
         public void OnSavingChanges()

+ 62 - 6
Jellyfin.Server.Implementations/JellyfinDb.cs

@@ -149,21 +149,77 @@ namespace Jellyfin.Server.Implementations
 
             modelBuilder.HasDefaultSchema("jellyfin");
 
+            // Collations
+
+            modelBuilder.Entity<User>()
+                .Property(user => user.Username)
+                .UseCollation("NOCASE");
+
+            // Delete behavior
+
+            modelBuilder.Entity<User>()
+                .HasOne(u => u.ProfileImage)
+                .WithOne()
+                .OnDelete(DeleteBehavior.Cascade);
+
+            modelBuilder.Entity<User>()
+                .HasMany(u => u.Permissions)
+                .WithOne()
+                .HasForeignKey(p => p.UserId)
+                .OnDelete(DeleteBehavior.Cascade);
+
+            modelBuilder.Entity<User>()
+                .HasMany(u => u.Preferences)
+                .WithOne()
+                .HasForeignKey(p => p.UserId)
+                .OnDelete(DeleteBehavior.Cascade);
+
+            modelBuilder.Entity<User>()
+                .HasMany(u => u.AccessSchedules)
+                .WithOne()
+                .OnDelete(DeleteBehavior.Cascade);
+
+            modelBuilder.Entity<User>()
+                .HasMany(u => u.DisplayPreferences)
+                .WithOne()
+                .OnDelete(DeleteBehavior.Cascade);
+
+            modelBuilder.Entity<User>()
+                .HasMany(u => u.ItemDisplayPreferences)
+                .WithOne()
+                .OnDelete(DeleteBehavior.Cascade);
+
             modelBuilder.Entity<DisplayPreferences>()
-                .HasIndex(entity => entity.UserId)
-                .IsUnique(false);
+                .HasMany(d => d.HomeSections)
+                .WithOne()
+                .OnDelete(DeleteBehavior.Cascade);
+
+            // Indexes
+
+            modelBuilder.Entity<User>()
+                .HasIndex(entity => entity.Username)
+                .IsUnique();
 
             modelBuilder.Entity<DisplayPreferences>()
                 .HasIndex(entity => new { entity.UserId, entity.ItemId, entity.Client })
                 .IsUnique();
 
-            modelBuilder.Entity<CustomItemDisplayPreferences>()
-                .HasIndex(entity => entity.UserId)
-                .IsUnique(false);
-
             modelBuilder.Entity<CustomItemDisplayPreferences>()
                 .HasIndex(entity => new { entity.UserId, entity.ItemId, entity.Client, entity.Key })
                 .IsUnique();
+
+            // Used to get a user's permissions or a specific permission for a user.
+            // Also prevents multiple values being created for a user.
+            // Filtered over non-null user ids for when other entities (groups, API keys) get permissions
+            modelBuilder.Entity<Permission>()
+                .HasIndex(p => new { p.UserId, p.Kind })
+                .HasFilter("[UserId] IS NOT NULL")
+                .IsUnique();
+
+            modelBuilder.Entity<Preference>()
+                .HasIndex(p => new { p.UserId, p.Kind })
+                .HasFilter("[UserId] IS NOT NULL")
+                .IsUnique();
         }
     }
 }

+ 535 - 0
Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.Designer.cs

@@ -0,0 +1,535 @@
+#pragma warning disable CS1591
+
+// <auto-generated />
+using System;
+using Jellyfin.Server.Implementations;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+namespace Jellyfin.Server.Implementations.Migrations
+{
+    [DbContext(typeof(JellyfinDb))]
+    [Migration("20210320181425_AddIndexesAndCollations")]
+    partial class AddIndexesAndCollations
+    {
+        protected override void BuildTargetModel(ModelBuilder modelBuilder)
+        {
+#pragma warning disable 612, 618
+            modelBuilder
+                .HasDefaultSchema("jellyfin")
+                .HasAnnotation("ProductVersion", "5.0.3");
+
+            modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("INTEGER");
+
+                    b.Property<int>("DayOfWeek")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<double>("EndHour")
+                        .HasColumnType("REAL");
+
+                    b.Property<double>("StartHour")
+                        .HasColumnType("REAL");
+
+                    b.Property<Guid>("UserId")
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("UserId");
+
+                    b.ToTable("AccessSchedules");
+                });
+
+            modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("INTEGER");
+
+                    b.Property<DateTime>("DateCreated")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ItemId")
+                        .HasMaxLength(256)
+                        .HasColumnType("TEXT");
+
+                    b.Property<int>("LogSeverity")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasMaxLength(512)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Overview")
+                        .HasMaxLength(512)
+                        .HasColumnType("TEXT");
+
+                    b.Property<uint>("RowVersion")
+                        .IsConcurrencyToken()
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("ShortOverview")
+                        .HasMaxLength(512)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Type")
+                        .IsRequired()
+                        .HasMaxLength(256)
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid>("UserId")
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("ActivityLogs");
+                });
+
+            modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemDisplayPreferences", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("Client")
+                        .IsRequired()
+                        .HasMaxLength(32)
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid>("ItemId")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Key")
+                        .IsRequired()
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid>("UserId")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Value")
+                        .IsRequired()
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("UserId", "ItemId", "Client", "Key")
+                        .IsUnique();
+
+                    b.ToTable("CustomItemDisplayPreferences");
+                });
+
+            modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("INTEGER");
+
+                    b.Property<int>("ChromecastVersion")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("Client")
+                        .IsRequired()
+                        .HasMaxLength(32)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("DashboardTheme")
+                        .HasMaxLength(32)
+                        .HasColumnType("TEXT");
+
+                    b.Property<bool>("EnableNextVideoInfoOverlay")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<int?>("IndexBy")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<Guid>("ItemId")
+                        .HasColumnType("TEXT");
+
+                    b.Property<int>("ScrollDirection")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<bool>("ShowBackdrop")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<bool>("ShowSidebar")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<int>("SkipBackwardLength")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<int>("SkipForwardLength")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("TvHome")
+                        .HasMaxLength(32)
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid>("UserId")
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("UserId", "ItemId", "Client")
+                        .IsUnique();
+
+                    b.ToTable("DisplayPreferences");
+                });
+
+            modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("INTEGER");
+
+                    b.Property<int>("DisplayPreferencesId")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<int>("Order")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<int>("Type")
+                        .HasColumnType("INTEGER");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("DisplayPreferencesId");
+
+                    b.ToTable("HomeSection");
+                });
+
+            modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("INTEGER");
+
+                    b.Property<DateTime>("LastModified")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Path")
+                        .IsRequired()
+                        .HasMaxLength(512)
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid?>("UserId")
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("UserId")
+                        .IsUnique();
+
+                    b.ToTable("ImageInfos");
+                });
+
+            modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("Client")
+                        .IsRequired()
+                        .HasMaxLength(32)
+                        .HasColumnType("TEXT");
+
+                    b.Property<int?>("IndexBy")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<Guid>("ItemId")
+                        .HasColumnType("TEXT");
+
+                    b.Property<bool>("RememberIndexing")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<bool>("RememberSorting")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("SortBy")
+                        .IsRequired()
+                        .HasMaxLength(64)
+                        .HasColumnType("TEXT");
+
+                    b.Property<int>("SortOrder")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<Guid>("UserId")
+                        .HasColumnType("TEXT");
+
+                    b.Property<int>("ViewType")
+                        .HasColumnType("INTEGER");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("UserId");
+
+                    b.ToTable("ItemDisplayPreferences");
+                });
+
+            modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("INTEGER");
+
+                    b.Property<int>("Kind")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<Guid?>("Permission_Permissions_Guid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<uint>("RowVersion")
+                        .IsConcurrencyToken()
+                        .HasColumnType("INTEGER");
+
+                    b.Property<Guid?>("UserId")
+                        .HasColumnType("TEXT");
+
+                    b.Property<bool>("Value")
+                        .HasColumnType("INTEGER");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("UserId", "Kind")
+                        .IsUnique()
+                        .HasFilter("[UserId] IS NOT NULL");
+
+                    b.ToTable("Permissions");
+                });
+
+            modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("INTEGER");
+
+                    b.Property<int>("Kind")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<Guid?>("Preference_Preferences_Guid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<uint>("RowVersion")
+                        .IsConcurrencyToken()
+                        .HasColumnType("INTEGER");
+
+                    b.Property<Guid?>("UserId")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Value")
+                        .IsRequired()
+                        .HasMaxLength(65535)
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("UserId", "Kind")
+                        .IsUnique()
+                        .HasFilter("[UserId] IS NOT NULL");
+
+                    b.ToTable("Preferences");
+                });
+
+            modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
+                {
+                    b.Property<Guid>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("AudioLanguagePreference")
+                        .HasMaxLength(255)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("AuthenticationProviderId")
+                        .IsRequired()
+                        .HasMaxLength(255)
+                        .HasColumnType("TEXT");
+
+                    b.Property<bool>("DisplayCollectionsView")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<bool>("DisplayMissingEpisodes")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("EasyPassword")
+                        .HasMaxLength(65535)
+                        .HasColumnType("TEXT");
+
+                    b.Property<bool>("EnableAutoLogin")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<bool>("EnableLocalPassword")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<bool>("EnableNextEpisodeAutoPlay")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<bool>("EnableUserPreferenceAccess")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<bool>("HidePlayedInLatest")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<long>("InternalId")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<int>("InvalidLoginAttemptCount")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<DateTime?>("LastActivityDate")
+                        .HasColumnType("TEXT");
+
+                    b.Property<DateTime?>("LastLoginDate")
+                        .HasColumnType("TEXT");
+
+                    b.Property<int?>("LoginAttemptsBeforeLockout")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<int>("MaxActiveSessions")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<int?>("MaxParentalAgeRating")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<bool>("MustUpdatePassword")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("Password")
+                        .HasMaxLength(65535)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("PasswordResetProviderId")
+                        .IsRequired()
+                        .HasMaxLength(255)
+                        .HasColumnType("TEXT");
+
+                    b.Property<bool>("PlayDefaultAudioTrack")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<bool>("RememberAudioSelections")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<bool>("RememberSubtitleSelections")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<int?>("RemoteClientBitrateLimit")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<uint>("RowVersion")
+                        .IsConcurrencyToken()
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("SubtitleLanguagePreference")
+                        .HasMaxLength(255)
+                        .HasColumnType("TEXT");
+
+                    b.Property<int>("SubtitleMode")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<int>("SyncPlayAccess")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("Username")
+                        .IsRequired()
+                        .HasMaxLength(255)
+                        .HasColumnType("TEXT")
+                        .UseCollation("NOCASE");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("Username")
+                        .IsUnique();
+
+                    b.ToTable("Users");
+                });
+
+            modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
+                {
+                    b.HasOne("Jellyfin.Data.Entities.User", null)
+                        .WithMany("AccessSchedules")
+                        .HasForeignKey("UserId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
+                {
+                    b.HasOne("Jellyfin.Data.Entities.User", null)
+                        .WithMany("DisplayPreferences")
+                        .HasForeignKey("UserId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b =>
+                {
+                    b.HasOne("Jellyfin.Data.Entities.DisplayPreferences", null)
+                        .WithMany("HomeSections")
+                        .HasForeignKey("DisplayPreferencesId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b =>
+                {
+                    b.HasOne("Jellyfin.Data.Entities.User", null)
+                        .WithOne("ProfileImage")
+                        .HasForeignKey("Jellyfin.Data.Entities.ImageInfo", "UserId")
+                        .OnDelete(DeleteBehavior.Cascade);
+                });
+
+            modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b =>
+                {
+                    b.HasOne("Jellyfin.Data.Entities.User", null)
+                        .WithMany("ItemDisplayPreferences")
+                        .HasForeignKey("UserId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
+                {
+                    b.HasOne("Jellyfin.Data.Entities.User", null)
+                        .WithMany("Permissions")
+                        .HasForeignKey("UserId")
+                        .OnDelete(DeleteBehavior.Cascade);
+                });
+
+            modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
+                {
+                    b.HasOne("Jellyfin.Data.Entities.User", null)
+                        .WithMany("Preferences")
+                        .HasForeignKey("UserId")
+                        .OnDelete(DeleteBehavior.Cascade);
+                });
+
+            modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
+                {
+                    b.Navigation("HomeSections");
+                });
+
+            modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
+                {
+                    b.Navigation("AccessSchedules");
+
+                    b.Navigation("DisplayPreferences");
+
+                    b.Navigation("ItemDisplayPreferences");
+
+                    b.Navigation("Permissions");
+
+                    b.Navigation("Preferences");
+
+                    b.Navigation("ProfileImage");
+                });
+#pragma warning restore 612, 618
+        }
+    }
+}

+ 240 - 0
Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.cs

@@ -0,0 +1,240 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1601
+
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace Jellyfin.Server.Implementations.Migrations
+{
+    public partial class AddIndexesAndCollations : Migration
+    {
+        protected override void Up(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.DropForeignKey(
+                name: "FK_ImageInfos_Users_UserId",
+                schema: "jellyfin",
+                table: "ImageInfos");
+
+            migrationBuilder.DropForeignKey(
+                name: "FK_Permissions_Users_Permission_Permissions_Guid",
+                schema: "jellyfin",
+                table: "Permissions");
+
+            migrationBuilder.DropForeignKey(
+                name: "FK_Preferences_Users_Preference_Preferences_Guid",
+                schema: "jellyfin",
+                table: "Preferences");
+
+            migrationBuilder.DropIndex(
+                name: "IX_Preferences_Preference_Preferences_Guid",
+                schema: "jellyfin",
+                table: "Preferences");
+
+            migrationBuilder.DropIndex(
+                name: "IX_Permissions_Permission_Permissions_Guid",
+                schema: "jellyfin",
+                table: "Permissions");
+
+            migrationBuilder.DropIndex(
+                name: "IX_DisplayPreferences_UserId",
+                schema: "jellyfin",
+                table: "DisplayPreferences");
+
+            migrationBuilder.DropIndex(
+                name: "IX_CustomItemDisplayPreferences_UserId",
+                schema: "jellyfin",
+                table: "CustomItemDisplayPreferences");
+
+            migrationBuilder.AlterColumn<string>(
+                name: "Username",
+                schema: "jellyfin",
+                table: "Users",
+                type: "TEXT",
+                maxLength: 255,
+                nullable: false,
+                collation: "NOCASE",
+                oldClrType: typeof(string),
+                oldType: "TEXT",
+                oldMaxLength: 255);
+
+            migrationBuilder.AddColumn<Guid>(
+                name: "UserId",
+                schema: "jellyfin",
+                table: "Preferences",
+                type: "TEXT",
+                nullable: true);
+
+            migrationBuilder.AddColumn<Guid>(
+                name: "UserId",
+                schema: "jellyfin",
+                table: "Permissions",
+                type: "TEXT",
+                nullable: true);
+
+            migrationBuilder.Sql("UPDATE Preferences SET UserId = Preference_Preferences_Guid");
+            migrationBuilder.Sql("UPDATE Permissions SET UserId = Permission_Permissions_Guid");
+
+            migrationBuilder.CreateIndex(
+                name: "IX_Users_Username",
+                schema: "jellyfin",
+                table: "Users",
+                column: "Username",
+                unique: true);
+
+            migrationBuilder.CreateIndex(
+                name: "IX_Preferences_UserId_Kind",
+                schema: "jellyfin",
+                table: "Preferences",
+                columns: new[] { "UserId", "Kind" },
+                unique: true,
+                filter: "[UserId] IS NOT NULL");
+
+            migrationBuilder.CreateIndex(
+                name: "IX_Permissions_UserId_Kind",
+                schema: "jellyfin",
+                table: "Permissions",
+                columns: new[] { "UserId", "Kind" },
+                unique: true,
+                filter: "[UserId] IS NOT NULL");
+
+            migrationBuilder.AddForeignKey(
+                name: "FK_ImageInfos_Users_UserId",
+                schema: "jellyfin",
+                table: "ImageInfos",
+                column: "UserId",
+                principalSchema: "jellyfin",
+                principalTable: "Users",
+                principalColumn: "Id",
+                onDelete: ReferentialAction.Cascade);
+
+            migrationBuilder.AddForeignKey(
+                name: "FK_Permissions_Users_UserId",
+                schema: "jellyfin",
+                table: "Permissions",
+                column: "UserId",
+                principalSchema: "jellyfin",
+                principalTable: "Users",
+                principalColumn: "Id",
+                onDelete: ReferentialAction.Cascade);
+
+            migrationBuilder.AddForeignKey(
+                name: "FK_Preferences_Users_UserId",
+                schema: "jellyfin",
+                table: "Preferences",
+                column: "UserId",
+                principalSchema: "jellyfin",
+                principalTable: "Users",
+                principalColumn: "Id",
+                onDelete: ReferentialAction.Cascade);
+        }
+
+        protected override void Down(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.DropForeignKey(
+                name: "FK_ImageInfos_Users_UserId",
+                schema: "jellyfin",
+                table: "ImageInfos");
+
+            migrationBuilder.DropForeignKey(
+                name: "FK_Permissions_Users_UserId",
+                schema: "jellyfin",
+                table: "Permissions");
+
+            migrationBuilder.DropForeignKey(
+                name: "FK_Preferences_Users_UserId",
+                schema: "jellyfin",
+                table: "Preferences");
+
+            migrationBuilder.DropIndex(
+                name: "IX_Users_Username",
+                schema: "jellyfin",
+                table: "Users");
+
+            migrationBuilder.DropIndex(
+                name: "IX_Preferences_UserId_Kind",
+                schema: "jellyfin",
+                table: "Preferences");
+
+            migrationBuilder.DropIndex(
+                name: "IX_Permissions_UserId_Kind",
+                schema: "jellyfin",
+                table: "Permissions");
+
+            migrationBuilder.DropColumn(
+                name: "UserId",
+                schema: "jellyfin",
+                table: "Preferences");
+
+            migrationBuilder.DropColumn(
+                name: "UserId",
+                schema: "jellyfin",
+                table: "Permissions");
+
+            migrationBuilder.AlterColumn<string>(
+                name: "Username",
+                schema: "jellyfin",
+                table: "Users",
+                type: "TEXT",
+                maxLength: 255,
+                nullable: false,
+                oldClrType: typeof(string),
+                oldType: "TEXT",
+                oldMaxLength: 255,
+                oldCollation: "NOCASE");
+
+            migrationBuilder.CreateIndex(
+                name: "IX_Preferences_Preference_Preferences_Guid",
+                schema: "jellyfin",
+                table: "Preferences",
+                column: "Preference_Preferences_Guid");
+
+            migrationBuilder.CreateIndex(
+                name: "IX_Permissions_Permission_Permissions_Guid",
+                schema: "jellyfin",
+                table: "Permissions",
+                column: "Permission_Permissions_Guid");
+
+            migrationBuilder.CreateIndex(
+                name: "IX_DisplayPreferences_UserId",
+                schema: "jellyfin",
+                table: "DisplayPreferences",
+                column: "UserId");
+
+            migrationBuilder.CreateIndex(
+                name: "IX_CustomItemDisplayPreferences_UserId",
+                schema: "jellyfin",
+                table: "CustomItemDisplayPreferences",
+                column: "UserId");
+
+            migrationBuilder.AddForeignKey(
+                name: "FK_ImageInfos_Users_UserId",
+                schema: "jellyfin",
+                table: "ImageInfos",
+                column: "UserId",
+                principalSchema: "jellyfin",
+                principalTable: "Users",
+                principalColumn: "Id",
+                onDelete: ReferentialAction.Restrict);
+
+            migrationBuilder.AddForeignKey(
+                name: "FK_Permissions_Users_Permission_Permissions_Guid",
+                schema: "jellyfin",
+                table: "Permissions",
+                column: "Permission_Permissions_Guid",
+                principalSchema: "jellyfin",
+                principalTable: "Users",
+                principalColumn: "Id",
+                onDelete: ReferentialAction.Restrict);
+
+            migrationBuilder.AddForeignKey(
+                name: "FK_Preferences_Users_Preference_Preferences_Guid",
+                schema: "jellyfin",
+                table: "Preferences",
+                column: "Preference_Preferences_Guid",
+                principalSchema: "jellyfin",
+                principalTable: "Users",
+                principalColumn: "Id",
+                onDelete: ReferentialAction.Restrict);
+        }
+    }
+}

+ 23 - 10
Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs

@@ -114,8 +114,6 @@ namespace Jellyfin.Server.Implementations.Migrations
 
                     b.HasKey("Id");
 
-                    b.HasIndex("UserId");
-
                     b.HasIndex("UserId", "ItemId", "Client", "Key")
                         .IsUnique();
 
@@ -173,8 +171,6 @@ namespace Jellyfin.Server.Implementations.Migrations
 
                     b.HasKey("Id");
 
-                    b.HasIndex("UserId");
-
                     b.HasIndex("UserId", "ItemId", "Client")
                         .IsUnique();
 
@@ -288,12 +284,17 @@ namespace Jellyfin.Server.Implementations.Migrations
                         .IsConcurrencyToken()
                         .HasColumnType("INTEGER");
 
+                    b.Property<Guid?>("UserId")
+                        .HasColumnType("TEXT");
+
                     b.Property<bool>("Value")
                         .HasColumnType("INTEGER");
 
                     b.HasKey("Id");
 
-                    b.HasIndex("Permission_Permissions_Guid");
+                    b.HasIndex("UserId", "Kind")
+                        .IsUnique()
+                        .HasFilter("[UserId] IS NOT NULL");
 
                     b.ToTable("Permissions");
                 });
@@ -314,6 +315,9 @@ namespace Jellyfin.Server.Implementations.Migrations
                         .IsConcurrencyToken()
                         .HasColumnType("INTEGER");
 
+                    b.Property<Guid?>("UserId")
+                        .HasColumnType("TEXT");
+
                     b.Property<string>("Value")
                         .IsRequired()
                         .HasMaxLength(65535)
@@ -321,7 +325,9 @@ namespace Jellyfin.Server.Implementations.Migrations
 
                     b.HasKey("Id");
 
-                    b.HasIndex("Preference_Preferences_Guid");
+                    b.HasIndex("UserId", "Kind")
+                        .IsUnique()
+                        .HasFilter("[UserId] IS NOT NULL");
 
                     b.ToTable("Preferences");
                 });
@@ -428,10 +434,14 @@ namespace Jellyfin.Server.Implementations.Migrations
                     b.Property<string>("Username")
                         .IsRequired()
                         .HasMaxLength(255)
-                        .HasColumnType("TEXT");
+                        .HasColumnType("TEXT")
+                        .UseCollation("NOCASE");
 
                     b.HasKey("Id");
 
+                    b.HasIndex("Username")
+                        .IsUnique();
+
                     b.ToTable("Users");
                 });
 
@@ -466,7 +476,8 @@ namespace Jellyfin.Server.Implementations.Migrations
                 {
                     b.HasOne("Jellyfin.Data.Entities.User", null)
                         .WithOne("ProfileImage")
-                        .HasForeignKey("Jellyfin.Data.Entities.ImageInfo", "UserId");
+                        .HasForeignKey("Jellyfin.Data.Entities.ImageInfo", "UserId")
+                        .OnDelete(DeleteBehavior.Cascade);
                 });
 
             modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b =>
@@ -482,14 +493,16 @@ namespace Jellyfin.Server.Implementations.Migrations
                 {
                     b.HasOne("Jellyfin.Data.Entities.User", null)
                         .WithMany("Permissions")
-                        .HasForeignKey("Permission_Permissions_Guid");
+                        .HasForeignKey("UserId")
+                        .OnDelete(DeleteBehavior.Cascade);
                 });
 
             modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
                 {
                     b.HasOne("Jellyfin.Data.Entities.User", null)
                         .WithMany("Preferences")
-                        .HasForeignKey("Preference_Preferences_Guid");
+                        .HasForeignKey("UserId")
+                        .OnDelete(DeleteBehavior.Cascade);
                 });
 
             modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>

+ 7 - 11
Jellyfin.Server.Implementations/Users/UserManager.cs

@@ -144,7 +144,13 @@ namespace Jellyfin.Server.Implementations.Users
                 throw new ArgumentException("The new and old names must be different.");
             }
 
-            if (Users.Any(u => u.Id != user.Id && u.Username.Equals(newName, StringComparison.OrdinalIgnoreCase)))
+            await using var dbContext = _dbProvider.CreateContext();
+
+            if (await dbContext.Users
+                .AsQueryable()
+                .Where(u => u.Username == newName && u.Id != user.Id)
+                .AnyAsync()
+                .ConfigureAwait(false))
             {
                 throw new ArgumentException(string.Format(
                     CultureInfo.InvariantCulture,
@@ -251,16 +257,6 @@ namespace Jellyfin.Server.Implementations.Users
             }
 
             await using var dbContext = _dbProvider.CreateContext();
-
-            // Clear all entities related to the user from the database.
-            if (user.ProfileImage != null)
-            {
-                dbContext.Remove(user.ProfileImage);
-            }
-
-            dbContext.RemoveRange(user.Permissions);
-            dbContext.RemoveRange(user.Preferences);
-            dbContext.RemoveRange(user.AccessSchedules);
             dbContext.Users.Remove(user);
             await dbContext.SaveChangesAsync().ConfigureAwait(false);
             _users.Remove(userId);