From d2065bfa1b0411e09e92d4112baa83c2aca1acf2 Mon Sep 17 00:00:00 2001 From: ta264 Date: Sun, 15 Dec 2019 15:04:42 +0000 Subject: [PATCH] Swap to dapper and system.text.json for database backend --- src/NzbDrone.Api/History/HistoryModule.cs | 4 +- .../Indexers/ReleaseModuleBase.cs | 2 +- .../Movies/AlternativeTitleModule.cs | 13 - .../Movies/AlternativeYearModule.cs | 13 - .../Movies/MovieBulkImportModule.cs | 2 - .../ServiceFactoryFixture.cs | 9 +- .../Extensions/IEnumerableExtensions.cs | 4 - .../BulkImport/AddMultiMoviesFixture.cs | 105 +++--- .../Datastore/BasicRepositoryFixture.cs | 91 ++++- .../Converters/BooleanIntConverterFixture.cs | 59 --- .../Converters/CommandConverterFixture.cs | 49 +-- .../Converters/DictionaryConverterFixture.cs | 41 ++ .../Converters/DoubleConverterFixture.cs | 70 ---- .../Converters/EnumIntConverterFixture.cs | 58 --- .../Converters/GuidConverterFixture.cs | 33 +- .../Converters/Int32ConverterFixture.cs | 58 --- .../Converters/OsPathConverterFixture.cs | 30 +- .../ProviderSettingConverterFixture.cs | 30 +- .../Converters/QualityIntConverterFixture.cs | 41 +- .../Converters/TimeSpanConverterFixture.cs | 65 ---- .../Converters/UtcConverterFixture.cs | 39 +- .../Datastore/DatabaseFixture.cs | 9 +- .../Datastore/DatabaseRelationshipFixture.cs | 44 --- .../Datastore/MarrDataLazyLoadingFixture.cs | 55 --- .../PagingOffsetFixture.cs | 29 -- .../ToSortDirectionFixture.cs | 53 --- .../ReflectionStrategyFixture/Benchmarks.cs | 40 -- ...entionFixture.cs => TableMapperFixture.cs} | 16 +- .../Datastore/WhereBuilderFixture.cs | 125 ++++++ ...matAllowedByProfileSpecificationFixture.cs | 15 +- .../LanguageSpecificationFixture.cs | 14 +- ...ityAllowedByProfileSpecificationFixture.cs | 7 +- .../QueueSpecificationFixture.cs | 10 +- .../RssSync/DelaySpecificationFixture.cs | 4 +- .../DiskSpace/DiskSpaceServiceFixture.cs | 4 +- .../PendingReleaseServiceTests/AddFixture.cs | 4 +- .../RemoveGrabbedFixture.cs | 12 +- .../RemoveRejectedFixture.cs | 3 +- src/NzbDrone.Core.Test/Framework/DbTest.cs | 5 +- .../Framework/DirectDataMapper.cs | 18 +- .../Checks/RootFolderCheckFixture.cs | 8 +- .../CleanupAdditionalUsersFixture.cs | 15 +- .../Housekeepers/CleanupUnusedTagsFixture.cs | 11 +- .../DatabaseTargetFixture.cs | 19 - .../SameFileSpecificationFixture.cs | 12 +- .../UpgradeSpecificationFixture.cs | 13 +- .../UpgradeMediaFileServiceFixture.cs | 2 - .../MovieRepositoryFixture.cs | 14 +- .../ParsingServiceTests/MapFixture.cs | 4 +- .../Radarr.Core.Test.csproj | 1 + .../Authentication/UserRepository.cs | 4 +- .../Backup/MakeDatabaseBackup.cs | 2 +- .../Blacklisting/BlacklistRepository.cs | 30 +- .../Configuration/ConfigRepository.cs | 3 +- .../CustomFormats/CustomFormat.cs | 8 +- .../CustomFormats/CustomFormatDefinition.cs | 14 + .../CustomFormats/CustomFormatRepository.cs | 4 +- .../CustomFormats/CustomFormatService.cs | 16 +- .../Datastore/BasicRepository.cs | 355 +++++++++++++----- .../Converters/BooleanIntConverter.cs | 51 --- .../Datastore/Converters/CommandConverter.cs | 31 +- .../Converters/CustomFormatIntConverter.cs | 80 ++-- .../Datastore/Converters/DoubleConverter.cs | 46 --- .../Converters/EmbeddedDocumentConverter.cs | 72 ++-- .../Datastore/Converters/EnumIntConverter.cs | 36 -- .../Datastore/Converters/GuidConverter.cs | 32 +- .../Datastore/Converters/Int32Converter.cs | 36 -- .../Converters/LanguageIntConverter.cs | 87 ++--- .../Converters/NoFlagsStringEnumConverter.cs | 17 + .../Datastore/Converters/OsPathConverter.cs | 35 +- .../Converters/ProviderSettingConverter.cs | 43 +-- .../Converters/QualityIntConverter.cs | 70 ++-- .../Converters/QualityTagStringConverter.cs | 59 +-- .../Datastore/Converters/TimeSpanConverter.cs | 38 +- .../Datastore/Converters/UtcConverter.cs | 46 +-- src/NzbDrone.Core/Datastore/Database.cs | 22 +- src/NzbDrone.Core/Datastore/DbFactory.cs | 12 +- .../Datastore/ExpressionVisitor.cs | 146 +++++++ .../Datastore/Extensions/BuilderExtensions.cs | 114 ++++++ .../Datastore/Extensions/MappingExtensions.cs | 63 ---- .../Extensions/PagingSpecExtensions.cs | 44 --- .../Extensions/RelationshipExtensions.cs | 51 --- src/NzbDrone.Core/Datastore/LazyList.cs | 30 -- src/NzbDrone.Core/Datastore/LogDatabase.cs | 6 +- src/NzbDrone.Core/Datastore/MainDatabase.cs | 6 +- .../036_update_with_quality_converters.cs | 22 +- .../Migration/104_add_moviefiles_table.cs | 4 - .../Migration/147_add_custom_formats.cs | 11 +- ..._add_language_to_file_history_blacklist.cs | 13 +- .../162_fix_profile_format_default.cs | 20 + src/NzbDrone.Core/Datastore/TableMapper.cs | 114 ++++++ src/NzbDrone.Core/Datastore/TableMapping.cs | 224 ++++++----- src/NzbDrone.Core/Datastore/WhereBuilder.cs | 303 +++++++++++++++ .../DownloadDecisionComparer.cs | 9 +- ...stomFormatAllowedByProfileSpecification.cs | 2 +- .../Specifications/CutoffSpecification.cs | 4 +- .../Specifications/LanguageSpecification.cs | 2 +- .../QualityAllowedByProfileSpecification.cs | 2 +- .../Specifications/QueueSpecification.cs | 2 +- .../RssSync/DelaySpecification.cs | 4 +- .../UpgradeAllowedSpecification.cs | 2 +- .../DiskSpace/DiskSpaceService.cs | 6 +- .../Download/DownloadClientRepository.cs | 2 +- .../Pending/PendingReleaseRepository.cs | 6 +- .../Download/Pending/PendingReleaseService.cs | 2 +- src/NzbDrone.Core/Extras/ExtraService.cs | 4 +- .../Extras/Files/ExtraFileRepository.cs | 8 +- .../HealthCheck/Checks/MountCheck.cs | 10 +- .../HealthCheck/Checks/RootFolderCheck.cs | 10 +- .../History/HistoryRepository.cs | 70 ++-- .../CleanupAbsolutePathMetadataFiles.cs | 7 +- .../CleanupAdditionalNamingSpecs.cs | 7 +- .../Housekeepers/CleanupAdditionalUsers.cs | 13 +- ...ownloadClientUnavailablePendingReleases.cs | 21 +- .../CleanupDuplicateMetadataFiles.cs | 9 +- .../CleanupOrphanedAlternativeTitles.cs | 7 +- .../Housekeepers/CleanupOrphanedBlacklist.cs | 7 +- .../CleanupOrphanedDownloadClientStatus.cs | 7 +- .../CleanupOrphanedHistoryItems.cs | 5 +- .../CleanupOrphanedIndexerStatus.cs | 5 +- .../CleanupOrphanedMetadataFiles.cs | 13 +- .../Housekeepers/CleanupOrphanedMovieFiles.cs | 15 +- .../CleanupOrphanedPendingReleases.cs | 17 +- .../Housekeepers/CleanupUnusedTags.cs | 18 +- .../FixFutureRunScheduledTasks.cs | 12 +- .../FixWronglyMatchedMovieFiles.cs | 2 +- .../Jobs/ScheduledTaskRepository.cs | 2 +- .../MediaFiles/MediaFileRepository.cs | 4 +- .../MediaFiles/MediaFileService.cs | 24 +- src/NzbDrone.Core/MediaFiles/MovieFile.cs | 4 +- .../Messaging/Commands/CommandRepository.cs | 46 +-- .../AlternativeTitles/AlternativeTitle.cs | 5 +- .../AlternativeTitleRepository.cs | 12 +- src/NzbDrone.Core/Movies/Movie.cs | 5 +- src/NzbDrone.Core/Movies/MovieRepository.cs | 245 ++++++------ src/NzbDrone.Core/Movies/MovieService.cs | 76 +++- src/NzbDrone.Core/Movies/QueryExtensions.cs | 9 - .../ImportExclusions/ImportExclusion.cs | 9 +- .../ImportExclusionsRepository.cs | 15 +- .../NetImport/NetImportDefinition.cs | 3 - src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs | 2 +- .../Profiles/ProfileRepository.cs | 2 +- .../Qualities/QualityDefinitionRepository.cs | 5 +- src/NzbDrone.Core/Qualities/Revision.cs | 3 +- src/NzbDrone.Core/Radarr.Core.csproj | 4 +- src/NzbDrone.Core/Tags/TagRepository.cs | 4 +- .../ThingiProvider/ProviderRepository.cs | 48 ++- .../Status/ProviderStatusRepository.cs | 5 +- .../Paths/MovieAncestorValidator.cs | 2 +- src/NzbDrone.Host.Test/ContainerFixture.cs | 6 + .../IntegrationTestBase.cs | 5 - src/NzbDrone.Test.Common/NzbDroneRunner.cs | 8 +- src/Radarr.Api.V3/History/HistoryModule.cs | 2 +- .../Indexers/ReleaseModuleBase.cs | 2 +- .../MovieFiles/MovieFileResource.cs | 4 +- 155 files changed, 2333 insertions(+), 2340 deletions(-) delete mode 100644 src/NzbDrone.Core.Test/Datastore/Converters/BooleanIntConverterFixture.cs create mode 100644 src/NzbDrone.Core.Test/Datastore/Converters/DictionaryConverterFixture.cs delete mode 100644 src/NzbDrone.Core.Test/Datastore/Converters/DoubleConverterFixture.cs delete mode 100644 src/NzbDrone.Core.Test/Datastore/Converters/EnumIntConverterFixture.cs delete mode 100644 src/NzbDrone.Core.Test/Datastore/Converters/Int32ConverterFixture.cs delete mode 100644 src/NzbDrone.Core.Test/Datastore/Converters/TimeSpanConverterFixture.cs delete mode 100644 src/NzbDrone.Core.Test/Datastore/MarrDataLazyLoadingFixture.cs delete mode 100644 src/NzbDrone.Core.Test/Datastore/PagingSpecExtensionsTests/PagingOffsetFixture.cs delete mode 100644 src/NzbDrone.Core.Test/Datastore/PagingSpecExtensionsTests/ToSortDirectionFixture.cs delete mode 100644 src/NzbDrone.Core.Test/Datastore/ReflectionStrategyFixture/Benchmarks.cs rename src/NzbDrone.Core.Test/Datastore/{MappingExtentionFixture.cs => TableMapperFixture.cs} (69%) create mode 100644 src/NzbDrone.Core.Test/Datastore/WhereBuilderFixture.cs create mode 100644 src/NzbDrone.Core/CustomFormats/CustomFormatDefinition.cs delete mode 100644 src/NzbDrone.Core/Datastore/Converters/BooleanIntConverter.cs delete mode 100644 src/NzbDrone.Core/Datastore/Converters/DoubleConverter.cs delete mode 100644 src/NzbDrone.Core/Datastore/Converters/EnumIntConverter.cs delete mode 100644 src/NzbDrone.Core/Datastore/Converters/Int32Converter.cs create mode 100644 src/NzbDrone.Core/Datastore/Converters/NoFlagsStringEnumConverter.cs create mode 100644 src/NzbDrone.Core/Datastore/ExpressionVisitor.cs create mode 100644 src/NzbDrone.Core/Datastore/Extensions/BuilderExtensions.cs delete mode 100644 src/NzbDrone.Core/Datastore/Extensions/MappingExtensions.cs delete mode 100644 src/NzbDrone.Core/Datastore/Extensions/PagingSpecExtensions.cs delete mode 100644 src/NzbDrone.Core/Datastore/Extensions/RelationshipExtensions.cs delete mode 100644 src/NzbDrone.Core/Datastore/LazyList.cs create mode 100644 src/NzbDrone.Core/Datastore/Migration/162_fix_profile_format_default.cs create mode 100644 src/NzbDrone.Core/Datastore/TableMapper.cs create mode 100644 src/NzbDrone.Core/Datastore/WhereBuilder.cs diff --git a/src/NzbDrone.Api/History/HistoryModule.cs b/src/NzbDrone.Api/History/HistoryModule.cs index 76325d82e..85a56c576 100644 --- a/src/NzbDrone.Api/History/HistoryModule.cs +++ b/src/NzbDrone.Api/History/HistoryModule.cs @@ -1,13 +1,11 @@ using System; using System.Linq; using Nancy; -using Radarr.Http.Extensions; using NzbDrone.Api.Movies; using NzbDrone.Core.Datastore; using NzbDrone.Core.Download; using NzbDrone.Core.History; using Radarr.Http; -using Radarr.Http.REST; using NzbDrone.Core.DecisionEngine.Specifications; namespace NzbDrone.Api.History @@ -37,7 +35,7 @@ protected HistoryResource MapToResource(Core.History.History model) if (model.Movie != null) { - resource.QualityCutoffNotMet = _qualityUpgradableSpecification.CutoffNotMet(model.Movie.Profile.Value, model.Quality); + resource.QualityCutoffNotMet = _qualityUpgradableSpecification.CutoffNotMet(model.Movie.Profile, model.Quality); } return resource; diff --git a/src/NzbDrone.Api/Indexers/ReleaseModuleBase.cs b/src/NzbDrone.Api/Indexers/ReleaseModuleBase.cs index 3dca0acce..018228c92 100644 --- a/src/NzbDrone.Api/Indexers/ReleaseModuleBase.cs +++ b/src/NzbDrone.Api/Indexers/ReleaseModuleBase.cs @@ -29,7 +29,7 @@ protected virtual ReleaseResource MapDecision(DownloadDecision decision, int ini if (decision.RemoteMovie.Movie != null) { release.QualityWeight = decision.RemoteMovie.Movie - .Profile.Value + .Profile .Items.FindIndex(v => v.Quality == release.Quality.Quality) * 100; } diff --git a/src/NzbDrone.Api/Movies/AlternativeTitleModule.cs b/src/NzbDrone.Api/Movies/AlternativeTitleModule.cs index 8847346c0..ca18bdecb 100644 --- a/src/NzbDrone.Api/Movies/AlternativeTitleModule.cs +++ b/src/NzbDrone.Api/Movies/AlternativeTitleModule.cs @@ -1,19 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Marr.Data; -using Nancy; -using NzbDrone.Api; -using NzbDrone.Common.Cache; -using NzbDrone.Common.Extensions; -using NzbDrone.Core.MediaCover; -using NzbDrone.Core.MediaFiles; -using NzbDrone.Core.MediaFiles.MovieImport; using NzbDrone.Core.Messaging.Events; -using NzbDrone.Core.MetadataSource; using NzbDrone.Core.MetadataSource.RadarrAPI; using NzbDrone.Core.Movies.AlternativeTitles; -using NzbDrone.Core.RootFolders; using NzbDrone.Core.Movies; using NzbDrone.Core.Movies.Events; using Radarr.Http; diff --git a/src/NzbDrone.Api/Movies/AlternativeYearModule.cs b/src/NzbDrone.Api/Movies/AlternativeYearModule.cs index eb40041b4..e14ef90cf 100644 --- a/src/NzbDrone.Api/Movies/AlternativeYearModule.cs +++ b/src/NzbDrone.Api/Movies/AlternativeYearModule.cs @@ -1,20 +1,7 @@ using System; -using System.Collections.Generic; -using System.Linq; -using Marr.Data; -using Nancy; -using NzbDrone.Api; using NzbDrone.Common.Cache; -using NzbDrone.Common.Extensions; -using NzbDrone.Common.Messaging; -using NzbDrone.Core.MediaCover; -using NzbDrone.Core.MediaFiles; -using NzbDrone.Core.MediaFiles.MovieImport; using NzbDrone.Core.Messaging.Events; -using NzbDrone.Core.MetadataSource; using NzbDrone.Core.MetadataSource.RadarrAPI; -using NzbDrone.Core.Movies.AlternativeTitles; -using NzbDrone.Core.RootFolders; using NzbDrone.Core.Movies; using NzbDrone.Core.Movies.Events; using Radarr.Http; diff --git a/src/NzbDrone.Api/Movies/MovieBulkImportModule.cs b/src/NzbDrone.Api/Movies/MovieBulkImportModule.cs index 81a04fa0d..201ae2507 100644 --- a/src/NzbDrone.Api/Movies/MovieBulkImportModule.cs +++ b/src/NzbDrone.Api/Movies/MovieBulkImportModule.cs @@ -1,12 +1,10 @@ using System.Collections.Generic; using Nancy; -using Radarr.Http.Extensions; using NzbDrone.Core.MediaCover; using NzbDrone.Core.MetadataSource; using NzbDrone.Core.Parser; using System.Linq; using System; -using Marr.Data; using NzbDrone.Common.Extensions; using NzbDrone.Core.Datastore; using NzbDrone.Core.MediaFiles; diff --git a/src/NzbDrone.Common.Test/ServiceFactoryFixture.cs b/src/NzbDrone.Common.Test/ServiceFactoryFixture.cs index d8c5d26a4..96ff4021b 100644 --- a/src/NzbDrone.Common.Test/ServiceFactoryFixture.cs +++ b/src/NzbDrone.Common.Test/ServiceFactoryFixture.cs @@ -7,6 +7,8 @@ using NzbDrone.Core.Messaging.Events; using Radarr.Host; using NzbDrone.Test.Common; +using NzbDrone.Core.CustomFormats; +using System.Collections.Generic; namespace NzbDrone.Common.Test { @@ -20,6 +22,11 @@ public void event_handlers_should_be_unique() container.Register(new MainDatabase(null)); container.Resolve().Register(); + // A dummy custom format repository since this isn't a DB test + var mockCustomFormat = Mocker.GetMock(); + mockCustomFormat.Setup(x => x.All()).Returns(new List()); + container.Register(mockCustomFormat.Object); + Mocker.SetConstant(container); var handlers = Subject.BuildAll>() @@ -28,4 +35,4 @@ public void event_handlers_should_be_unique() handlers.Should().OnlyHaveUniqueItems(); } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Common/Extensions/IEnumerableExtensions.cs b/src/NzbDrone.Common/Extensions/IEnumerableExtensions.cs index b06b6d736..c7928056c 100644 --- a/src/NzbDrone.Common/Extensions/IEnumerableExtensions.cs +++ b/src/NzbDrone.Common/Extensions/IEnumerableExtensions.cs @@ -133,10 +133,6 @@ private static IEnumerable InternalDropLast(IEnumerable source, int n) yield return buffer.Dequeue(); } } - public static bool In(this T source, List list) - { - return list.Contains(source); - } public static string ConcatToString(this IEnumerable source, string separator = ", ") { diff --git a/src/NzbDrone.Core.Test/BulkImport/AddMultiMoviesFixture.cs b/src/NzbDrone.Core.Test/BulkImport/AddMultiMoviesFixture.cs index fcad2a48e..be04adebe 100644 --- a/src/NzbDrone.Core.Test/BulkImport/AddMultiMoviesFixture.cs +++ b/src/NzbDrone.Core.Test/BulkImport/AddMultiMoviesFixture.cs @@ -5,71 +5,76 @@ using NzbDrone.Core.Organizer; using NzbDrone.Core.Movies; using NzbDrone.Core.Test.Framework; -using NzbDrone.Core.Movies.Events; using System.Collections.Generic; namespace NzbDrone.Core.Test.BulkImport { - [TestFixture] - public class AddMultiMoviesFixture : CoreTest - { - private List fakeMovies; + [TestFixture] + public class AddMultiMoviesFixture : CoreTest + { + private List fakeMovies; - [SetUp] - public void Setup() - { - fakeMovies = Builder.CreateListOfSize(3).BuildList(); - fakeMovies.ForEach(m => - { - m.Path = null; - m.RootFolderPath = @"C:\Test\TV"; - }); - } + [SetUp] + public void Setup() + { + fakeMovies = Builder.CreateListOfSize(3).BuildList(); + fakeMovies.ForEach(m => + { + m.Path = null; + m.RootFolderPath = @"C:\Test\TV"; + }); + } - [Test] - public void movies_added_event_should_have_proper_path() - { - Mocker.GetMock() - .Setup(s => s.GetMovieFolder(It.IsAny(), null)) - .Returns((Movie m, NamingConfig n) => m.Title); + [Test] + public void movies_added_event_should_have_proper_path() + { + Mocker.GetMock() + .Setup(s => s.GetMovieFolder(It.IsAny(), null)) + .Returns((Movie m, NamingConfig n) => m.Title); - var movies = Subject.AddMovies(fakeMovies); + Mocker.GetMock().Setup(s => s.FindByTmdbId(It.IsAny>())) + .Returns(new List()); - foreach (Movie movie in movies) - { - movie.Path.Should().NotBeNullOrEmpty(); - } + var movies = Subject.AddMovies(fakeMovies); - //Subject.GetAllMovies().Should().HaveCount(3); - } + foreach (Movie movie in movies) + { + movie.Path.Should().NotBeNullOrEmpty(); + } - [Test] - public void movies_added_should_ignore_already_added() - { - Mocker.GetMock() - .Setup(s => s.GetMovieFolder(It.IsAny(), null)) - .Returns((Movie m, NamingConfig n) => m.Title); + // Subject.GetAllMovies().Should().HaveCount(3); + } - Mocker.GetMock().Setup(s => s.All()).Returns(new List { fakeMovies[0] }); + [Test] + public void movies_added_should_ignore_already_added() + { + Mocker.GetMock() + .Setup(s => s.GetMovieFolder(It.IsAny(), null)) + .Returns((Movie m, NamingConfig n) => m.Title); - var movies = Subject.AddMovies(fakeMovies); + Mocker.GetMock().Setup(s => s.FindByTmdbId(It.IsAny>())) + .Returns(new List { fakeMovies[0] }); - Mocker.GetMock().Verify(v => v.InsertMany(It.Is>(l => l.Count == 2))); - } + var movies = Subject.AddMovies(fakeMovies); - [Test] - public void movies_added_should_ignore_duplicates() - { - Mocker.GetMock() - .Setup(s => s.GetMovieFolder(It.IsAny(), null)) - .Returns((Movie m, NamingConfig n) => m.Title); + Mocker.GetMock().Verify(v => v.InsertMany(It.Is>(l => l.Count == 2))); + } - fakeMovies[2].TmdbId = fakeMovies[0].TmdbId; + [Test] + public void movies_added_should_ignore_duplicates() + { + Mocker.GetMock() + .Setup(s => s.GetMovieFolder(It.IsAny(), null)) + .Returns((Movie m, NamingConfig n) => m.Title); - var movies = Subject.AddMovies(fakeMovies); + Mocker.GetMock().Setup(s => s.FindByTmdbId(It.IsAny>())) + .Returns(new List()); - Mocker.GetMock().Verify(v => v.InsertMany(It.Is>(l => l.Count == 2))); - } + fakeMovies[2].TmdbId = fakeMovies[0].TmdbId; - } -} \ No newline at end of file + var movies = Subject.AddMovies(fakeMovies); + + Mocker.GetMock().Verify(v => v.InsertMany(It.Is>(l => l.Count == 2))); + } + } +} diff --git a/src/NzbDrone.Core.Test/Datastore/BasicRepositoryFixture.cs b/src/NzbDrone.Core.Test/Datastore/BasicRepositoryFixture.cs index e4775e037..e8b8b0a29 100644 --- a/src/NzbDrone.Core.Test/Datastore/BasicRepositoryFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/BasicRepositoryFixture.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using FizzWare.NBuilder; using FluentAssertions; @@ -15,7 +16,7 @@ public class BasicRepositoryFixture : DbTest, ScheduledTask> { private ScheduledTask _basicType; - + private List _basicList; [SetUp] public void Setup() @@ -25,15 +26,28 @@ public void Setup() .With(c => c.Id = 0) .With(c => c.LastExecution = DateTime.UtcNow) .Build(); + + _basicList = Builder + .CreateListOfSize(5) + .All() + .With(x => x.Id = 0) + .BuildList(); } [Test] - public void should_be_able_to_add() + public void should_be_able_to_insert() { Subject.Insert(_basicType); Subject.All().Should().HaveCount(1); } + [Test] + public void should_be_able_to_insert_many() + { + Subject.InsertMany(_basicList); + Subject.All().Should().HaveCount(5); + } + [Test] public void purge_should_delete_all() { @@ -47,7 +61,6 @@ public void purge_should_delete_all() } - [Test] public void should_be_able_to_delete_model() { @@ -58,6 +71,16 @@ public void should_be_able_to_delete_model() Subject.All().Should().BeEmpty(); } + [Test] + public void should_be_able_to_delete_many() + { + Subject.InsertMany(_basicList); + Subject.All().Should().HaveCount(5); + + Subject.DeleteMany(_basicList.Take(2).ToList()); + Subject.All().Should().HaveCount(3); + } + [Test] public void should_be_able_to_find_by_id() { @@ -67,6 +90,64 @@ public void should_be_able_to_find_by_id() storeObject.Should().BeEquivalentTo(_basicType, o=>o.IncludingAllRuntimeProperties()); } + [Test] + public void should_be_able_to_find_by_multiple_id() + { + Subject.InsertMany(_basicList); + var storeObject = Subject.Get(_basicList.Take(2).Select(x => x.Id)); + storeObject.Should().HaveCount(2); + } + + [Test] + public void should_be_able_to_update() + { + Subject.Insert(_basicType); + _basicType.Interval = 999; + + Subject.Update(_basicType); + + Subject.All().First().Interval.Should().Be(999); + } + + [Test] + public void should_be_able_to_update_many() + { + Subject.InsertMany(_basicList); + _basicList.ForEach(x => x.Interval = 999); + + Subject.UpdateMany(_basicList); + + Subject.All().All(x => x.Interval == 999); + } + + [Test] + public void should_be_able_to_update_single_field() + { + Subject.Insert(_basicType); + _basicType.Interval = 999; + _basicType.LastExecution = DateTime.UtcNow; + + Subject.SetFields(_basicType, x => x.Interval); + + var dbValue = Subject.Single(); + dbValue.Interval.Should().Be(999); + dbValue.LastExecution.Should().NotBe(_basicType.LastExecution); + } + + [Test] + public void should_be_able_to_update_many_single_field() + { + Subject.InsertMany(_basicList); + _basicList.ForEach(x => x.Interval = 999); + _basicList.ForEach(x => x.LastExecution = DateTime.UtcNow); + + Subject.SetFields(_basicList, x => x.Interval); + + var dbValue = Subject.All().First(); + dbValue.Interval.Should().Be(999); + dbValue.LastExecution.Should().NotBe(_basicType.LastExecution); + } + [Test] public void should_be_able_to_get_single() { @@ -86,7 +167,6 @@ public void getting_model_with_invalid_id_should_throw() Assert.Throws(() => Subject.Get(12)); } - [Test] public void get_all_with_empty_db_should_return_empty_list() { @@ -98,7 +178,6 @@ public void get_all_with_empty_db_should_return_empty_list() public void should_be_able_to_call_ToList_on_empty_quariable() { Subject.All().ToList().Should().BeEmpty(); - } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core.Test/Datastore/Converters/BooleanIntConverterFixture.cs b/src/NzbDrone.Core.Test/Datastore/Converters/BooleanIntConverterFixture.cs deleted file mode 100644 index 649a7303e..000000000 --- a/src/NzbDrone.Core.Test/Datastore/Converters/BooleanIntConverterFixture.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using FluentAssertions; -using Marr.Data.Converters; -using NUnit.Framework; -using NzbDrone.Core.Test.Framework; - -namespace NzbDrone.Core.Test.Datastore.Converters -{ - [TestFixture] - public class BooleanIntConverterFixture : CoreTest - { - [TestCase(true, 1)] - [TestCase(false, 0)] - public void should_return_int_when_saving_boolean_to_db(bool input, int expected) - { - Subject.ToDB(input).Should().Be(expected); - } - - [Test] - public void should_return_db_null_for_null_value_when_saving_to_db() - { - Subject.ToDB(null).Should().Be(DBNull.Value); - } - - [TestCase(1, true)] - [TestCase(0, false)] - public void should_return_bool_when_getting_int_from_db(int input, bool expected) - { - var context = new ConverterContext - { - DbValue = (long)input - }; - - Subject.FromDB(context).Should().Be(expected); - } - - [Test] - public void should_return_db_null_for_null_value_when_getting_from_db() - { - var context = new ConverterContext - { - DbValue = DBNull.Value - }; - - Subject.FromDB(context).Should().Be(DBNull.Value); - } - - [Test] - public void should_throw_for_non_boolean_equivalent_number_value_when_getting_from_db() - { - var context = new ConverterContext - { - DbValue = (long)2 - }; - - Assert.Throws(() => Subject.FromDB(context)); - } - } -} diff --git a/src/NzbDrone.Core.Test/Datastore/Converters/CommandConverterFixture.cs b/src/NzbDrone.Core.Test/Datastore/Converters/CommandConverterFixture.cs index 17ce66cfb..aa3c4c114 100644 --- a/src/NzbDrone.Core.Test/Datastore/Converters/CommandConverterFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/Converters/CommandConverterFixture.cs @@ -1,64 +1,51 @@ -using System; -using System.Data; -using FluentAssertions; -using Marr.Data.Converters; -using Moq; -using NUnit.Framework; -using NzbDrone.Common.Serializer; using NzbDrone.Core.Datastore.Converters; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Movies.Commands; +using NUnit.Framework; +using FluentAssertions; +using System.Data.SQLite; namespace NzbDrone.Core.Test.Datastore.Converters { [TestFixture] public class CommandConverterFixture : CoreTest { + SQLiteParameter param; + + [SetUp] + public void Setup() + { + param = new SQLiteParameter(); + } + [Test] public void should_return_json_string_when_saving_boolean_to_db() { var command = new RefreshMovieCommand(); - Subject.ToDB(command).Should().BeOfType(); + Subject.SetValue(param, command); + param.Value.Should().BeOfType(); } [Test] public void should_return_null_for_null_value_when_saving_to_db() { - Subject.ToDB(null).Should().Be(null); - } - - [Test] - public void should_return_db_null_for_db_null_value_when_saving_to_db() - { - Subject.ToDB(DBNull.Value).Should().Be(DBNull.Value); + Subject.SetValue(param, null); + param.Value.Should().BeNull(); } [Test] public void should_return_command_when_getting_json_from_db() { - var dataRecordMock = new Mock(); - dataRecordMock.Setup(s => s.GetOrdinal("Name")).Returns(0); - dataRecordMock.Setup(s => s.GetString(0)).Returns("RefreshMovie"); + var data = "{\"name\": \"RefreshMovie\"}"; - var context = new ConverterContext - { - DataRecord = dataRecordMock.Object, - DbValue = new RefreshMovieCommand().ToJson() - }; - - Subject.FromDB(context).Should().BeOfType(); + Subject.Parse(data).Should().BeOfType(); } [Test] public void should_return_null_for_null_value_when_getting_from_db() { - var context = new ConverterContext - { - DbValue = DBNull.Value - }; - - Subject.FromDB(context).Should().Be(null); + Subject.Parse(null).Should().BeNull(); } } } diff --git a/src/NzbDrone.Core.Test/Datastore/Converters/DictionaryConverterFixture.cs b/src/NzbDrone.Core.Test/Datastore/Converters/DictionaryConverterFixture.cs new file mode 100644 index 000000000..f67834116 --- /dev/null +++ b/src/NzbDrone.Core.Test/Datastore/Converters/DictionaryConverterFixture.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Data.SQLite; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Datastore.Converters; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.Datastore.Converters +{ + [TestFixture] + public class DictionaryConverterFixture : CoreTest>> + { + SQLiteParameter param; + + [SetUp] + public void Setup() + { + param = new SQLiteParameter(); + } + + [Test] + public void should_serialize_in_camel_case() + { + var dict = new Dictionary + { + { "Data", "Should be lowercased" }, + { "CamelCase", "Should be cameled" } + }; + + Subject.SetValue(param, dict); + + var result = (string)param.Value; + + result.Should().Contain("data"); + result.Should().NotContain("Data"); + + result.Should().Contain("camelCase"); + result.Should().NotContain("CamelCase"); + } + } +} diff --git a/src/NzbDrone.Core.Test/Datastore/Converters/DoubleConverterFixture.cs b/src/NzbDrone.Core.Test/Datastore/Converters/DoubleConverterFixture.cs deleted file mode 100644 index bf4974124..000000000 --- a/src/NzbDrone.Core.Test/Datastore/Converters/DoubleConverterFixture.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using FluentAssertions; -using Marr.Data.Converters; -using NUnit.Framework; -using NzbDrone.Core.Datastore.Converters; -using NzbDrone.Core.Test.Framework; - -namespace NzbDrone.Core.Test.Datastore.Converters -{ - [TestFixture] - public class DoubleConverterFixture : CoreTest - { - [Test] - public void should_return_double_when_saving_double_to_db() - { - var input = 10.5D; - - Subject.ToDB(input).Should().Be(input); - } - - [Test] - public void should_return_null_for_null_value_when_saving_to_db() - { - Subject.ToDB(null).Should().Be(null); - } - - [Test] - public void should_return_db_null_for_db_null_value_when_saving_to_db() - { - Subject.ToDB(DBNull.Value).Should().Be(DBNull.Value); - } - - [Test] - public void should_return_double_when_getting_double_from_db() - { - var expected = 10.5D; - - var context = new ConverterContext - { - DbValue = expected - }; - - Subject.FromDB(context).Should().Be(expected); - } - - [Test] - public void should_return_double_when_getting_string_from_db() - { - var expected = 10.5D; - - var context = new ConverterContext - { - DbValue = $"{expected}" - }; - - Subject.FromDB(context).Should().Be(expected); - } - - [Test] - public void should_return_null_for_null_value_when_getting_from_db() - { - var context = new ConverterContext - { - DbValue = DBNull.Value - }; - - Subject.FromDB(context).Should().Be(DBNull.Value); - } - } -} diff --git a/src/NzbDrone.Core.Test/Datastore/Converters/EnumIntConverterFixture.cs b/src/NzbDrone.Core.Test/Datastore/Converters/EnumIntConverterFixture.cs deleted file mode 100644 index a54296855..000000000 --- a/src/NzbDrone.Core.Test/Datastore/Converters/EnumIntConverterFixture.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Reflection; -using FluentAssertions; -using Marr.Data.Converters; -using Marr.Data.Mapping; -using Moq; -using NUnit.Framework; -using NzbDrone.Core.Test.Framework; -using NzbDrone.Core.Movies; -using NzbDrone.Core.Download.Pending; - -namespace NzbDrone.Core.Test.Datastore.Converters -{ - [TestFixture] - public class EnumIntConverterFixture : CoreTest - { - [Test] - public void should_return_int_when_saving_enum_to_db() - { - Subject.ToDB(PendingReleaseReason.Delay).Should().Be((int)PendingReleaseReason.Delay); - } - - [Test] - public void should_return_db_null_for_null_value_when_saving_to_db() - { - Subject.ToDB(null).Should().Be(DBNull.Value); - } - - [Test] - public void should_return_enum_when_getting_int_from_db() - { - var mockMemberInfo = new Mock(); - mockMemberInfo.SetupGet(s => s.DeclaringType).Returns(typeof(PendingRelease)); - mockMemberInfo.SetupGet(s => s.Name).Returns("Reason"); - - var expected = PendingReleaseReason.Delay; - - var context = new ConverterContext - { - ColumnMap = new ColumnMap(mockMemberInfo.Object) { FieldType = typeof(PendingReleaseReason) }, - DbValue = (long)expected - }; - - Subject.FromDB(context).Should().Be(expected); - } - - [Test] - public void should_return_null_for_null_value_when_getting_from_db() - { - var context = new ConverterContext - { - DbValue = DBNull.Value - }; - - Subject.FromDB(context).Should().Be(null); - } - } -} diff --git a/src/NzbDrone.Core.Test/Datastore/Converters/GuidConverterFixture.cs b/src/NzbDrone.Core.Test/Datastore/Converters/GuidConverterFixture.cs index 8444fa053..d8448ca0a 100644 --- a/src/NzbDrone.Core.Test/Datastore/Converters/GuidConverterFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/Converters/GuidConverterFixture.cs @@ -1,6 +1,6 @@ using System; +using System.Data.SQLite; using FluentAssertions; -using Marr.Data.Converters; using NUnit.Framework; using NzbDrone.Core.Datastore.Converters; using NzbDrone.Core.Test.Framework; @@ -10,18 +10,21 @@ namespace NzbDrone.Core.Test.Datastore.Converters [TestFixture] public class GuidConverterFixture : CoreTest { + SQLiteParameter param; + + [SetUp] + public void Setup() + { + param = new SQLiteParameter(); + } + [Test] public void should_return_string_when_saving_guid_to_db() { var guid = Guid.NewGuid(); - Subject.ToDB(guid).Should().Be(guid.ToString()); - } - - [Test] - public void should_return_db_null_for_null_value_when_saving_to_db() - { - Subject.ToDB(null).Should().Be(DBNull.Value); + Subject.SetValue(param, guid); + param.Value.Should().Be(guid.ToString()); } [Test] @@ -29,23 +32,13 @@ public void should_return_guid_when_getting_string_from_db() { var guid = Guid.NewGuid(); - var context = new ConverterContext - { - DbValue = guid.ToString() - }; - - Subject.FromDB(context).Should().Be(guid); + Subject.Parse(guid.ToString()).Should().Be(guid); } [Test] public void should_return_empty_guid_for_db_null_value_when_getting_from_db() { - var context = new ConverterContext - { - DbValue = DBNull.Value - }; - - Subject.FromDB(context).Should().Be(Guid.Empty); + Subject.Parse(null).Should().Be(Guid.Empty); } } } diff --git a/src/NzbDrone.Core.Test/Datastore/Converters/Int32ConverterFixture.cs b/src/NzbDrone.Core.Test/Datastore/Converters/Int32ConverterFixture.cs deleted file mode 100644 index 0be50e92a..000000000 --- a/src/NzbDrone.Core.Test/Datastore/Converters/Int32ConverterFixture.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using FluentAssertions; -using Marr.Data.Converters; -using NUnit.Framework; -using NzbDrone.Core.Datastore.Converters; -using NzbDrone.Core.Test.Framework; - -namespace NzbDrone.Core.Test.Datastore.Converters -{ - [TestFixture] - public class Int32ConverterFixture : CoreTest - { - [Test] - public void should_return_int_when_saving_int_to_db() - { - var i = 5; - - Subject.ToDB(i).Should().Be(i); - } - - [Test] - public void should_return_int_when_getting_int_from_db() - { - var i = 5; - - var context = new ConverterContext - { - DbValue = i - }; - - Subject.FromDB(context).Should().Be(i); - } - - [Test] - public void should_return_int_when_getting_string_from_db() - { - var i = 5; - - var context = new ConverterContext - { - DbValue = i.ToString() - }; - - Subject.FromDB(context).Should().Be(i); - } - - [Test] - public void should_return_db_null_for_db_null_value_when_getting_from_db() - { - var context = new ConverterContext - { - DbValue = DBNull.Value - }; - - Subject.FromDB(context).Should().Be(DBNull.Value); - } - } -} diff --git a/src/NzbDrone.Core.Test/Datastore/Converters/OsPathConverterFixture.cs b/src/NzbDrone.Core.Test/Datastore/Converters/OsPathConverterFixture.cs index f7f3da0d8..d953fac4b 100644 --- a/src/NzbDrone.Core.Test/Datastore/Converters/OsPathConverterFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/Converters/OsPathConverterFixture.cs @@ -1,6 +1,5 @@ -using System; +using System.Data.SQLite; using FluentAssertions; -using Marr.Data.Converters; using NUnit.Framework; using NzbDrone.Common.Disk; using NzbDrone.Core.Datastore.Converters; @@ -12,13 +11,22 @@ namespace NzbDrone.Core.Test.Datastore.Converters [TestFixture] public class OsPathConverterFixture : CoreTest { + SQLiteParameter param; + + [SetUp] + public void Setup() + { + param = new SQLiteParameter(); + } + [Test] public void should_return_string_when_saving_os_path_to_db() { var path = @"C:\Test\TV".AsOsAgnostic(); var osPath = new OsPath(path); - Subject.ToDB(osPath).Should().Be(path); + Subject.SetValue(param, osPath); + param.Value.Should().Be(path); } [Test] @@ -27,23 +35,13 @@ public void should_return_os_path_when_getting_string_from_db() var path = @"C:\Test\TV".AsOsAgnostic(); var osPath = new OsPath(path); - var context = new ConverterContext - { - DbValue = path - }; - - Subject.FromDB(context).Should().Be(osPath); + Subject.Parse(path).Should().Be(osPath); } [Test] - public void should_return_db_null_for_db_null_value_when_getting_from_db() + public void should_return_empty_for_null_value_when_getting_from_db() { - var context = new ConverterContext - { - DbValue = DBNull.Value - }; - - Subject.FromDB(context).Should().Be(DBNull.Value); + Subject.Parse(null).IsEmpty.Should().BeTrue(); } } } diff --git a/src/NzbDrone.Core.Test/Datastore/Converters/ProviderSettingConverterFixture.cs b/src/NzbDrone.Core.Test/Datastore/Converters/ProviderSettingConverterFixture.cs index 9aafbee73..79d4f0ad2 100644 --- a/src/NzbDrone.Core.Test/Datastore/Converters/ProviderSettingConverterFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/Converters/ProviderSettingConverterFixture.cs @@ -1,6 +1,5 @@ -using System; +using System.Data.SQLite; using FluentAssertions; -using Marr.Data.Converters; using NUnit.Framework; using NzbDrone.Core.Datastore.Converters; using NzbDrone.Core.Test.Framework; @@ -8,32 +7,29 @@ namespace NzbDrone.Core.Test.Datastore.Converters { + [Ignore("To reinstate once dapper changes worked out")] [TestFixture] public class ProviderSettingConverterFixture : CoreTest { + SQLiteParameter param; + + [SetUp] + public void Setup() + { + param = new SQLiteParameter(); + } + [Test] public void should_return_null_config_if_config_is_null() { - var result = Subject.FromDB(new ConverterContext() - { - DbValue = DBNull.Value - }); - - - result.Should().Be(NullConfig.Instance); + Subject.Parse(null).Should().Be(NullConfig.Instance); } [TestCase(null)] [TestCase("")] public void should_return_null_config_if_config_is_empty(object dbValue) { - var result = Subject.FromDB(new ConverterContext() - { - DbValue = dbValue - }); - - - result.Should().Be(NullConfig.Instance); + Subject.Parse(dbValue).Should().Be(NullConfig.Instance); } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core.Test/Datastore/Converters/QualityIntConverterFixture.cs b/src/NzbDrone.Core.Test/Datastore/Converters/QualityIntConverterFixture.cs index 31ae8b6b3..f6759ce62 100644 --- a/src/NzbDrone.Core.Test/Datastore/Converters/QualityIntConverterFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/Converters/QualityIntConverterFixture.cs @@ -1,6 +1,5 @@ -using System; +using System.Data.SQLite; using FluentAssertions; -using Marr.Data.Converters; using NUnit.Framework; using NzbDrone.Core.Datastore.Converters; using NzbDrone.Core.Qualities; @@ -9,26 +8,30 @@ namespace NzbDrone.Core.Test.Datastore.Converters { [TestFixture] - public class QualityIntConverterFixture : CoreTest + public class QualityIntConverterFixture : CoreTest { + SQLiteParameter param; + + [SetUp] + public void Setup() + { + param = new SQLiteParameter(); + } + [Test] public void should_return_int_when_saving_quality_to_db() { var quality = Quality.Bluray1080p; - Subject.ToDB(quality).Should().Be(quality.Id); + Subject.SetValue(param, quality); + param.Value.Should().Be(quality.Id); } [Test] public void should_return_0_when_saving_db_null_to_db() { - Subject.ToDB(DBNull.Value).Should().Be(0); - } - - [Test] - public void should_throw_when_saving_another_object_to_db() - { - Assert.Throws(() => Subject.ToDB("Not a quality")); + Subject.SetValue(param, null); + param.Value.Should().Be(0); } [Test] @@ -36,23 +39,13 @@ public void should_return_quality_when_getting_string_from_db() { var quality = Quality.Bluray1080p; - var context = new ConverterContext - { - DbValue = quality.Id - }; - - Subject.FromDB(context).Should().Be(quality); + Subject.Parse(quality.Id).Should().Be(quality); } [Test] - public void should_return_db_null_for_db_null_value_when_getting_from_db() + public void should_return_unknown_for_null_value_when_getting_from_db() { - var context = new ConverterContext - { - DbValue = DBNull.Value - }; - - Subject.FromDB(context).Should().Be(Quality.Unknown); + Subject.Parse(null).Should().Be(Quality.Unknown); } } } diff --git a/src/NzbDrone.Core.Test/Datastore/Converters/TimeSpanConverterFixture.cs b/src/NzbDrone.Core.Test/Datastore/Converters/TimeSpanConverterFixture.cs deleted file mode 100644 index c96848179..000000000 --- a/src/NzbDrone.Core.Test/Datastore/Converters/TimeSpanConverterFixture.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Globalization; -using FluentAssertions; -using Marr.Data.Converters; -using NUnit.Framework; -using NzbDrone.Core.Datastore.Converters; -using NzbDrone.Core.Test.Framework; - -namespace NzbDrone.Core.Test.Datastore.Converters -{ - [TestFixture] - public class TimeSpanConverterFixture : CoreTest - { - [Test] - public void should_return_string_when_saving_timespan_to_db() - { - var timeSpan = TimeSpan.FromMinutes(5); - - Subject.ToDB(timeSpan).Should().Be(timeSpan.ToString("c", CultureInfo.InvariantCulture)); - } - - [Test] - public void should_return_null_when_saving_empty_string_to_db() - { - Subject.ToDB("").Should().Be(null); - } - - [Test] - public void should_return_time_span_when_getting_time_span_from_db() - { - var timeSpan = TimeSpan.FromMinutes(5); - - var context = new ConverterContext - { - DbValue = timeSpan - }; - - Subject.FromDB(context).Should().Be(timeSpan); - } - - [Test] - public void should_return_time_span_when_getting_string_from_db() - { - var timeSpan = TimeSpan.FromMinutes(5); - - var context = new ConverterContext - { - DbValue = timeSpan.ToString("c", CultureInfo.InvariantCulture) - }; - - Subject.FromDB(context).Should().Be(timeSpan); - } - - [Test] - public void should_return_time_span_zero_for_db_null_value_when_getting_from_db() - { - var context = new ConverterContext - { - DbValue = DBNull.Value - }; - - Subject.FromDB(context).Should().Be(TimeSpan.Zero); - } - } -} diff --git a/src/NzbDrone.Core.Test/Datastore/Converters/UtcConverterFixture.cs b/src/NzbDrone.Core.Test/Datastore/Converters/UtcConverterFixture.cs index 904f653d3..da6c1a9e6 100644 --- a/src/NzbDrone.Core.Test/Datastore/Converters/UtcConverterFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/Converters/UtcConverterFixture.cs @@ -1,6 +1,6 @@ using System; +using System.Data.SQLite; using FluentAssertions; -using Marr.Data.Converters; using NUnit.Framework; using NzbDrone.Core.Datastore.Converters; using NzbDrone.Core.Test.Framework; @@ -8,20 +8,23 @@ namespace NzbDrone.Core.Test.Datastore.Converters { [TestFixture] - public class UtcConverterFixture : CoreTest + public class UtcConverterFixture : CoreTest { + SQLiteParameter param; + + [SetUp] + public void Setup() + { + param = new SQLiteParameter(); + } + [Test] public void should_return_date_time_when_saving_date_time_to_db() { var dateTime = DateTime.Now; - Subject.ToDB(dateTime).Should().Be(dateTime.ToUniversalTime()); - } - - [Test] - public void should_return_db_null_when_saving_db_null_to_db() - { - Subject.ToDB(DBNull.Value).Should().Be(DBNull.Value); + Subject.SetValue(param, dateTime); + param.Value.Should().Be(dateTime.ToUniversalTime()); } [Test] @@ -29,23 +32,7 @@ public void should_return_time_span_when_getting_time_span_from_db() { var dateTime = DateTime.Now.ToUniversalTime(); - var context = new ConverterContext - { - DbValue = dateTime - }; - - Subject.FromDB(context).Should().Be(dateTime); - } - - [Test] - public void should_return_db_null_for_db_null_value_when_getting_from_db() - { - var context = new ConverterContext - { - DbValue = DBNull.Value - }; - - Subject.FromDB(context).Should().Be(DBNull.Value); + Subject.Parse(dateTime).Should().Be(dateTime); } } } diff --git a/src/NzbDrone.Core.Test/Datastore/DatabaseFixture.cs b/src/NzbDrone.Core.Test/Datastore/DatabaseFixture.cs index 0196db290..47f79ab86 100644 --- a/src/NzbDrone.Core.Test/Datastore/DatabaseFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/DatabaseFixture.cs @@ -1,10 +1,11 @@ using System; using System.Linq; +using Dapper; using FluentAssertions; using NUnit.Framework; using NzbDrone.Core.Datastore; -using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Movies; +using NzbDrone.Core.Test.Framework; namespace NzbDrone.Core.Test.Datastore { @@ -14,15 +15,15 @@ public class DatabaseFixture : DbTest public void SingleOrDefault_should_return_null_on_empty_db() { Mocker.Resolve() - .GetDataMapper().Query() - .SingleOrDefault(c => c.CleanTitle == "SomeTitle") + .OpenConnection().Query("SELECT * FROM Movies") + .SingleOrDefault() .Should() .BeNull(); } [Test] - public void vaccume() + public void vacuum() { Mocker.Resolve().Vacuum(); } diff --git a/src/NzbDrone.Core.Test/Datastore/DatabaseRelationshipFixture.cs b/src/NzbDrone.Core.Test/Datastore/DatabaseRelationshipFixture.cs index 27810c21d..8a4380f48 100644 --- a/src/NzbDrone.Core.Test/Datastore/DatabaseRelationshipFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/DatabaseRelationshipFixture.cs @@ -2,10 +2,8 @@ using FizzWare.NBuilder; using FluentAssertions; using NUnit.Framework; -using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Qualities; using NzbDrone.Core.Test.Framework; -using NzbDrone.Core.Movies; using System.Collections.Generic; using NzbDrone.Core.Languages; @@ -20,48 +18,6 @@ public void Setup() // This is kinda hacky here, since we are kinda testing if the QualityDef converter works as well. } - [Ignore("MovieFile isnt lazy loaded anymore so this will fail.")] - [Test] - //TODO: Update this! - public void one_to_one() - { - var episodeFile = Builder.CreateNew() - .With(c => c.Quality = new QualityModel()) - .BuildNew(); - - Db.Insert(episodeFile); - - var episode = Builder.CreateNew() - .With(c => c.MovieFileId = episodeFile.Id) - .BuildNew(); - - Db.Insert(episode); - - var loadedEpisode = Db.Single(); - var loadedEpisodeFile = loadedEpisode.MovieFile; - - loadedEpisodeFile.Should().NotBeNull(); - loadedEpisodeFile.Should().BeEquivalentTo(episodeFile, - options => options - .IncludingAllRuntimeProperties() - .Excluding(c => c.DateAdded) - .Excluding(c => c.Path) - .Excluding(c => c.Movie)); - } - - [Test] - public void one_to_one_should_not_query_db_if_foreign_key_is_zero() - { - var episode = Builder.CreateNew() - .With(c => c.MovieFileId = 0) - .BuildNew(); - - Db.Insert(episode); - - Db.Single().MovieFile.Should().BeNull(); - } - - [Test] public void embedded_document_as_json() { diff --git a/src/NzbDrone.Core.Test/Datastore/MarrDataLazyLoadingFixture.cs b/src/NzbDrone.Core.Test/Datastore/MarrDataLazyLoadingFixture.cs deleted file mode 100644 index 2200b0d44..000000000 --- a/src/NzbDrone.Core.Test/Datastore/MarrDataLazyLoadingFixture.cs +++ /dev/null @@ -1,55 +0,0 @@ -using FizzWare.NBuilder; -using NUnit.Framework; -using NzbDrone.Core.Datastore; -using NzbDrone.Core.Profiles; -using NzbDrone.Core.Test.Framework; -using NzbDrone.Core.Movies; -using NzbDrone.Core.Qualities; -using NzbDrone.Core.MediaFiles; - -namespace NzbDrone.Core.Test.Datastore -{ - - [TestFixture] - public class MarrDataLazyLoadingFixture : DbTest - { - [SetUp] - public void Setup() - { - var profile = new Profile - { - Name = "Test", - Cutoff = Quality.WEBDL720p.Id, - Items = Qualities.QualityFixture.GetDefaultQualities() - }; - - - profile = Db.Insert(profile); - - var series = Builder.CreateListOfSize(1) - .All() - .With(v => v.ProfileId = profile.Id) - .BuildListOfNew(); - - Db.InsertMany(series); - - var episodeFiles = Builder.CreateListOfSize(1) - .All() - .With(v => v.MovieId = series[0].Id) - .With(v => v.Quality = new QualityModel()) - .BuildListOfNew(); - - Db.InsertMany(episodeFiles); - - var episodes = Builder.CreateListOfSize(10) - .All() - .With(v => v.Monitored = true) - .With(v => v.MovieFileId = episodeFiles[0].Id) - .BuildListOfNew(); - - Db.InsertMany(episodes); - } - - - } -} diff --git a/src/NzbDrone.Core.Test/Datastore/PagingSpecExtensionsTests/PagingOffsetFixture.cs b/src/NzbDrone.Core.Test/Datastore/PagingSpecExtensionsTests/PagingOffsetFixture.cs deleted file mode 100644 index ad64ff036..000000000 --- a/src/NzbDrone.Core.Test/Datastore/PagingSpecExtensionsTests/PagingOffsetFixture.cs +++ /dev/null @@ -1,29 +0,0 @@ -using FluentAssertions; -using NUnit.Framework; -using NzbDrone.Core.Datastore; -using NzbDrone.Core.Datastore.Extensions; -using NzbDrone.Core.Movies; - -namespace NzbDrone.Core.Test.Datastore.PagingSpecExtensionsTests -{ - public class PagingOffsetFixture - { - [TestCase(1, 10, 0)] - [TestCase(2, 10, 10)] - [TestCase(3, 20, 40)] - [TestCase(1, 100, 0)] - public void should_calcuate_expected_offset(int page, int pageSize, int expected) - { - var pagingSpec = new PagingSpec - { - Page = page, - PageSize = pageSize, - SortDirection = SortDirection.Ascending, - SortKey = "AirDate" - }; - - pagingSpec.PagingOffset().Should().Be(expected); - } - - } -} diff --git a/src/NzbDrone.Core.Test/Datastore/PagingSpecExtensionsTests/ToSortDirectionFixture.cs b/src/NzbDrone.Core.Test/Datastore/PagingSpecExtensionsTests/ToSortDirectionFixture.cs deleted file mode 100644 index 0067adb1f..000000000 --- a/src/NzbDrone.Core.Test/Datastore/PagingSpecExtensionsTests/ToSortDirectionFixture.cs +++ /dev/null @@ -1,53 +0,0 @@ -using FluentAssertions; -using NUnit.Framework; -using NzbDrone.Core.Datastore; -using NzbDrone.Core.Datastore.Extensions; -using NzbDrone.Core.Movies; - -namespace NzbDrone.Core.Test.Datastore.PagingSpecExtensionsTests -{ - public class ToSortDirectionFixture - { - [Test] - public void should_convert_default_to_asc() - { - var pagingSpec = new PagingSpec - { - Page = 1, - PageSize = 10, - SortDirection = SortDirection.Default, - SortKey = "AirDate" - }; - - pagingSpec.ToSortDirection().Should().Be(Marr.Data.QGen.SortDirection.Asc); - } - - [Test] - public void should_convert_ascending_to_asc() - { - var pagingSpec = new PagingSpec - { - Page = 1, - PageSize = 10, - SortDirection = SortDirection.Ascending, - SortKey = "AirDate" - }; - - pagingSpec.ToSortDirection().Should().Be(Marr.Data.QGen.SortDirection.Asc); - } - - [Test] - public void should_convert_descending_to_desc() - { - var pagingSpec = new PagingSpec - { - Page = 1, - PageSize = 10, - SortDirection = SortDirection.Descending, - SortKey = "AirDate" - }; - - pagingSpec.ToSortDirection().Should().Be(Marr.Data.QGen.SortDirection.Desc); - } - } -} diff --git a/src/NzbDrone.Core.Test/Datastore/ReflectionStrategyFixture/Benchmarks.cs b/src/NzbDrone.Core.Test/Datastore/ReflectionStrategyFixture/Benchmarks.cs deleted file mode 100644 index 48a1a3639..000000000 --- a/src/NzbDrone.Core.Test/Datastore/ReflectionStrategyFixture/Benchmarks.cs +++ /dev/null @@ -1,40 +0,0 @@ -using NUnit.Framework; - -namespace NzbDrone.Core.Test.Datastore.ReflectionStrategyFixture -{ - [TestFixture] - public class Benchmarks - { -/* private const int iterations = 5000000; - private object _target; - private IReflectionStrategy _simpleReflectionStrategy; - - [SetUp] - public void Setup() - { - // _simpleReflectionStrategy = new DelegateReflectionStrategy(); - } - - [Test] - public void clr_reflection_test() - { - _target = new Series(); - - var del = _simpleReflectionStrategy.BuildSetter(typeof(Series), "Title"); - - for (int i = 0; i < iterations; i++) - { - del(_target, "TestTile"); - //_simpleReflectionStrategy.SetFieldValue(_target, "Title", "TestTile"); - } - } - - - private void SetField() - { - - - }*/ - - } -} \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/Datastore/MappingExtentionFixture.cs b/src/NzbDrone.Core.Test/Datastore/TableMapperFixture.cs similarity index 69% rename from src/NzbDrone.Core.Test/Datastore/MappingExtentionFixture.cs rename to src/NzbDrone.Core.Test/Datastore/TableMapperFixture.cs index a8fc9670f..e71c800a7 100644 --- a/src/NzbDrone.Core.Test/Datastore/MappingExtentionFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/TableMapperFixture.cs @@ -1,16 +1,15 @@ using System.Collections.Generic; +using Dapper; using FluentAssertions; -using Marr.Data; using NUnit.Framework; using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore.Converters; -using NzbDrone.Core.Datastore.Extensions; using NzbDrone.Core.Movies; namespace NzbDrone.Core.Test.Datastore { [TestFixture] - public class MappingExtensionFixture + public class TableMapperFixture { public class EmbeddedType : IEmbeddedDocument @@ -39,19 +38,16 @@ public class TypeWithNoMappableProperties [SetUp] public void Setup() { - MapRepository.Instance.RegisterTypeConverter(typeof(List), new EmbeddedDocumentConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(EmbeddedType), new EmbeddedDocumentConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(int), new Int32Converter()); - + SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter>()); + SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter()); } - [Test] public void test_mappable_types() { var properties = typeof(TypeWithAllMappableProperties).GetProperties(); properties.Should().NotBeEmpty(); - properties.Should().OnlyContain(c => MappingExtensions.IsMappableProperty(c)); + properties.Should().OnlyContain(c => ColumnMapper.IsMappableProperty(c)); } [Test] @@ -59,7 +55,7 @@ public void test_un_mappable_types() { var properties = typeof(TypeWithNoMappableProperties).GetProperties(); properties.Should().NotBeEmpty(); - properties.Should().NotContain(c => MappingExtensions.IsMappableProperty(c)); + properties.Should().NotContain(c => ColumnMapper.IsMappableProperty(c)); } } } diff --git a/src/NzbDrone.Core.Test/Datastore/WhereBuilderFixture.cs b/src/NzbDrone.Core.Test/Datastore/WhereBuilderFixture.cs new file mode 100644 index 000000000..01772c9e1 --- /dev/null +++ b/src/NzbDrone.Core.Test/Datastore/WhereBuilderFixture.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Datastore; +using NzbDrone.Core.Movies; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.Datastore +{ + [TestFixture] + public class WhereBuilderFixture : CoreTest + { + private WhereBuilder Subject; + + [OneTimeSetUp] + public void MapTables() + { + // Generate table mapping + Mocker.Resolve(); + } + + private WhereBuilder Where(Expression> filter) + { + return new WhereBuilder(filter); + } + + [Test] + public void where_equal_const() + { + Subject = Where(x => x.Id == 10); + + var name = Subject.Parameters.ParameterNames.First(); + Subject.ToString().Should().Be($"(\"Movies\".\"Id\" = @{name})"); + Subject.Parameters.Get(name).Should().Be(10); + } + + [Test] + public void where_equal_variable() + { + var id = 10; + Subject = Where(x => x.Id == id); + + var name = Subject.Parameters.ParameterNames.First(); + Subject.ToString().Should().Be($"(\"Movies\".\"Id\" = @{name})"); + Subject.Parameters.Get(name).Should().Be(id); + } + + [Test] + public void where_column_contains_string() + { + var test = "small"; + Subject = Where(x => x.CleanTitle.Contains(test)); + + var name = Subject.Parameters.ParameterNames.First(); + Subject.ToString().Should().Be($"(\"Movies\".\"CleanTitle\" LIKE '%' || @{name} || '%')"); + Subject.Parameters.Get(name).Should().Be(test); + } + + [Test] + public void where_string_contains_column() + { + var test = "small"; + Subject = Where(x => test.Contains(x.CleanTitle)); + + var name = Subject.Parameters.ParameterNames.First(); + Subject.ToString().Should().Be($"(@{name} LIKE '%' || \"Movies\".\"CleanTitle\" || '%')"); + Subject.Parameters.Get(name).Should().Be(test); + } + + [Test] + public void where_in_list() + { + var list = new List {1, 2, 3}; + Subject = Where(x => list.Contains(x.Id)); + + var name = Subject.Parameters.ParameterNames.First(); + Subject.ToString().Should().Be($"(\"Movies\".\"Id\" IN @{name})"); + + var param = Subject.Parameters.Get>(name); + param.Should().BeEquivalentTo(list); + } + + [Test] + public void where_in_list_2() + { + var list = new List {1, 2, 3}; + Subject = Where(x => x.CleanTitle == "test" && list.Contains(x.Id)); + + var names = Subject.Parameters.ParameterNames.ToList(); + Subject.ToString().Should().Be($"((\"Movies\".\"CleanTitle\" = @{names[0]}) AND (\"Movies\".\"Id\" IN @{names[1]}))"); + } + + [Test] + public void enum_as_int() + { + Subject = Where(x => x.PathState == MoviePathState.Static); + + var name = Subject.Parameters.ParameterNames.First(); + Subject.ToString().Should().Be($"(\"Movies\".\"PathState\" = @{name})"); + } + + [Test] + public void enum_in_list() + { + var allowed = new List { MoviePathState.Dynamic, MoviePathState.Static }; + Subject = Where(x => allowed.Contains(x.PathState)); + + var name = Subject.Parameters.ParameterNames.First(); + Subject.ToString().Should().Be($"(\"Movies\".\"PathState\" IN @{name})"); + } + + [Test] + public void enum_in_array() + { + var allowed = new MoviePathState[] { MoviePathState.Dynamic, MoviePathState.Static }; + Subject = Where(x => allowed.Contains(x.PathState)); + + var name = Subject.Parameters.ParameterNames.First(); + Subject.ToString().Should().Be($"(\"Movies\".\"PathState\" IN @{name})"); + } + } +} diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/CustomFormatAllowedByProfileSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/CustomFormatAllowedByProfileSpecificationFixture.cs index 970cd52da..e6eec3a34 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/CustomFormatAllowedByProfileSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/CustomFormatAllowedByProfileSpecificationFixture.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using FizzWare.NBuilder; using FluentAssertions; -using Marr.Data; using NUnit.Framework; using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.Parser.Model; @@ -33,7 +32,7 @@ public void Setup() var fakeSeries = Builder.CreateNew() - .With(c => c.Profile = (LazyLoaded)new Profile { Cutoff = Quality.Bluray1080p.Id }) + .With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p.Id }) .Build(); remoteMovie = new RemoteMovie @@ -49,7 +48,7 @@ public void Setup() public void should_allow_if_format_is_defined_in_profile() { remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List {_format1}; - remoteMovie.Movie.Profile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); + remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeTrue(); } @@ -58,7 +57,7 @@ public void should_allow_if_format_is_defined_in_profile() public void should_deny_if_format_is_defined_in_profile() { remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List {_format2}; - remoteMovie.Movie.Profile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); + remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeFalse(); } @@ -67,7 +66,7 @@ public void should_deny_if_format_is_defined_in_profile() public void should_deny_if_one_format_is_defined_in_profile() { remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List {_format2, _format1}; - remoteMovie.Movie.Profile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); + remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeFalse(); } @@ -76,7 +75,7 @@ public void should_deny_if_one_format_is_defined_in_profile() public void should_allow_if_all_format_is_defined_in_profile() { remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List {_format2, _format1}; - remoteMovie.Movie.Profile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name); + remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeTrue(); } @@ -85,7 +84,7 @@ public void should_allow_if_all_format_is_defined_in_profile() public void should_deny_if_no_format_was_parsed_and_none_not_in_profile() { remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List {}; - remoteMovie.Movie.Profile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name); + remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeFalse(); } @@ -94,7 +93,7 @@ public void should_deny_if_no_format_was_parsed_and_none_not_in_profile() public void should_allow_if_no_format_was_parsed_and_none_in_profile() { remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List {}; - remoteMovie.Movie.Profile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(CustomFormats.CustomFormat.None.Name, _format1.Name, _format2.Name); + remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(CustomFormats.CustomFormat.None.Name, _format1.Name, _format2.Name); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeTrue(); } diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/LanguageSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/LanguageSpecificationFixture.cs index 9057b005e..7418b8d5d 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/LanguageSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/LanguageSpecificationFixture.cs @@ -1,9 +1,7 @@ using System.Collections.Generic; using FluentAssertions; -using Marr.Data; using NUnit.Framework; using NzbDrone.Core.DecisionEngine.Specifications; -using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Profiles; using NzbDrone.Core.Test.Framework; @@ -29,10 +27,10 @@ public void Setup() }, Movie = new Movie { - Profile = new LazyLoaded(new Profile - { - Language = Language.English - }) + Profile = new Profile + { + Language = Language.English + } } }; } @@ -66,10 +64,10 @@ public void should_return_false_if_language_is_german() [Test] public void should_return_true_if_allowed_language_any() { - _remoteMovie.Movie.Profile = new LazyLoaded(new Profile + _remoteMovie.Movie.Profile = new Profile { Language = Language.Any - }); + }; WithGermanRelease(); diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/QualityAllowedByProfileSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/QualityAllowedByProfileSpecificationFixture.cs index 980e7363f..1b36f6b22 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/QualityAllowedByProfileSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/QualityAllowedByProfileSpecificationFixture.cs @@ -1,6 +1,5 @@ using FizzWare.NBuilder; using FluentAssertions; -using Marr.Data; using NUnit.Framework; using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.Parser.Model; @@ -35,7 +34,7 @@ public class QualityAllowedByProfileSpecificationFixture : CoreTest.CreateNew() - .With(c => c.Profile = (LazyLoaded)new Profile { Cutoff = Quality.Bluray1080p.Id }) + .With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p.Id }) .Build(); remoteMovie = new RemoteMovie @@ -49,7 +48,7 @@ public void Setup() public void should_allow_if_quality_is_defined_in_profile(Quality qualityType) { remoteMovie.ParsedMovieInfo.Quality.Quality = qualityType; - remoteMovie.Movie.Profile.Value.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p); + remoteMovie.Movie.Profile.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeTrue(); } @@ -58,7 +57,7 @@ public void should_allow_if_quality_is_defined_in_profile(Quality qualityType) public void should_not_allow_if_quality_is_not_defined_in_profile(Quality qualityType) { remoteMovie.ParsedMovieInfo.Quality.Quality = qualityType; - remoteMovie.Movie.Profile.Value.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p); + remoteMovie.Movie.Profile.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeFalse(); } diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/QueueSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/QueueSpecificationFixture.cs index 45b5af5c6..b4e3feb6c 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/QueueSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/QueueSpecificationFixture.cs @@ -83,7 +83,7 @@ public void should_return_true_when_movie_doesnt_match() [Test] public void should_return_true_when_quality_in_queue_is_lower() { - _movie.Profile.Value.Cutoff = Quality.Bluray1080p.Id; + _movie.Profile.Cutoff = Quality.Bluray1080p.Id; var remoteMovie = Builder.CreateNew() .With(r => r.Movie = _movie) @@ -115,7 +115,7 @@ public void should_return_false_when_qualities_are_the_same() [Test] public void should_return_false_when_quality_in_queue_is_better() { - _movie.Profile.Value.Cutoff = Quality.Bluray1080p.Id; + _movie.Profile.Cutoff = Quality.Bluray1080p.Id; var remoteMovie = Builder.CreateNew() .With(r => r.Movie = _movie) @@ -132,7 +132,7 @@ public void should_return_false_when_quality_in_queue_is_better() [Test] public void should_return_false_if_quality_in_queue_meets_cutoff() { - _movie.Profile.Value.Cutoff = _remoteMovie.ParsedMovieInfo.Quality.Quality.Id; + _movie.Profile.Cutoff = _remoteMovie.ParsedMovieInfo.Quality.Quality.Id; var remoteMovie = Builder.CreateNew() .With(r => r.Movie = _movie) @@ -151,8 +151,8 @@ public void should_return_false_if_quality_in_queue_meets_cutoff() [Test] public void should_return_false_when_quality_is_better_and_upgrade_allowed_is_false_for_quality_profile() { - _movie.Profile.Value.Cutoff = Quality.Bluray1080p.Id; - _movie.Profile.Value.UpgradeAllowed = false; + _movie.Profile.Cutoff = Quality.Bluray1080p.Id; + _movie.Profile.UpgradeAllowed = false; var remoteMovie = Builder.CreateNew() .With(r => r.Movie = _movie) diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs index 83f009722..8ec870095 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; -using System.Linq; using FizzWare.NBuilder; using FluentAssertions; -using Marr.Data; using Moq; using NUnit.Framework; using NzbDrone.Core.DecisionEngine.Specifications; @@ -78,7 +76,7 @@ private void GivenExistingFile(QualityModel quality) // Quality = quality // }); - _remoteEpisode.Movie.MovieFile = new LazyLoaded(new MovieFile { Quality = quality }); + _remoteEpisode.Movie.MovieFile = new MovieFile { Quality = quality }; } private void GivenUpgradeForExistingFile() diff --git a/src/NzbDrone.Core.Test/DiskSpace/DiskSpaceServiceFixture.cs b/src/NzbDrone.Core.Test/DiskSpace/DiskSpaceServiceFixture.cs index 154227db6..cec2506b4 100644 --- a/src/NzbDrone.Core.Test/DiskSpace/DiskSpaceServiceFixture.cs +++ b/src/NzbDrone.Core.Test/DiskSpace/DiskSpaceServiceFixture.cs @@ -50,8 +50,8 @@ public void SetUp() private void GivenMovies(params Movie[] movies) { Mocker.GetMock() - .Setup(v => v.GetAllMovies()) - .Returns(movies.ToList()); + .Setup(v => v.AllMoviePaths()) + .Returns(movies.Select(x => x.Path).ToList()); } private void GivenExistingFolder(string folder) diff --git a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/AddFixture.cs b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/AddFixture.cs index 39d526e2c..964a19c9e 100644 --- a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/AddFixture.cs +++ b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/AddFixture.cs @@ -1,13 +1,11 @@ using System; using System.Collections.Generic; using FizzWare.NBuilder; -using Marr.Data; using Moq; using NUnit.Framework; using NzbDrone.Common.Extensions; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download.Pending; -using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Profiles; using NzbDrone.Core.Qualities; @@ -46,7 +44,7 @@ public void Setup() }, }; - _movie.Profile = new LazyLoaded(_profile); + _movie.Profile = _profile; _release = Builder.CreateNew().Build(); diff --git a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveGrabbedFixture.cs b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveGrabbedFixture.cs index 670865e40..fe269bdfe 100644 --- a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveGrabbedFixture.cs +++ b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveGrabbedFixture.cs @@ -1,19 +1,17 @@ using System.Collections.Generic; -using FizzWare.NBuilder; -using Marr.Data; -using Moq; -using NUnit.Framework; -using NzbDrone.Common.Extensions; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Download.Pending; -using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Profiles; using NzbDrone.Core.Qualities; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Movies; using System.Linq; +using FizzWare.NBuilder; +using Moq; +using NzbDrone.Common.Extensions; +using NUnit.Framework; namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests { @@ -46,7 +44,7 @@ public void Setup() }, }; - _movie.Profile = new LazyLoaded(_profile); + _movie.Profile = _profile; _release = Builder.CreateNew().Build(); diff --git a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveRejectedFixture.cs b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveRejectedFixture.cs index 3c0cc0ef9..5d287ed0e 100644 --- a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveRejectedFixture.cs +++ b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveRejectedFixture.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using FizzWare.NBuilder; -using Marr.Data; using Moq; using NUnit.Framework; using NzbDrone.Common.Extensions; @@ -47,7 +46,7 @@ public void Setup() }, }; - _movie.Profile = new LazyLoaded(_profile); + _movie.Profile = _profile; _release = Builder.CreateNew().Build(); diff --git a/src/NzbDrone.Core.Test/Framework/DbTest.cs b/src/NzbDrone.Core.Test/Framework/DbTest.cs index 1d7ddd6be..cbb40525d 100644 --- a/src/NzbDrone.Core.Test/Framework/DbTest.cs +++ b/src/NzbDrone.Core.Test/Framework/DbTest.cs @@ -1,10 +1,7 @@ using System; using System.Collections.Generic; using System.Data.SQLite; -using System.IO; using System.Linq; -using FluentMigrator.Runner; -using Marr.Data; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using NUnit.Framework; @@ -111,7 +108,7 @@ protected void SetupContainer() Mocker.SetConstant(Mocker.Resolve()); Mocker.SetConstant(Mocker.Resolve()); - MapRepository.Instance.EnableTraceLogging = true; + SqlBuilderExtensions.LogSql = true; } [SetUp] diff --git a/src/NzbDrone.Core.Test/Framework/DirectDataMapper.cs b/src/NzbDrone.Core.Test/Framework/DirectDataMapper.cs index 27b4354c1..2e7a27271 100644 --- a/src/NzbDrone.Core.Test/Framework/DirectDataMapper.cs +++ b/src/NzbDrone.Core.Test/Framework/DirectDataMapper.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Data; -using System.Data.Common; using System.Linq; using NzbDrone.Common.Serializer; using NzbDrone.Core.Datastore; @@ -16,27 +15,16 @@ public interface IDirectDataMapper public class DirectDataMapper : IDirectDataMapper { - private readonly DbProviderFactory _providerFactory; - private readonly string _connectionString; + private readonly IDatabase _database; public DirectDataMapper(IDatabase database) { - var dataMapper = database.GetDataMapper(); - _providerFactory = dataMapper.ProviderFactory; - _connectionString = dataMapper.ConnectionString; + _database = database; } - private DbConnection OpenConnection() - { - var connection = _providerFactory.CreateConnection(); - connection.ConnectionString = _connectionString; - connection.Open(); - return connection; - } - public DataTable GetDataTable(string sql) { - using (var connection = OpenConnection()) + using (var connection = _database.OpenConnection()) { using (var cmd = connection.CreateCommand()) { diff --git a/src/NzbDrone.Core.Test/HealthCheck/Checks/RootFolderCheckFixture.cs b/src/NzbDrone.Core.Test/HealthCheck/Checks/RootFolderCheckFixture.cs index bf1a06cf4..7ad2d56c3 100644 --- a/src/NzbDrone.Core.Test/HealthCheck/Checks/RootFolderCheckFixture.cs +++ b/src/NzbDrone.Core.Test/HealthCheck/Checks/RootFolderCheckFixture.cs @@ -20,8 +20,8 @@ private void GivenMissingRootFolder() .ToList(); Mocker.GetMock() - .Setup(s => s.GetAllMovies()) - .Returns(movies); + .Setup(s => s.AllMoviePaths()) + .Returns(movies.Select(x => x.Path).ToList()); Mocker.GetMock() .Setup(s => s.GetParentFolder(movies.First().Path)) @@ -36,8 +36,8 @@ private void GivenMissingRootFolder() public void should_not_return_error_when_no_movie() { Mocker.GetMock() - .Setup(s => s.GetAllMovies()) - .Returns(new List()); + .Setup(s => s.AllMoviePaths()) + .Returns(new List()); Subject.Check().ShouldBeOk(); } diff --git a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupAdditionalUsersFixture.cs b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupAdditionalUsersFixture.cs index 065b9d375..df20e02ef 100644 --- a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupAdditionalUsersFixture.cs +++ b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupAdditionalUsersFixture.cs @@ -1,4 +1,5 @@ -using FizzWare.NBuilder; +using System; +using FizzWare.NBuilder; using FluentAssertions; using NUnit.Framework; using NzbDrone.Core.Authentication; @@ -14,7 +15,11 @@ public class CleanupAdditionalUsersFixture : DbTest.CreateListOfSize(5) - .BuildListOfNew(); + .All() + .With(x => x.Id = 0) + .BuildListOfNew(); + + specs.ForEach(x => x.Identifier = Guid.NewGuid()); Db.InsertMany(specs); @@ -26,7 +31,9 @@ public void should_delete_additional_users() public void should_not_delete_if_only_one_user() { var spec = Builder.CreateNew() - .BuildNew(); + .With(x => x.Id = 0) + .With(x => x.Identifier = Guid.NewGuid()) + .BuildNew(); Db.Insert(spec); @@ -34,4 +41,4 @@ public void should_not_delete_if_only_one_user() AllStoredModels.Should().HaveCount(1); } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupUnusedTagsFixture.cs b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupUnusedTagsFixture.cs index fa7401ef5..99515c1c6 100644 --- a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupUnusedTagsFixture.cs +++ b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupUnusedTagsFixture.cs @@ -14,7 +14,10 @@ public class CleanupUnusedTagsFixture : DbTest [Test] public void should_delete_unused_tags() { - var tags = Builder.CreateListOfSize(2).BuildList(); + var tags = Builder.CreateListOfSize(2) + .All() + .With(x => x.Id = 0) + .BuildList(); Db.InsertMany(tags); Subject.Clean(); @@ -24,11 +27,15 @@ public void should_delete_unused_tags() [Test] public void should_not_delete_used_tags() { - var tags = Builder.CreateListOfSize(2).BuildList(); + var tags = Builder.CreateListOfSize(2) + .All() + .With(x => x.Id = 0) + .BuildList(); Db.InsertMany(tags); var restrictions = Builder.CreateListOfSize(2) .All() + .With(v => v.Id = 0) .With(v => v.Tags.Add(tags[0].Id)) .BuildList(); Db.InsertMany(restrictions); diff --git a/src/NzbDrone.Core.Test/InstrumentationTests/DatabaseTargetFixture.cs b/src/NzbDrone.Core.Test/InstrumentationTests/DatabaseTargetFixture.cs index 6608bd42f..2863ae5e8 100644 --- a/src/NzbDrone.Core.Test/InstrumentationTests/DatabaseTargetFixture.cs +++ b/src/NzbDrone.Core.Test/InstrumentationTests/DatabaseTargetFixture.cs @@ -1,7 +1,6 @@ using System; using System.Threading; using FluentAssertions; -using Marr.Data; using NLog; using NUnit.Framework; using NzbDrone.Common.Instrumentation; @@ -10,7 +9,6 @@ using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Test.Framework; using NzbDrone.Test.Common; -using NzbDrone.Test.Common.Categories; namespace NzbDrone.Core.Test.InstrumentationTests { @@ -64,23 +62,6 @@ public void write_long_log() VerifyLog(StoredModel, LogLevel.Info); } - - [Test] - [Explicit] - [ManualTest] - public void perf_test() - { - MapRepository.Instance.EnableTraceLogging = false; - for (int i = 0; i < 1000; i++) - { - _logger.Info(Guid.NewGuid()); - } - - Thread.Sleep(1000); - - MapRepository.Instance.EnableTraceLogging = true; - } - [Test] public void write_log_exception() { diff --git a/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Specifications/SameFileSpecificationFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Specifications/SameFileSpecificationFixture.cs index 344cafaad..c29e4ae33 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Specifications/SameFileSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Specifications/SameFileSpecificationFixture.cs @@ -1,7 +1,5 @@ -using System.Linq; using FizzWare.NBuilder; using FluentAssertions; -using Marr.Data; using NUnit.Framework; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles.MovieImport.Specifications; @@ -39,11 +37,11 @@ public void should_be_accepted_if_file_size_is_different() { _localMovie.Movie = Builder.CreateNew() .With(e => e.MovieFileId = 1) - .With(e => e.MovieFile = new LazyLoaded( + .With(e => e.MovieFile = new MovieFile { Size = _localMovie.Size + 100.Megabytes() - })) + }) .Build(); Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeTrue(); @@ -54,11 +52,11 @@ public void should_be_reject_if_file_size_is_the_same() { _localMovie.Movie = Builder.CreateNew() .With(e => e.MovieFileId = 1) - .With(e => e.MovieFile = new LazyLoaded( + .With(e => e.MovieFile = new MovieFile { Size = _localMovie.Size - })) + }) .Build(); Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeFalse(); @@ -69,7 +67,7 @@ public void should_be_accepted_if_file_cannot_be_fetched() { _localMovie.Movie = Builder.CreateNew() .With(e => e.MovieFileId = 1) - .With(e => e.MovieFile = new LazyLoaded((MovieFile)null)) + .With(e => e.MovieFile = null) .Build(); Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeTrue(); diff --git a/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Specifications/UpgradeSpecificationFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Specifications/UpgradeSpecificationFixture.cs index 5dca61469..664e245b5 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Specifications/UpgradeSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Specifications/UpgradeSpecificationFixture.cs @@ -1,7 +1,5 @@ -using System.Linq; using FizzWare.NBuilder; using FluentAssertions; -using Marr.Data; using NUnit.Framework; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles.MovieImport.Specifications; @@ -48,13 +46,11 @@ public void should_return_true_if_upgrade_for_existing_episodeFile() { _localMovie.Movie.MovieFileId = 1; - _localMovie.Movie.MovieFile = new LazyLoaded( + _localMovie.Movie.MovieFile = new MovieFile { Quality = new QualityModel(Quality.SDTV, new Revision(version: 1)) - } - ); - + }; Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeTrue(); } @@ -64,12 +60,11 @@ public void should_return_true_if_upgrade_for_existing_episodeFile() public void should_return_false_if_not_an_upgrade_for_existing_episodeFile() { _localMovie.Movie.MovieFileId = 1; - _localMovie.Movie.MovieFile = new LazyLoaded( + _localMovie.Movie.MovieFile = new MovieFile { Quality = new QualityModel(Quality.Bluray720p, new Revision(version: 1)) - } - ); + }; Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeFalse(); } diff --git a/src/NzbDrone.Core.Test/MediaFiles/UpgradeMediaFileServiceFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/UpgradeMediaFileServiceFixture.cs index e86dc3086..ce7e9fc34 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/UpgradeMediaFileServiceFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/UpgradeMediaFileServiceFixture.cs @@ -1,7 +1,5 @@ -using System.Linq; using FizzWare.NBuilder; using FluentAssertions; -using Marr.Data; using Moq; using NUnit.Framework; using NzbDrone.Common.Disk; diff --git a/src/NzbDrone.Core.Test/MovieTests/MovieRepositoryTests/MovieRepositoryFixture.cs b/src/NzbDrone.Core.Test/MovieTests/MovieRepositoryTests/MovieRepositoryFixture.cs index b93735053..dd7fb9a40 100644 --- a/src/NzbDrone.Core.Test/MovieTests/MovieRepositoryTests/MovieRepositoryFixture.cs +++ b/src/NzbDrone.Core.Test/MovieTests/MovieRepositoryTests/MovieRepositoryFixture.cs @@ -5,6 +5,8 @@ using NzbDrone.Core.Qualities; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Movies; +using System; +using System.Linq; namespace NzbDrone.Core.Test.MovieTests.MovieRepositoryTests { @@ -12,13 +14,17 @@ namespace NzbDrone.Core.Test.MovieTests.MovieRepositoryTests public class MovieRepositoryFixture : DbTest { + private IProfileRepository _profileRepository; + [SetUp] public void Setup() { + _profileRepository = Mocker.Resolve(); + Mocker.SetConstant(_profileRepository); } [Test] - public void should_lazyload_quality_profile() + public void should_load_quality_profile() { var profile = new Profile { @@ -29,16 +35,14 @@ public void should_lazyload_quality_profile() Name = "TestProfile" }; - - Mocker.Resolve().Insert(profile); + _profileRepository.Insert(profile); var movie = Builder.CreateNew().BuildNew(); movie.ProfileId = profile.Id; Subject.Insert(movie); - - StoredModel.Profile.Should().NotBeNull(); + Subject.All().Single().Profile.Should().NotBeNull(); } } } diff --git a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs index d65bb83a8..2d2cdc5e1 100644 --- a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs @@ -1,11 +1,9 @@ -using System; using System.Collections.Generic; using System.Linq; using FizzWare.NBuilder; using FluentAssertions; using Moq; using NUnit.Framework; -using NzbDrone.Core.Datastore; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Movies.AlternativeTitles; using NzbDrone.Core.Parser; @@ -36,7 +34,7 @@ public void Setup() .With(m => m.Title = "Fack Ju Göthe 2") .With(m => m.CleanTitle = "fackjugoethe2") .With(m => m.Year = 2015) - .With(m => m.AlternativeTitles = new LazyList( new List {new AlternativeTitle("Fack Ju Göthe 2: Same same")})) + .With(m => m.AlternativeTitles = new List {new AlternativeTitle("Fack Ju Göthe 2: Same same")}) .Build(); _parsedMovieInfo = new ParsedMovieInfo diff --git a/src/NzbDrone.Core.Test/Radarr.Core.Test.csproj b/src/NzbDrone.Core.Test/Radarr.Core.Test.csproj index 9dd7c95a7..50e0e2b8e 100644 --- a/src/NzbDrone.Core.Test/Radarr.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/Radarr.Core.Test.csproj @@ -7,6 +7,7 @@ + diff --git a/src/NzbDrone.Core/Authentication/UserRepository.cs b/src/NzbDrone.Core/Authentication/UserRepository.cs index fd5873190..b439149b3 100644 --- a/src/NzbDrone.Core/Authentication/UserRepository.cs +++ b/src/NzbDrone.Core/Authentication/UserRepository.cs @@ -20,12 +20,12 @@ public UserRepository(IMainDatabase database, IEventAggregator eventAggregator) public User FindUser(string username) { - return Query.Where(u => u.Username == username).SingleOrDefault(); + return Query(x => x.Username == username).SingleOrDefault(); } public User FindUser(Guid identifier) { - return Query.Where(u => u.Identifier == identifier).SingleOrDefault(); + return Query(x => x.Identifier == identifier).SingleOrDefault(); } } } diff --git a/src/NzbDrone.Core/Backup/MakeDatabaseBackup.cs b/src/NzbDrone.Core/Backup/MakeDatabaseBackup.cs index 2b82e4bc3..44d9186e6 100644 --- a/src/NzbDrone.Core/Backup/MakeDatabaseBackup.cs +++ b/src/NzbDrone.Core/Backup/MakeDatabaseBackup.cs @@ -27,7 +27,7 @@ public MakeDatabaseBackup(Logger logger) public void BackupDatabase(IDatabase database, string targetDirectory) { var sourceConnectionString = ""; - using (var db = database.GetDataMapper()) + using (var db = database.OpenConnection()) { sourceConnectionString = db.ConnectionString; } diff --git a/src/NzbDrone.Core/Blacklisting/BlacklistRepository.cs b/src/NzbDrone.Core/Blacklisting/BlacklistRepository.cs index fc398d89b..b0b175264 100644 --- a/src/NzbDrone.Core/Blacklisting/BlacklistRepository.cs +++ b/src/NzbDrone.Core/Blacklisting/BlacklistRepository.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using System.Linq; +using Dapper; using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; -using Marr.Data.QGen; using NzbDrone.Core.Movies; namespace NzbDrone.Core.Blacklisting @@ -22,26 +23,35 @@ public BlacklistRepository(IMainDatabase database, IEventAggregator eventAggrega public List BlacklistedByTitle(int movieId, string sourceTitle) { - return Query.Where(e => e.MovieId == movieId) - .AndWhere(e => e.SourceTitle.Contains(sourceTitle)).ToList(); + return Query(x => x.MovieId == movieId && x.SourceTitle.Contains(sourceTitle)); } public List BlacklistedByTorrentInfoHash(int movieId, string torrentInfoHash) { - return Query.Where(e => e.MovieId == movieId) - .AndWhere(e => e.TorrentInfoHash.Contains(torrentInfoHash)).ToList(); + return Query(x => x.MovieId == movieId && x.TorrentInfoHash.Contains(torrentInfoHash)); } public List BlacklistedByMovie(int movieId) { - return Query.Where(b => b.MovieId == movieId).ToList(); + return Query(x => x.MovieId == movieId); } - protected override SortBuilder GetPagedQuery(QueryBuilder query, PagingSpec pagingSpec) + private IEnumerable SelectJoined(SqlBuilder.Template sql) { - var baseQuery = query.Join(JoinType.Inner, h => h.Movie, (h, s) => h.MovieId == s.Id); - - return base.GetPagedQuery(baseQuery, pagingSpec); + using (var conn = _database.OpenConnection()) + { + return conn.Query( + sql.RawSql, + (bl, movie) => { + bl.Movie = movie; + return bl; + }, + sql.Parameters) + .ToList(); + } } + + protected override SqlBuilder PagedBuilder() => new SqlBuilder().Join("Movies ON Movies.Id = Blacklist.MovieId"); + protected override IEnumerable PagedSelector(SqlBuilder.Template sql) => SelectJoined(sql); } } diff --git a/src/NzbDrone.Core/Configuration/ConfigRepository.cs b/src/NzbDrone.Core/Configuration/ConfigRepository.cs index 23d3cbd7b..d1beb585d 100644 --- a/src/NzbDrone.Core/Configuration/ConfigRepository.cs +++ b/src/NzbDrone.Core/Configuration/ConfigRepository.cs @@ -18,10 +18,9 @@ public ConfigRepository(IMainDatabase database, IEventAggregator eventAggregator { } - public Config Get(string key) { - return Query.Where(c => c.Key == key).SingleOrDefault(); + return Query(c => c.Key == key).SingleOrDefault(); } public Config Upsert(string key, string value) diff --git a/src/NzbDrone.Core/CustomFormats/CustomFormat.cs b/src/NzbDrone.Core/CustomFormats/CustomFormat.cs index 3aa08fda7..79aaa0c6f 100644 --- a/src/NzbDrone.Core/CustomFormats/CustomFormat.cs +++ b/src/NzbDrone.Core/CustomFormats/CustomFormat.cs @@ -7,6 +7,10 @@ namespace NzbDrone.Core.CustomFormats { public class CustomFormat : ModelBase, IEquatable { + public string Name { get; set; } + + public List FormatTags { get; set; } + public CustomFormat() { @@ -18,9 +22,7 @@ public CustomFormat(string name, params string[] tags) FormatTags = tags.Select(t => new FormatTag(t)).ToList(); } - public string Name { get; set; } - - public List FormatTags { get; set; } + public static implicit operator CustomFormatDefinition(CustomFormat format) => new CustomFormatDefinition { Id = format.Id, Name = format.Name, FormatTags = format.FormatTags }; public override string ToString() { diff --git a/src/NzbDrone.Core/CustomFormats/CustomFormatDefinition.cs b/src/NzbDrone.Core/CustomFormats/CustomFormatDefinition.cs new file mode 100644 index 000000000..029699c68 --- /dev/null +++ b/src/NzbDrone.Core/CustomFormats/CustomFormatDefinition.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using NzbDrone.Core.Datastore; + +namespace NzbDrone.Core.CustomFormats +{ + public class CustomFormatDefinition : ModelBase + { + public string Name { get; set; } + + public List FormatTags { get; set; } + + public static implicit operator CustomFormat(CustomFormatDefinition def) => new CustomFormat { Id = def.Id, Name = def.Name, FormatTags = def.FormatTags }; + } +} diff --git a/src/NzbDrone.Core/CustomFormats/CustomFormatRepository.cs b/src/NzbDrone.Core/CustomFormats/CustomFormatRepository.cs index 205bdec36..843b090b1 100644 --- a/src/NzbDrone.Core/CustomFormats/CustomFormatRepository.cs +++ b/src/NzbDrone.Core/CustomFormats/CustomFormatRepository.cs @@ -3,12 +3,12 @@ namespace NzbDrone.Core.CustomFormats { - public interface ICustomFormatRepository : IBasicRepository + public interface ICustomFormatRepository : IBasicRepository { } - public class CustomFormatRepository : BasicRepository, ICustomFormatRepository + public class CustomFormatRepository : BasicRepository, ICustomFormatRepository { public CustomFormatRepository(IMainDatabase database, IEventAggregator eventAggregator) : base(database, eventAggregator) diff --git a/src/NzbDrone.Core/CustomFormats/CustomFormatService.cs b/src/NzbDrone.Core/CustomFormats/CustomFormatService.cs index 8b68980aa..f57e1da75 100644 --- a/src/NzbDrone.Core/CustomFormats/CustomFormatService.cs +++ b/src/NzbDrone.Core/CustomFormats/CustomFormatService.cs @@ -7,9 +7,7 @@ using NzbDrone.Core.Blacklisting; using NzbDrone.Core.Datastore; using NzbDrone.Core.History; -using NzbDrone.Core.Lifecycle; using NzbDrone.Core.MediaFiles; -using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Profiles; namespace NzbDrone.Core.CustomFormats @@ -23,8 +21,7 @@ public interface ICustomFormatService void Delete(int id); } - - public class CustomFormatService : ICustomFormatService, IHandle + public class CustomFormatService : ICustomFormatService { private readonly ICustomFormatRepository _formatRepository; private IProfileService _profileService; @@ -58,6 +55,9 @@ public CustomFormatService(ICustomFormatRepository formatRepository, ICacheManag _cache = cacheManager.GetCache>(typeof(CustomFormat), "formats"); _historyService = historyService; _logger = logger; + + // Fill up the cache for subsequent DB lookups + All(); } public void Update(CustomFormat customFormat) @@ -144,7 +144,7 @@ private Dictionary AllDictionary() { return _cache.Get("all", () => { - var all = _formatRepository.All().ToDictionary(m => m.Id); + var all = _formatRepository.All().Select(x => (CustomFormat)x).ToDictionary(m => m.Id); AllCustomFormats = all; return all; }); @@ -194,11 +194,5 @@ public static Dictionary> Templates }; } } - - public void Handle(ApplicationStartedEvent message) - { - // Fillup cache for DataMapper. - All(); - } } } diff --git a/src/NzbDrone.Core/Datastore/BasicRepository.cs b/src/NzbDrone.Core/Datastore/BasicRepository.cs index 344ec01da..4a0684887 100644 --- a/src/NzbDrone.Core/Datastore/BasicRepository.cs +++ b/src/NzbDrone.Core/Datastore/BasicRepository.cs @@ -3,11 +3,10 @@ using System.Data; using System.Linq; using System.Linq.Expressions; -using Marr.Data; -using Marr.Data.QGen; -using NzbDrone.Common.Extensions; +using System.Reflection; +using System.Text; +using Dapper; using NzbDrone.Core.Datastore.Events; -using NzbDrone.Core.Datastore.Extensions; using NzbDrone.Core.Messaging.Events; namespace NzbDrone.Core.Datastore @@ -31,43 +30,89 @@ namespace NzbDrone.Core.Datastore bool HasItems(); void DeleteMany(IEnumerable ids); void SetFields(TModel model, params Expression>[] properties); + void SetFields(IList models, params Expression>[] properties); TModel Single(); PagingSpec GetPaged(PagingSpec pagingSpec); } public class BasicRepository : IBasicRepository where TModel : ModelBase, new() { - private readonly IDatabase _database; private readonly IEventAggregator _eventAggregator; + private readonly PropertyInfo _keyProperty; + private readonly List _properties; + private readonly string _updateSql; + private readonly string _insertSql; - protected IDataMapper DataMapper => _database.GetDataMapper(); + protected readonly IDatabase _database; + protected readonly string _table; + protected string _selectTemplate; + protected string _deleteTemplate; public BasicRepository(IDatabase database, IEventAggregator eventAggregator) { _database = database; _eventAggregator = eventAggregator; + + var type = typeof(TModel); + + _table = TableMapping.Mapper.TableNameMapping(type); + _keyProperty = type.GetProperty(nameof(ModelBase.Id)); + + var excluded = TableMapping.Mapper.ExcludeProperties(type).Select(x => x.Name).ToList(); + excluded.Add(_keyProperty.Name); + _properties = type.GetProperties().Where(x => !excluded.Contains(x.Name)).ToList(); + + _insertSql = GetInsertSql(); + _updateSql = GetUpdateSql(_properties); + + _selectTemplate = $"SELECT /**select**/ FROM {_table} /**join**/ /**innerjoin**/ /**leftjoin**/ /**where**/ /**orderby**/"; + _deleteTemplate = $"DELETE FROM {_table} /**where**/"; } - protected QueryBuilder Query => AddJoinQueries(DataMapper.Query()); + protected virtual SqlBuilder BuilderBase() => new SqlBuilder(); + protected virtual SqlBuilder Builder() => BuilderBase().SelectAll(); - protected void Delete(Expression> filter) + protected virtual IEnumerable GetResults(SqlBuilder.Template sql) { - DataMapper.Delete(filter); + using (var conn = _database.OpenConnection()) + { + return conn.Query(sql.RawSql, sql.Parameters); + } } - public IEnumerable All() + protected List Query(Expression> where) { - return AddJoinQueries(DataMapper.Query()).ToList(); + return Query(Builder().Where(where)); + } + + protected List Query(SqlBuilder builder) + { + return Query(builder, GetResults); + } + + protected List Query(SqlBuilder builder, Func> queryFunc) + { + var sql = builder.AddTemplate(_selectTemplate).LogQuery(); + + return queryFunc(sql).ToList(); } public int Count() { - return DataMapper.Query().GetRowCount(); + using (var conn = _database.OpenConnection()) + { + return conn.ExecuteScalar($"SELECT COUNT(*) FROM {_table}"); + } + } + + public virtual IEnumerable All() + { + return Query(Builder()); } public TModel Get(int id) { - var model = Query.Where(c => c.Id == id).SingleOrDefault(); + var model = Query(x => x.Id == id).FirstOrDefault(); if (model == null) { @@ -79,13 +124,16 @@ public TModel Get(int id) public IEnumerable Get(IEnumerable ids) { - var idList = ids.ToList(); - var query = string.Format("Id IN ({0})", string.Join(",", idList)); - var result = Query.Where(m => m.Id.In(idList)).ToList(); - - if (result.Count != idList.Count()) + if (!ids.Any()) { - throw new ApplicationException($"Expected query to return {idList.Count} rows but returned {result.Count}"); + return new List(); + } + + var result = Query(x => ids.Contains(x.Id)); + + if (result.Count != ids.Count()) + { + throw new ApplicationException($"Expected query to return {ids.Count()} rows but returned {result.Count}"); } return result; @@ -108,13 +156,70 @@ public TModel Insert(TModel model) throw new InvalidOperationException("Can't insert model with existing ID " + model.Id); } - DataMapper.Insert(model); + using (var conn = _database.OpenConnection()) + { + model = Insert(conn, null, model); + } ModelCreated(model); return model; } + private string GetInsertSql() + { + var sbColumnList = new StringBuilder(null); + for (var i = 0; i < _properties.Count; i++) + { + var property = _properties[i]; + sbColumnList.AppendFormat("\"{0}\"", property.Name); + if (i < _properties.Count - 1) + sbColumnList.Append(", "); + } + + var sbParameterList = new StringBuilder(null); + for (var i = 0; i < _properties.Count; i++) + { + var property = _properties[i]; + sbParameterList.AppendFormat("@{0}", property.Name); + if (i < _properties.Count - 1) + sbParameterList.Append(", "); + } + + return $"INSERT INTO {_table} ({sbColumnList.ToString()}) VALUES ({sbParameterList.ToString()}); SELECT last_insert_rowid() id"; + } + + private TModel Insert(IDbConnection connection, IDbTransaction transaction, TModel model) + { + + var multi = connection.QueryMultiple(_insertSql, model, transaction); + var id = (int)multi.Read().First().id; + _keyProperty.SetValue(model, id); + + return model; + } + + public void InsertMany(IList models) + { + if (models.Any(x => x.Id != 0)) + { + throw new InvalidOperationException("Can't insert model with existing ID != 0"); + } + + using (var conn = _database.OpenConnection()) + { + using (IDbTransaction tran = conn.BeginTransaction(IsolationLevel.ReadCommitted)) + { + foreach (var model in models) + { + Insert(conn, tran, model); + } + + tran.Commit(); + } + } + } + public TModel Update(TModel model) { if (model.Id == 0) @@ -122,52 +227,59 @@ public TModel Update(TModel model) throw new InvalidOperationException("Can't update model with ID 0"); } - DataMapper.Update(model, c => c.Id == model.Id); + using (var conn = _database.OpenConnection()) + { + UpdateFields(conn, null, model, _properties); + } ModelUpdated(model); return model; } - public void Delete(TModel model) + public void UpdateMany(IList models) { - Delete(model.Id); - } - - public void InsertMany(IList models) - { - using (var unitOfWork = new UnitOfWork(() => DataMapper)) + if (models.Any(x => x.Id == 0)) { - unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted); + throw new InvalidOperationException("Can't update model with ID 0"); + } - foreach (var model in models) - { - unitOfWork.DB.Insert(model); - } - - unitOfWork.Commit(); + using (var conn = _database.OpenConnection()) + { + UpdateFields(conn, null, models, _properties); } } - public void UpdateMany(IList models) + protected void Delete(Expression> where) { - using (var unitOfWork = new UnitOfWork(() => DataMapper)) + Delete(Builder().Where(where)); + } + + protected void Delete(SqlBuilder builder) + { + var sql = builder.AddTemplate(_deleteTemplate).LogQuery(); + + using (var conn = _database.OpenConnection()) { - unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted); + conn.Execute(sql.RawSql, sql.Parameters); + } + } - foreach (var model in models) - { - var localModel = model; + public void Delete(TModel model) + { + Delete(x => x.Id == model.Id); + } - if (model.Id == 0) - { - throw new InvalidOperationException("Can't update model with ID 0"); - } + public void Delete(int id) + { + Delete(x => x.Id == id); + } - unitOfWork.DB.Update(model, c => c.Id == localModel.Id); - } - - unitOfWork.Commit(); + public void DeleteMany(IEnumerable ids) + { + if (ids.Any()) + { + Delete(x => ids.Contains(x.Id)); } } @@ -187,31 +299,13 @@ public TModel Upsert(TModel model) return model; } - public void Delete(int id) - { - DataMapper.Delete(c => c.Id == id); - } - - public void DeleteMany(IEnumerable ids) - { - using (var unitOfWork = new UnitOfWork(() => DataMapper)) - { - unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted); - - foreach (var id in ids) - { - var localId = id; - - unitOfWork.DB.Delete(c => c.Id == localId); - } - - unitOfWork.Commit(); - } - } - public void Purge(bool vacuum = false) { - DataMapper.Delete(c => c.Id > -1); + using (var conn = _database.OpenConnection()) + { + conn.Execute($"DELETE FROM [{_table}]"); + } + if (vacuum) { Vacuum(); @@ -232,43 +326,115 @@ public void SetFields(TModel model, params Expression>[] pr { if (model.Id == 0) { - throw new InvalidOperationException("Attempted to updated model without ID"); + throw new InvalidOperationException("Attempted to update model without ID"); } - DataMapper.Update() - .Where(c => c.Id == model.Id) - .ColumnsIncluding(properties) - .Entity(model) - .Execute(); + var propertiesToUpdate = properties.Select(x => x.GetMemberName()).ToList(); + + using (var conn = _database.OpenConnection()) + { + UpdateFields(conn, null, model, propertiesToUpdate); + } ModelUpdated(model); } + public void SetFields(IList models, params Expression>[] properties) + { + if (models.Any(x => x.Id == 0)) + { + throw new InvalidOperationException("Attempted to update model without ID"); + } + + var propertiesToUpdate = properties.Select(x => x.GetMemberName()).ToList(); + + using (var conn = _database.OpenConnection()) + { + UpdateFields(conn, null, models, propertiesToUpdate); + } + + foreach(var model in models) + { + ModelUpdated(model); + } + } + + private string GetUpdateSql(List propertiesToUpdate) + { + var sb = new StringBuilder(); + sb.AppendFormat("update {0} set ", _table); + + for (var i = 0; i < propertiesToUpdate.Count; i++) + { + var property = propertiesToUpdate[i]; + sb.AppendFormat("\"{0}\" = @{1}", property.Name, property.Name); + if (i < propertiesToUpdate.Count - 1) + sb.Append(", "); + } + + sb.Append($" where \"{_keyProperty.Name}\" = @{_keyProperty.Name}"); + + return sb.ToString(); + } + + private void UpdateFields(IDbConnection connection, IDbTransaction transaction, TModel model, List propertiesToUpdate) + { + var sql = propertiesToUpdate == _properties ? _updateSql : GetUpdateSql(propertiesToUpdate); + + connection.Execute(sql, model, transaction: transaction); + } + + private void UpdateFields(IDbConnection connection, IDbTransaction transaction, IList models, List propertiesToUpdate) + { + var sql = propertiesToUpdate == _properties ? _updateSql : GetUpdateSql(propertiesToUpdate); + + connection.Execute(sql, models, transaction: transaction); + } + + protected virtual SqlBuilder PagedBuilder() => BuilderBase(); + protected virtual IEnumerable PagedSelector(SqlBuilder.Template sql) => GetResults(sql); + public virtual PagingSpec GetPaged(PagingSpec pagingSpec) { - pagingSpec.Records = GetPagedQuery(Query, pagingSpec).ToList(); - pagingSpec.TotalRecords = GetPagedQuery(Query, pagingSpec).GetRowCount(); + pagingSpec.Records = GetPagedRecords(PagedBuilder().SelectAll(), pagingSpec, PagedSelector); + pagingSpec.TotalRecords = GetPagedRecordCount(PagedBuilder().SelectCount(), pagingSpec); return pagingSpec; } - protected virtual SortBuilder GetPagedQuery(QueryBuilder query, PagingSpec pagingSpec) + private void AddFilters(SqlBuilder builder, PagingSpec pagingSpec) { - var filterExpressions = pagingSpec.FilterExpressions; - var sortQuery = query.Where(filterExpressions.FirstOrDefault()); + var filters = pagingSpec.FilterExpressions; - if (filterExpressions.Count > 1) + foreach (var filter in filters) { - // Start at the second item for the AndWhere clauses - for (var i = 1; i < filterExpressions.Count; i++) - { - sortQuery.AndWhere(filterExpressions[i]); - } + builder.Where(filter); } + } - return sortQuery.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection()) - .Skip(pagingSpec.PagingOffset()) - .Take(pagingSpec.PageSize); + protected List GetPagedRecords(SqlBuilder builder, PagingSpec pagingSpec, Func> queryFunc) + { + AddFilters(builder, pagingSpec); + + var sortDirection = pagingSpec.SortDirection == SortDirection.Descending ? "DESC" : "ASC"; + var pagingOffset = (pagingSpec.Page - 1)*pagingSpec.PageSize; + builder.OrderBy($"{pagingSpec.SortKey} {sortDirection} LIMIT {pagingSpec.PageSize} OFFSET {pagingOffset}"); + + var sql = builder.AddTemplate(_selectTemplate).LogQuery(); + + return queryFunc(sql).ToList(); + } + + protected int GetPagedRecordCount(SqlBuilder builder, PagingSpec pagingSpec) + { + AddFilters(builder, pagingSpec); + + var sql = builder.AddTemplate(_selectTemplate).LogQuery(); + + using (var conn = _database.OpenConnection()) + { + return conn.ExecuteScalar(sql.RawSql, sql.Parameters); + } } protected void ModelCreated(TModel model) @@ -294,11 +460,6 @@ private void PublishModelEvent(TModel model, ModelAction action) } } - protected virtual QueryBuilder AddJoinQueries(QueryBuilder baseQuery) - { - return baseQuery; - } - protected virtual bool PublishModelEvents => false; } } diff --git a/src/NzbDrone.Core/Datastore/Converters/BooleanIntConverter.cs b/src/NzbDrone.Core/Datastore/Converters/BooleanIntConverter.cs deleted file mode 100644 index 397746b23..000000000 --- a/src/NzbDrone.Core/Datastore/Converters/BooleanIntConverter.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using Marr.Data.Converters; -using Marr.Data.Mapping; - -namespace NzbDrone.Core.Datastore.Converters -{ - public class BooleanIntConverter : IConverter - { - public object FromDB(ConverterContext context) - { - if (context.DbValue == DBNull.Value) - { - return DBNull.Value; - } - - var val = (long)context.DbValue; - - switch (val) - { - case 1: - return true; - case 0: - return false; - default: - throw new ConversionException(string.Format("The BooleanCharConverter could not convert the value '{0}' to a Boolean.", context.DbValue)); - } - } - - public object FromDB(ColumnMap map, object dbValue) - { - return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); - } - - public object ToDB(object clrValue) - { - var val = (Nullable)clrValue; - - switch (val) - { - case true: - return 1; - case false: - return 0; - default: - return DBNull.Value; - } - } - - public Type DbType => typeof(int); - } -} \ No newline at end of file diff --git a/src/NzbDrone.Core/Datastore/Converters/CommandConverter.cs b/src/NzbDrone.Core/Datastore/Converters/CommandConverter.cs index 0bb97f23a..0a1f6ebca 100644 --- a/src/NzbDrone.Core/Datastore/Converters/CommandConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/CommandConverter.cs @@ -1,30 +1,28 @@ -using System; -using Marr.Data.Converters; +using System.Data; +using System.Text.Json; using NzbDrone.Common.Extensions; using NzbDrone.Common.Reflection; -using NzbDrone.Common.Serializer; using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Datastore.Converters { - public class CommandConverter : EmbeddedDocumentConverter + public class CommandConverter : EmbeddedDocumentConverter { - public override object FromDB(ConverterContext context) + public override Command Parse(object value) { - if (context.DbValue == DBNull.Value) - { - return null; - } - - var stringValue = (string)context.DbValue; + var stringValue = (string) value; if (stringValue.IsNullOrWhiteSpace()) { return null; } - var ordinal = context.DataRecord.GetOrdinal("Name"); - var contract = context.DataRecord.GetString(ordinal); + string contract; + using (JsonDocument body = JsonDocument.Parse(stringValue)) + { + contract = body.RootElement.GetProperty("name").GetString(); + } + var impType = typeof (Command).Assembly.FindTypeByName(contract + "Command"); if (impType == null) @@ -32,7 +30,12 @@ public override object FromDB(ConverterContext context) throw new CommandNotFoundException(contract); } - return Json.Deserialize(stringValue, impType); + return (Command) JsonSerializer.Deserialize(stringValue, impType, SerializerSettings); + } + + public override void SetValue(IDbDataParameter parameter, Command value) + { + parameter.Value = value == null ? null : JsonSerializer.Serialize(value, SerializerSettings); } } } diff --git a/src/NzbDrone.Core/Datastore/Converters/CustomFormatIntConverter.cs b/src/NzbDrone.Core/Datastore/Converters/CustomFormatIntConverter.cs index 3875371b9..69db3e979 100644 --- a/src/NzbDrone.Core/Datastore/Converters/CustomFormatIntConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/CustomFormatIntConverter.cs @@ -1,91 +1,57 @@ using System; -using System.ServiceModel; -using Marr.Data.Converters; -using Marr.Data.Mapping; -using NzbDrone.Core.Qualities; -using Newtonsoft.Json; +using System.Data; +using System.Text.Json; +using System.Text.Json.Serialization; +using Dapper; +using NzbDrone.Common.Serializer; using NzbDrone.Core.CustomFormats; namespace NzbDrone.Core.Datastore.Converters { - public class CustomFormatIntConverter : JsonConverter, IConverter + public class DapperCustomFormatIntConverter : SqlMapper.TypeHandler { - //TODO think of something better. - public object FromDB(ConverterContext context) + public override void SetValue(IDbDataParameter parameter, CustomFormat value) { - if (context.DbValue == DBNull.Value) + parameter.Value = value.Id; + } + + public override CustomFormat Parse(object value) + { + Console.WriteLine(value.ToJson()); + + if (value is DBNull) { return null; } - var val = Convert.ToInt32(context.DbValue); + var val = Convert.ToInt32(value); if (val == 0) { return CustomFormat.None; } - if (CustomFormatService.AllCustomFormats == null) - { - throw new Exception("***FATAL*** WE TRIED ACCESSING ALL CUSTOM FORMATS BEFORE IT WAS INITIALIZED. PLEASE SAVE THIS LOG AND OPEN AN ISSUE ON GITHUB."); - } - return CustomFormatService.AllCustomFormats[val]; } + } - public object FromDB(ColumnMap map, object dbValue) + public class CustomFormatIntConverter : JsonConverter + { + public override CustomFormat Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); - } - - public object ToDB(object clrValue) - { - if(clrValue == DBNull.Value) return null; - - if(!(clrValue is CustomFormat)) - { - throw new InvalidOperationException("Attempted to save a quality definition that isn't really a quality definition"); - } - - var quality = (CustomFormat) clrValue; - - if (CustomFormatService.AllCustomFormats?.ContainsKey(quality.Id) == false) - { - //throw new Exception("Attempted to save an unknown custom format! Make sure you do not have stale custom formats lying around!"); - } - - return quality.Id; - } - - public Type DbType => typeof(int); - - public override bool CanConvert(Type objectType) - { - return objectType == typeof(CustomFormat); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - var item = reader.Value; - - var val = Convert.ToInt32(item); + var val = reader.GetInt32(); if (val == 0) { return CustomFormat.None; } - if (CustomFormatService.AllCustomFormats == null) - { - throw new Exception("***FATAL*** WE TRIED ACCESSING ALL CUSTOM FORMATS BEFORE IT WAS INITIALIZED. PLEASE SAVE THIS LOG AND OPEN AN ISSUE ON GITHUB."); - } - return CustomFormatService.AllCustomFormats[val]; } - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void Write(Utf8JsonWriter writer, CustomFormat value, JsonSerializerOptions options) { - writer.WriteValue(ToDB(value)); + writer.WriteNumberValue(value.Id); } } } diff --git a/src/NzbDrone.Core/Datastore/Converters/DoubleConverter.cs b/src/NzbDrone.Core/Datastore/Converters/DoubleConverter.cs deleted file mode 100644 index 82f50e326..000000000 --- a/src/NzbDrone.Core/Datastore/Converters/DoubleConverter.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using Marr.Data.Converters; -using Marr.Data.Mapping; - -namespace NzbDrone.Core.Datastore.Converters -{ - public class DoubleConverter : IConverter - { - public object FromDB(ConverterContext context) - { - if (context.DbValue == DBNull.Value) - { - return DBNull.Value; - } - - if (context.DbValue is double) - { - return context.DbValue; - } - - return Convert.ToDouble(context.DbValue); - } - - public object FromDB(ColumnMap map, object dbValue) - { - if (dbValue == DBNull.Value) - { - return DBNull.Value; - } - - if (dbValue is double) - { - return dbValue; - } - - return Convert.ToDouble(dbValue); - } - - public object ToDB(object clrValue) - { - return clrValue; - } - - public Type DbType { get; private set; } - } -} diff --git a/src/NzbDrone.Core/Datastore/Converters/EmbeddedDocumentConverter.cs b/src/NzbDrone.Core/Datastore/Converters/EmbeddedDocumentConverter.cs index d2b9146f2..92a634cdd 100644 --- a/src/NzbDrone.Core/Datastore/Converters/EmbeddedDocumentConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/EmbeddedDocumentConverter.cs @@ -1,65 +1,49 @@ -using System; -using Marr.Data.Converters; -using Marr.Data.Mapping; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Newtonsoft.Json.Converters; +using System.Data; +using System.Text.Json; +using System.Text.Json.Serialization; +using Dapper; namespace NzbDrone.Core.Datastore.Converters { - public class EmbeddedDocumentConverter : IConverter + public class EmbeddedDocumentConverter : SqlMapper.TypeHandler { - private readonly JsonSerializerSettings SerializerSetting; + protected readonly JsonSerializerOptions SerializerSettings; - public EmbeddedDocumentConverter(params JsonConverter[] converters) + public EmbeddedDocumentConverter() { - SerializerSetting = new JsonSerializerSettings + var serializerSettings = new JsonSerializerOptions { - DateTimeZoneHandling = DateTimeZoneHandling.Utc, - NullValueHandling = NullValueHandling.Ignore, - Formatting = Formatting.Indented, - DefaultValueHandling = DefaultValueHandling.Include, - ContractResolver = new CamelCasePropertyNamesContractResolver() + AllowTrailingCommas = true, + IgnoreNullValues = false, + PropertyNameCaseInsensitive = true, + DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = true }; - SerializerSetting.Converters.Add(new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy() }); - SerializerSetting.Converters.Add(new VersionConverter()); + serializerSettings.Converters.Add(new NoFlagsStringEnumConverter()); + serializerSettings.Converters.Add(new TimeSpanConverter()); + serializerSettings.Converters.Add(new UtcConverter()); + SerializerSettings = serializerSettings; + } + + public EmbeddedDocumentConverter(params JsonConverter[] converters) : this() + { foreach (var converter in converters) { - SerializerSetting.Converters.Add(converter); + SerializerSettings.Converters.Add(converter); } } - public virtual object FromDB(ConverterContext context) + public override void SetValue(IDbDataParameter parameter, T value) { - if (context.DbValue == DBNull.Value) - { - return DBNull.Value; - } - - var stringValue = (string)context.DbValue; - - if (string.IsNullOrWhiteSpace(stringValue)) - { - return null; - } - return JsonConvert.DeserializeObject(stringValue, context.ColumnMap.FieldType, SerializerSetting); + parameter.Value = JsonSerializer.Serialize(value, SerializerSettings); } - public object FromDB(ColumnMap map, object dbValue) + public override T Parse(object value) { - return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); + return JsonSerializer.Deserialize((string) value, SerializerSettings); } - - public object ToDB(object clrValue) - { - if (clrValue == null) return null; - if (clrValue == DBNull.Value) return DBNull.Value; - - return JsonConvert.SerializeObject(clrValue, SerializerSetting); - } - - public Type DbType => typeof(string); } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core/Datastore/Converters/EnumIntConverter.cs b/src/NzbDrone.Core/Datastore/Converters/EnumIntConverter.cs deleted file mode 100644 index 22d6b5336..000000000 --- a/src/NzbDrone.Core/Datastore/Converters/EnumIntConverter.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using Marr.Data.Converters; -using Marr.Data.Mapping; - -namespace NzbDrone.Core.Datastore.Converters -{ - public class EnumIntConverter : IConverter - { - public Type DbType => typeof(int); - - public object FromDB(ConverterContext context) - { - if (context.DbValue != null && context.DbValue != DBNull.Value) - { - return Enum.ToObject(context.ColumnMap.FieldType, (long)context.DbValue); - } - - return null; - } - - public object FromDB(ColumnMap map, object dbValue) - { - return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); - } - - public object ToDB(object clrValue) - { - if (clrValue != null) - { - return (int)clrValue; - } - - return DBNull.Value; - } - } -} \ No newline at end of file diff --git a/src/NzbDrone.Core/Datastore/Converters/GuidConverter.cs b/src/NzbDrone.Core/Datastore/Converters/GuidConverter.cs index 1ad387513..256ab6502 100644 --- a/src/NzbDrone.Core/Datastore/Converters/GuidConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/GuidConverter.cs @@ -1,40 +1,24 @@ using System; -using Marr.Data.Converters; -using Marr.Data.Mapping; +using System.Data; +using Dapper; namespace NzbDrone.Core.Datastore.Converters { - public class GuidConverter : IConverter + public class GuidConverter : SqlMapper.TypeHandler { - public object FromDB(ConverterContext context) + public override Guid Parse(object value) { - if (context.DbValue == DBNull.Value) + if (value == null) { return Guid.Empty; } - var value = (string)context.DbValue; - - return new Guid(value); + return new Guid((string)value); } - public object FromDB(ColumnMap map, object dbValue) + public override void SetValue(IDbDataParameter parameter, Guid value) { - return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); + parameter.Value = value.ToString(); } - - public object ToDB(object clrValue) - { - if (clrValue == null) - { - return DBNull.Value; - } - - var value = clrValue; - - return value.ToString(); - } - - public Type DbType => typeof(string); } } diff --git a/src/NzbDrone.Core/Datastore/Converters/Int32Converter.cs b/src/NzbDrone.Core/Datastore/Converters/Int32Converter.cs deleted file mode 100644 index 29aae4c00..000000000 --- a/src/NzbDrone.Core/Datastore/Converters/Int32Converter.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using Marr.Data.Converters; -using Marr.Data.Mapping; - -namespace NzbDrone.Core.Datastore.Converters -{ - public class Int32Converter : IConverter - { - public object FromDB(ConverterContext context) - { - if (context.DbValue == DBNull.Value) - { - return DBNull.Value; - } - - if (context.DbValue is int) - { - return context.DbValue; - } - - return Convert.ToInt32(context.DbValue); - } - - public object FromDB(ColumnMap map, object dbValue) - { - return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); - } - - public object ToDB(object clrValue) - { - return clrValue; - } - - public Type DbType { get; private set; } - } -} diff --git a/src/NzbDrone.Core/Datastore/Converters/LanguageIntConverter.cs b/src/NzbDrone.Core/Datastore/Converters/LanguageIntConverter.cs index 0d71b1b72..e7f935d71 100644 --- a/src/NzbDrone.Core/Datastore/Converters/LanguageIntConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/LanguageIntConverter.cs @@ -1,65 +1,48 @@ using System; -using Marr.Data.Converters; -using Marr.Data.Mapping; -using Newtonsoft.Json; +using System.Data; +using System.Text.Json; +using System.Text.Json.Serialization; +using Dapper; using NzbDrone.Core.Languages; namespace NzbDrone.Core.Datastore.Converters { - public class LanguageIntConverter : JsonConverter, IConverter + public class DapperLanguageIntConverter : SqlMapper.TypeHandler { - public object FromDB(ConverterContext context) + public override void SetValue(IDbDataParameter parameter, Language value) { - if (context.DbValue == DBNull.Value) + if (value == null) + { + throw new InvalidOperationException("Attempted to save a language that isn't really a language"); + } + else + { + parameter.Value = (int) value; + } + } + + public override Language Parse(object value) + { + if (value == null || value is DBNull) { return Language.Unknown; } - var val = Convert.ToInt32(context.DbValue); - - return (Language)val; - } - - public object FromDB(ColumnMap map, object dbValue) - { - return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); - } - - public object ToDB(object clrValue) - { - if (clrValue == DBNull.Value) return 0; - - if (clrValue as Language == null) - { - throw new InvalidOperationException("Attempted to save a language that isn't really a language"); - } - - var language = clrValue as Language; - return (int)language; - } - - public Type DbType - { - get - { - return typeof(int); - } - } - - public override bool CanConvert(Type objectType) - { - return objectType == typeof(Language); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - var item = reader.Value; - return (Language)Convert.ToInt32(item); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - writer.WriteValue(ToDB(value)); + return (Language) Convert.ToInt32(value); } } -} \ No newline at end of file + + public class LanguageIntConverter : JsonConverter + { + public override Language Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var item = reader.GetInt32(); + return (Language)item; + } + + public override void Write(Utf8JsonWriter writer, Language value, JsonSerializerOptions options) + { + writer.WriteNumberValue((int) value); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/Converters/NoFlagsStringEnumConverter.cs b/src/NzbDrone.Core/Datastore/Converters/NoFlagsStringEnumConverter.cs new file mode 100644 index 000000000..bd30b659c --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Converters/NoFlagsStringEnumConverter.cs @@ -0,0 +1,17 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace NzbDrone.Core.Datastore.Converters +{ + public class NoFlagsStringEnumConverter : JsonConverterFactory + { + private static JsonStringEnumConverter s_stringEnumConverter = new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, false); + + public override bool CanConvert(Type typeToConvert) + => typeToConvert.IsEnum && !typeToConvert.IsDefined(typeof(FlagsAttribute), inherit: false); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) + => s_stringEnumConverter.CreateConverter(typeToConvert, options); + } +} diff --git a/src/NzbDrone.Core/Datastore/Converters/OsPathConverter.cs b/src/NzbDrone.Core/Datastore/Converters/OsPathConverter.cs index ba2b239fd..dfb2e52c2 100644 --- a/src/NzbDrone.Core/Datastore/Converters/OsPathConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/OsPathConverter.cs @@ -1,36 +1,25 @@ using System; -using Marr.Data.Converters; -using Marr.Data.Mapping; +using System.Data; +using Dapper; using NzbDrone.Common.Disk; namespace NzbDrone.Core.Datastore.Converters { - public class OsPathConverter : IConverter + public class OsPathConverter : SqlMapper.TypeHandler { - public object FromDB(ConverterContext context) + public override void SetValue(IDbDataParameter parameter, OsPath value) { - if (context.DbValue == DBNull.Value) + parameter.Value = value.FullPath; + } + + public override OsPath Parse(object value) + { + if (value == null || value is DBNull) { - return DBNull.Value; + return new OsPath(null); } - var value = (string)context.DbValue; - - return new OsPath(value); + return new OsPath((string) value); } - - public object FromDB(ColumnMap map, object dbValue) - { - return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); - } - - public object ToDB(object clrValue) - { - var value = (OsPath)clrValue; - - return value.FullPath; - } - - public Type DbType => typeof(string); } } diff --git a/src/NzbDrone.Core/Datastore/Converters/ProviderSettingConverter.cs b/src/NzbDrone.Core/Datastore/Converters/ProviderSettingConverter.cs index ace64d6af..1c2edace9 100644 --- a/src/NzbDrone.Core/Datastore/Converters/ProviderSettingConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/ProviderSettingConverter.cs @@ -1,40 +1,23 @@ -using System; -using Marr.Data.Converters; -using NzbDrone.Common.Reflection; -using NzbDrone.Common.Serializer; +using System.Data; +using System.Text.Json; using NzbDrone.Core.ThingiProvider; namespace NzbDrone.Core.Datastore.Converters { - public class ProviderSettingConverter : EmbeddedDocumentConverter + public class ProviderSettingConverter : EmbeddedDocumentConverter { - public override object FromDB(ConverterContext context) + public override IProviderConfig Parse(object value) { - if (context.DbValue == DBNull.Value) - { - return NullConfig.Instance; - } + // We can't deserialize based on another column, happens in ProviderRepository instead + return null; + } - var stringValue = (string)context.DbValue; - - if (string.IsNullOrWhiteSpace(stringValue)) - { - return NullConfig.Instance; - } - - var ordinal = context.DataRecord.GetOrdinal("ConfigContract"); - var contract = context.DataRecord.GetString(ordinal); - - - var impType = typeof (IProviderConfig).Assembly.FindTypeByName(contract); - - if (impType == null) - { - throw new ConfigContractNotFoundException(contract); - } - - return Json.Deserialize(stringValue, impType); + public override void SetValue(IDbDataParameter parameter, IProviderConfig value) + { + // Cast to object to get all properties written out + // https://github.com/dotnet/corefx/issues/38650 + parameter.Value = JsonSerializer.Serialize((object)value, SerializerSettings); } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core/Datastore/Converters/QualityIntConverter.cs b/src/NzbDrone.Core/Datastore/Converters/QualityIntConverter.cs index 72a3cb53a..3c2b70144 100644 --- a/src/NzbDrone.Core/Datastore/Converters/QualityIntConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/QualityIntConverter.cs @@ -1,59 +1,37 @@ using System; -using Marr.Data.Converters; -using Marr.Data.Mapping; using NzbDrone.Core.Qualities; -using Newtonsoft.Json; +using System.Text.Json; +using System.Text.Json.Serialization; +using Dapper; +using System.Data; namespace NzbDrone.Core.Datastore.Converters { - public class QualityIntConverter : JsonConverter, IConverter + public class QualityIntConverter : JsonConverter { - public object FromDB(ConverterContext context) + public override Quality Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - if (context.DbValue == DBNull.Value) - { - return Quality.Unknown; - } - - var val = Convert.ToInt32(context.DbValue); - - return (Quality)val; + var item = reader.GetInt32(); + return (Quality)item; } - public object FromDB(ColumnMap map, object dbValue) + public override void Write(Utf8JsonWriter writer, Quality value, JsonSerializerOptions options) { - return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); - } - - public object ToDB(object clrValue) - { - if (clrValue == DBNull.Value) return 0; - - if (clrValue as Quality == null) - { - throw new InvalidOperationException("Attempted to save a quality that isn't really a quality"); - } - - var quality = clrValue as Quality; - return (int)quality; - } - - public Type DbType => typeof(int); - - public override bool CanConvert(Type objectType) - { - return objectType == typeof(Quality); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - var item = reader.Value; - return (Quality)Convert.ToInt32(item); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - writer.WriteValue(ToDB(value)); + writer.WriteNumberValue((int) value); } } + + public class DapperQualityIntConverter : SqlMapper.TypeHandler + { + public override void SetValue(IDbDataParameter parameter, Quality value) + { + parameter.Value = value == null ? 0 : (int) value; + } + + public override Quality Parse(object value) + { + return (Quality) Convert.ToInt32(value); + } + } + } diff --git a/src/NzbDrone.Core/Datastore/Converters/QualityTagStringConverter.cs b/src/NzbDrone.Core/Datastore/Converters/QualityTagStringConverter.cs index fc7e51ea8..21381bf79 100644 --- a/src/NzbDrone.Core/Datastore/Converters/QualityTagStringConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/QualityTagStringConverter.cs @@ -1,60 +1,41 @@ using System; -using Marr.Data.Converters; -using Marr.Data.Mapping; -using NzbDrone.Core.Qualities; -using Newtonsoft.Json; using NzbDrone.Core.CustomFormats; +using System.Text.Json.Serialization; +using System.Text.Json; +using Dapper; +using System.Data; namespace NzbDrone.Core.Datastore.Converters { - public class QualityTagStringConverter : JsonConverter, IConverter + public class DapperQualityTagStringConverter : SqlMapper.TypeHandler { - public object FromDB(ConverterContext context) + public override void SetValue(IDbDataParameter parameter, FormatTag value) { - if (context.DbValue == DBNull.Value) + parameter.Value = value.Raw; + } + + public override FormatTag Parse(object value) + { + if (value == null || value is DBNull) { return new FormatTag(""); //Will throw argument exception! } - var val = Convert.ToString(context.DbValue); - - return new FormatTag(val); + return new FormatTag(Convert.ToString(value)); } + } - public object FromDB(ColumnMap map, object dbValue) + public class QualityTagStringConverter : JsonConverter + { + public override FormatTag Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); - } - - public object ToDB(object clrValue) - { - if(clrValue == DBNull.Value) return 0; - - if(!(clrValue is FormatTag)) - { - throw new InvalidOperationException("Attempted to save a quality tag that isn't really a quality tag"); - } - - var quality = (FormatTag) clrValue; - return quality.Raw; - } - - public Type DbType => typeof(string); - - public override bool CanConvert(Type objectType) - { - return objectType == typeof(FormatTag); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - var item = reader.Value; + var item = reader.GetString(); return new FormatTag(Convert.ToString(item)); } - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void Write(Utf8JsonWriter writer, FormatTag value, JsonSerializerOptions options) { - writer.WriteValue(ToDB(value)); + writer.WriteStringValue(value.Raw); } } } diff --git a/src/NzbDrone.Core/Datastore/Converters/TimeSpanConverter.cs b/src/NzbDrone.Core/Datastore/Converters/TimeSpanConverter.cs index c94551055..07f2d9314 100644 --- a/src/NzbDrone.Core/Datastore/Converters/TimeSpanConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/TimeSpanConverter.cs @@ -1,43 +1,19 @@ using System; -using System.Globalization; -using Marr.Data.Converters; -using Marr.Data.Mapping; -using NzbDrone.Common.Extensions; +using System.Text.Json; +using System.Text.Json.Serialization; namespace NzbDrone.Core.Datastore.Converters { - public class TimeSpanConverter : IConverter + public class TimeSpanConverter : JsonConverter { - public object FromDB(ConverterContext context) + public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - if (context.DbValue == DBNull.Value) - { - return TimeSpan.Zero; - } - - if (context.DbValue is TimeSpan) - { - return context.DbValue; - } - - return TimeSpan.Parse(context.DbValue.ToString(), CultureInfo.InvariantCulture); + return TimeSpan.Parse(reader.GetString()); } - public object FromDB(ColumnMap map, object dbValue) + public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) { - return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); + writer.WriteStringValue(value.ToString()); } - - public object ToDB(object clrValue) - { - if (clrValue.ToString().IsNullOrWhiteSpace()) - { - return null; - } - - return ((TimeSpan)clrValue).ToString("c", CultureInfo.InvariantCulture); - } - - public Type DbType { get; private set; } } } diff --git a/src/NzbDrone.Core/Datastore/Converters/UtcConverter.cs b/src/NzbDrone.Core/Datastore/Converters/UtcConverter.cs index 1225f0806..db70f2359 100644 --- a/src/NzbDrone.Core/Datastore/Converters/UtcConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/UtcConverter.cs @@ -1,32 +1,34 @@ -using System; -using Marr.Data.Converters; -using Marr.Data.Mapping; +using System; +using System.Data; +using System.Text.Json; +using System.Text.Json.Serialization; +using Dapper; namespace NzbDrone.Core.Datastore.Converters { - public class UtcConverter : IConverter + public class DapperUtcConverter : SqlMapper.TypeHandler { - public object FromDB(ConverterContext context) + public override void SetValue(IDbDataParameter parameter, DateTime value) { - return context.DbValue; + parameter.Value = value.ToUniversalTime(); } - public object FromDB(ColumnMap map, object dbValue) + public override DateTime Parse(object value) { - return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); + return DateTime.SpecifyKind((DateTime)value, DateTimeKind.Utc); } - - public object ToDB(object clrValue) - { - if (clrValue == DBNull.Value) - { - return clrValue; - } - - var dateTime = (DateTime)clrValue; - return dateTime.ToUniversalTime(); - } - - public Type DbType => typeof(DateTime); } -} \ No newline at end of file + + public class UtcConverter : JsonConverter + { + public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return DateTime.Parse(reader.GetString()); + } + + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssZ")); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/Database.cs b/src/NzbDrone.Core/Datastore/Database.cs index 1ff4fba49..609c562b0 100644 --- a/src/NzbDrone.Core/Datastore/Database.cs +++ b/src/NzbDrone.Core/Datastore/Database.cs @@ -1,5 +1,6 @@ using System; -using Marr.Data; +using System.Data; +using Dapper; using NLog; using NzbDrone.Common.Instrumentation; @@ -7,7 +8,7 @@ namespace NzbDrone.Core.Datastore { public interface IDatabase { - IDataMapper GetDataMapper(); + IDbConnection OpenConnection(); Version Version { get; } int Migration { get; } void Vacuum(); @@ -16,17 +17,17 @@ public interface IDatabase public class Database : IDatabase { private readonly string _databaseName; - private readonly Func _datamapperFactory; + private readonly Func _datamapperFactory; private readonly Logger _logger = NzbDroneLogger.GetLogger(typeof(Database)); - public Database(string databaseName, Func datamapperFactory) + public Database(string databaseName, Func datamapperFactory) { _databaseName = databaseName; _datamapperFactory = datamapperFactory; } - public IDataMapper GetDataMapper() + public IDbConnection OpenConnection() { return _datamapperFactory(); } @@ -37,7 +38,7 @@ public Version Version { using (var db = _datamapperFactory()) { - var version = db.ExecuteScalar("SELECT sqlite_version()").ToString(); + var version = db.QueryFirstOrDefault("SELECT sqlite_version()"); return new Version(version); } } @@ -47,9 +48,10 @@ public int Migration { get { - var migration = _datamapperFactory() - .ExecuteScalar("SELECT version from VersionInfo ORDER BY version DESC LIMIT 1").ToString(); - return Convert.ToInt32(migration); + using (var db = _datamapperFactory()) + { + return db.QueryFirstOrDefault("SELECT version from VersionInfo ORDER BY version DESC LIMIT 1"); + } } } @@ -60,7 +62,7 @@ public void Vacuum() _logger.Info("Vacuuming {0} database", _databaseName); using (var db = _datamapperFactory()) { - db.ExecuteNonQuery("Vacuum;"); + db.Execute("Vacuum;"); } _logger.Info("{0} database compressed", _databaseName); } diff --git a/src/NzbDrone.Core/Datastore/DbFactory.cs b/src/NzbDrone.Core/Datastore/DbFactory.cs index 07e406595..79924c69a 100644 --- a/src/NzbDrone.Core/Datastore/DbFactory.cs +++ b/src/NzbDrone.Core/Datastore/DbFactory.cs @@ -1,7 +1,5 @@ using System; using System.Data.SQLite; -using Marr.Data; -using Marr.Data.Reflection; using NLog; using NzbDrone.Common.Composition; using NzbDrone.Common.Disk; @@ -31,7 +29,6 @@ static DbFactory() { InitializeEnvironment(); - MapRepository.Instance.ReflectionStrategy = new SimpleReflectionStrategy(); TableMapping.Map(); } @@ -98,12 +95,11 @@ public IDatabase Create(MigrationContext migrationContext) var db = new Database(migrationContext.MigrationType.ToString(), () => { - var dataMapper = new DataMapper(SQLiteFactory.Instance, connectionString) - { - SqlMode = SqlModes.Text, - }; + var conn = SQLiteFactory.Instance.CreateConnection(); + conn.ConnectionString = connectionString; + conn.Open(); - return dataMapper; + return conn; }); return db; diff --git a/src/NzbDrone.Core/Datastore/ExpressionVisitor.cs b/src/NzbDrone.Core/Datastore/ExpressionVisitor.cs new file mode 100644 index 000000000..6e3ab91fd --- /dev/null +++ b/src/NzbDrone.Core/Datastore/ExpressionVisitor.cs @@ -0,0 +1,146 @@ +/* This class was copied from Mehfuz's LinqExtender project, which is available from github. + * http://mehfuzh.github.com/LinqExtender/ + */ + +using System; +using System.Linq.Expressions; + +namespace NzbDrone.Core.Datastore +{ + /// + /// Expression visitor + /// + public class ExpressionVisitor + { + /// + /// Visits expression and delegates call to different to branch. + /// + /// + /// + protected virtual Expression Visit(Expression expression) + { + if (expression == null) + return null; + + switch (expression.NodeType) + { + case ExpressionType.Lambda: + return VisitLamda((LambdaExpression)expression); + case ExpressionType.ArrayLength: + case ExpressionType.Convert: + case ExpressionType.ConvertChecked: + case ExpressionType.Negate: + case ExpressionType.UnaryPlus: + case ExpressionType.NegateChecked: + case ExpressionType.Not: + case ExpressionType.Quote: + case ExpressionType.TypeAs: + return VisitUnary((UnaryExpression)expression); + case ExpressionType.Add: + case ExpressionType.AddChecked: + case ExpressionType.And: + case ExpressionType.AndAlso: + case ExpressionType.ArrayIndex: + case ExpressionType.Coalesce: + case ExpressionType.Divide: + case ExpressionType.Equal: + case ExpressionType.ExclusiveOr: + case ExpressionType.GreaterThan: + case ExpressionType.GreaterThanOrEqual: + case ExpressionType.LeftShift: + case ExpressionType.LessThan: + case ExpressionType.LessThanOrEqual: + case ExpressionType.Modulo: + case ExpressionType.Multiply: + case ExpressionType.MultiplyChecked: + case ExpressionType.NotEqual: + case ExpressionType.Or: + case ExpressionType.OrElse: + case ExpressionType.Power: + case ExpressionType.RightShift: + case ExpressionType.Subtract: + case ExpressionType.SubtractChecked: + return VisitBinary((BinaryExpression)expression); + case ExpressionType.Call: + return VisitMethodCall((MethodCallExpression)expression); + case ExpressionType.Constant: + return VisitConstant((ConstantExpression)expression); + case ExpressionType.MemberAccess: + return VisitMemberAccess((MemberExpression)expression); + case ExpressionType.Parameter: + return VisitParameter((ParameterExpression)expression); + + } + throw new ArgumentOutOfRangeException("expression", expression.NodeType.ToString()); + } + + /// + /// Visits the constance expression. To be implemented by user. + /// + /// + /// + protected virtual Expression VisitConstant(ConstantExpression expression) + { + return expression; + } + + /// + /// Visits the memeber access expression. To be implemented by user. + /// + /// + /// + protected virtual Expression VisitMemberAccess(MemberExpression expression) + { + return expression; + } + + /// + /// Visits the method call expression. To be implemented by user. + /// + /// + /// + protected virtual Expression VisitMethodCall(MethodCallExpression expression) + { + throw new NotImplementedException(); + } + + /// + /// Visits the binary expression. + /// + /// + /// + protected virtual Expression VisitBinary(BinaryExpression expression) + { + Visit(expression.Left); + Visit(expression.Right); + return expression; + } + + /// + /// Visits the unary expression. + /// + /// + /// + protected virtual Expression VisitUnary(UnaryExpression expression) + { + Visit(expression.Operand); + return expression; + } + + /// + /// Visits the lamda expression. + /// + /// + /// + protected virtual Expression VisitLamda(LambdaExpression lambdaExpression) + { + Visit(lambdaExpression.Body); + return lambdaExpression; + } + + private Expression VisitParameter(ParameterExpression expression) + { + return expression; + } + } +} diff --git a/src/NzbDrone.Core/Datastore/Extensions/BuilderExtensions.cs b/src/NzbDrone.Core/Datastore/Extensions/BuilderExtensions.cs new file mode 100644 index 000000000..c3c7a8967 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Extensions/BuilderExtensions.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using Dapper; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Serializer; + +namespace NzbDrone.Core.Datastore +{ + public static class SqlBuilderExtensions + { + public static bool LogSql { get; set; } + + public static SqlBuilder SelectAll(this SqlBuilder builder) + { + return builder.Select("*"); + } + + public static SqlBuilder SelectCount(this SqlBuilder builder) + { + return builder.Select("COUNT(*)"); + } + + public static SqlBuilder Where(this SqlBuilder builder, Expression> filter) + { + var wb = new WhereBuilder(filter); + + return builder.Where(wb.ToString(), wb.Parameters); + } + + public static SqlBuilder OrWhere(this SqlBuilder builder, Expression> filter) + { + var wb = new WhereBuilder(filter); + + return builder.OrWhere(wb.ToString(), wb.Parameters); + } + + public static SqlBuilder.Template LogQuery(this SqlBuilder.Template template) + { + if (LogSql) + { + var sb = new StringBuilder(); + sb.AppendLine(); + sb.AppendLine("==== Begin Query Trace ===="); + sb.AppendLine(); + sb.AppendLine("QUERY TEXT:"); + sb.AppendLine(template.RawSql); + sb.AppendLine(); + sb.AppendLine("PARAMETERS:"); + foreach (var p in ((DynamicParameters)template.Parameters).ToDictionary()) + { + object val = (p.Value is string) ? string.Format("\"{0}\"", p.Value) : p.Value; + sb.AppendFormat("{0} = [{1}]", p.Key, val.ToJson() ?? "NULL").AppendLine(); + } + sb.AppendLine(); + sb.AppendLine("==== End Query Trace ===="); + sb.AppendLine(); + + Trace.Write(sb.ToString()); + } + + return template; + } + + private static Dictionary ToDictionary(this DynamicParameters dynamicParams) + { + var argsDictionary = new Dictionary(); + var iLookup = (SqlMapper.IParameterLookup) dynamicParams; + + foreach (var paramName in dynamicParams.ParameterNames) + { + var value = iLookup[paramName]; + argsDictionary.Add(paramName, value); + } + + var templates = dynamicParams.GetType().GetField("templates", BindingFlags.NonPublic | BindingFlags.Instance); + if (templates != null) + { + var list = templates.GetValue(dynamicParams) as List; + if (list != null) + { + foreach (var objProps in list.Select(obj => obj.GetPropertyValuePairs().ToList())) + { + objProps.ForEach(p => argsDictionary.Add(p.Key, p.Value)); + } + } + } + + return argsDictionary; + } + + private static Dictionary GetPropertyValuePairs(this object obj, String[] hidden = null) + { + var type = obj.GetType(); + var pairs = hidden == null + ? type.GetProperties() + .DistinctBy(propertyInfo => propertyInfo.Name) + .ToDictionary( + propertyInfo => propertyInfo.Name, + propertyInfo => propertyInfo.GetValue(obj, null)) + : type.GetProperties() + .Where(it => !hidden.Contains(it.Name)) + .DistinctBy(propertyInfo => propertyInfo.Name) + .ToDictionary( + propertyInfo => propertyInfo.Name, + propertyInfo => propertyInfo.GetValue(obj, null)); + return pairs; + } + } +} diff --git a/src/NzbDrone.Core/Datastore/Extensions/MappingExtensions.cs b/src/NzbDrone.Core/Datastore/Extensions/MappingExtensions.cs deleted file mode 100644 index 4d635024a..000000000 --- a/src/NzbDrone.Core/Datastore/Extensions/MappingExtensions.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Reflection; -using Marr.Data; -using Marr.Data.Mapping; -using NzbDrone.Common.Reflection; -using NzbDrone.Core.ThingiProvider; - -namespace NzbDrone.Core.Datastore.Extensions -{ - public static class MappingExtensions - { - - public static ColumnMapBuilder MapResultSet(this FluentMappings.MappingsFluentEntity mapBuilder) where T : ResultSet, new() - { - return mapBuilder - .Columns - .AutoMapPropertiesWhere(IsMappableProperty); - } - - public static ColumnMapBuilder RegisterDefinition(this FluentMappings.MappingsFluentEntity mapBuilder, string tableName = null) where T : ProviderDefinition, new() - { - return RegisterModel(mapBuilder, tableName).Ignore(c => c.ImplementationName); - } - - public static ColumnMapBuilder RegisterModel(this FluentMappings.MappingsFluentEntity mapBuilder, string tableName = null) where T : ModelBase, new() - { - return mapBuilder.Table.MapTable(tableName) - .Columns - .AutoMapPropertiesWhere(IsMappableProperty) - .PrefixAltNames(string.Format("{0}_", typeof(T).Name)) - .For(c => c.Id) - .SetPrimaryKey() - .SetReturnValue() - .SetAutoIncrement(); - } - - public static RelationshipBuilder AutoMapChildModels(this ColumnMapBuilder mapBuilder) - { - return mapBuilder.Relationships.AutoMapPropertiesWhere(m => - m.MemberType == MemberTypes.Property && - typeof(ModelBase).IsAssignableFrom(((PropertyInfo) m).PropertyType)); - } - - public static bool IsMappableProperty(MemberInfo memberInfo) - { - var propertyInfo = memberInfo as PropertyInfo; - - if (propertyInfo == null) return false; - - - if (!propertyInfo.IsReadable() || !propertyInfo.IsWritable()) - { - return false; - } - - if (propertyInfo.PropertyType.IsSimpleType() || MapRepository.Instance.TypeConverters.ContainsKey(propertyInfo.PropertyType)) - { - return true; - } - - return false; - } - } -} \ No newline at end of file diff --git a/src/NzbDrone.Core/Datastore/Extensions/PagingSpecExtensions.cs b/src/NzbDrone.Core/Datastore/Extensions/PagingSpecExtensions.cs deleted file mode 100644 index 39cc5b7a6..000000000 --- a/src/NzbDrone.Core/Datastore/Extensions/PagingSpecExtensions.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Linq; -using System.Linq.Expressions; - -namespace NzbDrone.Core.Datastore.Extensions -{ - public static class PagingSpecExtensions - { - public static Expression> OrderByClause(this PagingSpec pagingSpec) - { - return CreateExpression(pagingSpec.SortKey); - } - - public static int PagingOffset(this PagingSpec pagingSpec) - { - return (pagingSpec.Page - 1)*pagingSpec.PageSize; - } - - public static Marr.Data.QGen.SortDirection ToSortDirection(this PagingSpec pagingSpec) - { - if (pagingSpec.SortDirection == SortDirection.Descending) return Marr.Data.QGen.SortDirection.Desc; - - return Marr.Data.QGen.SortDirection.Asc; - } - - private static Expression> CreateExpression(string propertyName) - { - Type type = typeof(TModel); - ParameterExpression parameterExpression = Expression.Parameter(type, "x"); - Expression expressionBody = parameterExpression; - - var splitPropertyName = propertyName.Split('.').ToList(); - - foreach (var property in splitPropertyName) - { - expressionBody = Expression.Property(expressionBody, property); - } - - expressionBody = Expression.Convert(expressionBody, typeof(object)); - return Expression.Lambda>(expressionBody, parameterExpression); - } - } -} - \ No newline at end of file diff --git a/src/NzbDrone.Core/Datastore/Extensions/RelationshipExtensions.cs b/src/NzbDrone.Core/Datastore/Extensions/RelationshipExtensions.cs deleted file mode 100644 index 9374e0b97..000000000 --- a/src/NzbDrone.Core/Datastore/Extensions/RelationshipExtensions.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Linq; -using System.Linq.Expressions; -using Marr.Data; -using Marr.Data.Mapping; - -namespace NzbDrone.Core.Datastore.Extensions -{ - public static class RelationshipExtensions - { - public static RelationshipBuilder HasOne(this RelationshipBuilder relationshipBuilder, Expression>> portalExpression, Func childIdSelector) - where TParent : ModelBase - where TChild : ModelBase - { - return relationshipBuilder.For(portalExpression.GetMemberName()) - .LazyLoad( - condition: parent => childIdSelector(parent) > 0, - query: (db, parent) => - { - var id = childIdSelector(parent); - return db.Query().Where(c => c.Id == id).SingleOrDefault(); - } - ); - } - - public static RelationshipBuilder Relationship(this ColumnMapBuilder mapBuilder) - { - return mapBuilder.Relationships.AutoMapComplexTypeProperties(); - } - - public static RelationshipBuilder HasMany(this RelationshipBuilder relationshipBuilder, Expression>> portalExpression, Func parentIdSelector) - where TParent : ModelBase - where TChild : ModelBase - { - return relationshipBuilder.For(portalExpression.GetMemberName()) - .LazyLoad((db, parent) => db.Query().Where(c => parentIdSelector(c) == parent.Id).ToList()); - } - - private static string GetMemberName(this Expression> member) - { - var expression = member.Body as MemberExpression; - - if (expression == null) - { - expression = (MemberExpression)((UnaryExpression)member.Body).Operand; - } - - return expression.Member.Name; - } - } -} \ No newline at end of file diff --git a/src/NzbDrone.Core/Datastore/LazyList.cs b/src/NzbDrone.Core/Datastore/LazyList.cs deleted file mode 100644 index 17d0aba02..000000000 --- a/src/NzbDrone.Core/Datastore/LazyList.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; -using Marr.Data; - -namespace NzbDrone.Core.Datastore -{ - public class LazyList : LazyLoaded> - { - public LazyList() - : this(new List()) - { - - } - - public LazyList(IEnumerable items) - : base(new List(items)) - { - - } - - public static implicit operator LazyList(List val) - { - return new LazyList(val); - } - - public static implicit operator List(LazyList lazy) - { - return lazy.Value; - } - } -} \ No newline at end of file diff --git a/src/NzbDrone.Core/Datastore/LogDatabase.cs b/src/NzbDrone.Core/Datastore/LogDatabase.cs index e1c9c3a20..05e152eb0 100644 --- a/src/NzbDrone.Core/Datastore/LogDatabase.cs +++ b/src/NzbDrone.Core/Datastore/LogDatabase.cs @@ -1,5 +1,5 @@ using System; -using Marr.Data; +using System.Data; namespace NzbDrone.Core.Datastore { @@ -17,9 +17,9 @@ public LogDatabase(IDatabase database) _database = database; } - public IDataMapper GetDataMapper() + public IDbConnection OpenConnection() { - return _database.GetDataMapper(); + return _database.OpenConnection(); } public Version Version => _database.Version; diff --git a/src/NzbDrone.Core/Datastore/MainDatabase.cs b/src/NzbDrone.Core/Datastore/MainDatabase.cs index 869c3a6da..82ab7c716 100644 --- a/src/NzbDrone.Core/Datastore/MainDatabase.cs +++ b/src/NzbDrone.Core/Datastore/MainDatabase.cs @@ -1,5 +1,5 @@ using System; -using Marr.Data; +using System.Data; namespace NzbDrone.Core.Datastore { @@ -17,9 +17,9 @@ public MainDatabase(IDatabase database) _database = database; } - public IDataMapper GetDataMapper() + public IDbConnection OpenConnection() { - return _database.GetDataMapper(); + return _database.OpenConnection(); } public Version Version => _database.Version; diff --git a/src/NzbDrone.Core/Datastore/Migration/036_update_with_quality_converters.cs b/src/NzbDrone.Core/Datastore/Migration/036_update_with_quality_converters.cs index e98e667a8..df5f49bfc 100644 --- a/src/NzbDrone.Core/Datastore/Migration/036_update_with_quality_converters.cs +++ b/src/NzbDrone.Core/Datastore/Migration/036_update_with_quality_converters.cs @@ -26,7 +26,7 @@ protected override void MainDbUpgrade() private void ConvertQualityProfiles(IDbConnection conn, IDbTransaction tran) { - var qualityProfileItemConverter = new EmbeddedDocumentConverter(new QualityIntConverter()); + var qualityProfileItemConverter = new EmbeddedDocumentConverter>(new QualityIntConverter()); // Convert 'Allowed' column in QualityProfiles from Json List to Json List (int = Quality) using (IDbCommand qualityProfileCmd = conn.CreateCommand()) @@ -44,13 +44,12 @@ private void ConvertQualityProfiles(IDbConnection conn, IDbTransaction tran) var items = Quality.DefaultQualityDefinitions.OrderBy(v => v.Weight).Select(v => new ProfileQualityItem { Quality = v.Quality, Allowed = allowed.Contains(v.Quality) }).ToList(); - var allowedNewJson = qualityProfileItemConverter.ToDB(items); - using (IDbCommand updateCmd = conn.CreateCommand()) { updateCmd.Transaction = tran; updateCmd.CommandText = "UPDATE QualityProfiles SET Items = ? WHERE Id = ?"; - updateCmd.AddParameter(allowedNewJson); + var param = updateCmd.CreateParameter(); + qualityProfileItemConverter.SetValue(param, items); updateCmd.AddParameter(id); updateCmd.ExecuteNonQuery(); @@ -70,7 +69,7 @@ private void ConvertQualityModels(IDbConnection conn, IDbTransaction tran) private void ConvertQualityModel(IDbConnection conn, IDbTransaction tran, string tableName) { - var qualityModelConverter = new EmbeddedDocumentConverter(new QualityIntConverter()); + var qualityModelConverter = new EmbeddedDocumentConverter(new QualityIntConverter()); using (IDbCommand qualityModelCmd = conn.CreateCommand()) { @@ -89,17 +88,18 @@ private void ConvertQualityModel(IDbConnection conn, IDbTransaction tran, string continue; } - var qualityNewJson = qualityModelConverter.ToDB(new DestinationQualityModel036 - { - Quality = sourceQuality.Quality.Id, - Proper = sourceQuality.Proper - }); + var qualityNew = new DestinationQualityModel036 + { + Quality = sourceQuality.Quality.Id, + Proper = sourceQuality.Proper + }; using (IDbCommand updateCmd = conn.CreateCommand()) { updateCmd.Transaction = tran; updateCmd.CommandText = "UPDATE " + tableName + " SET Quality = ? WHERE Quality = ?"; - updateCmd.AddParameter(qualityNewJson); + var param = updateCmd.CreateParameter(); + qualityModelConverter.SetValue(param, qualityNew); updateCmd.AddParameter(qualityJson); updateCmd.ExecuteNonQuery(); diff --git a/src/NzbDrone.Core/Datastore/Migration/104_add_moviefiles_table.cs b/src/NzbDrone.Core/Datastore/Migration/104_add_moviefiles_table.cs index bd74367d6..f192b7709 100644 --- a/src/NzbDrone.Core/Datastore/Migration/104_add_moviefiles_table.cs +++ b/src/NzbDrone.Core/Datastore/Migration/104_add_moviefiles_table.cs @@ -1,9 +1,5 @@ using FluentMigrator; -using Marr.Data.Mapping; using NzbDrone.Core.Datastore.Migration.Framework; -using NzbDrone.Core.Movies; -using NzbDrone.Core.MediaFiles; -using NzbDrone.Core.Datastore.Extensions; namespace NzbDrone.Core.Datastore.Migration { diff --git a/src/NzbDrone.Core/Datastore/Migration/147_add_custom_formats.cs b/src/NzbDrone.Core/Datastore/Migration/147_add_custom_formats.cs index 24ebdf709..af13aeefe 100644 --- a/src/NzbDrone.Core/Datastore/Migration/147_add_custom_formats.cs +++ b/src/NzbDrone.Core/Datastore/Migration/147_add_custom_formats.cs @@ -1,13 +1,6 @@ -using System.Collections.Generic; -using System.Data; - using System.Linq; - using FluentMigrator; - using Marr.Data.QGen; - using Newtonsoft.Json.Linq; -using NzbDrone.Common.Extensions; -using NzbDrone.Common.Serializer; +using System.Data; +using FluentMigrator; using NzbDrone.Core.Datastore.Migration.Framework; - using NzbDrone.Core.Qualities; namespace NzbDrone.Core.Datastore.Migration { diff --git a/src/NzbDrone.Core/Datastore/Migration/154_add_language_to_file_history_blacklist.cs b/src/NzbDrone.Core/Datastore/Migration/154_add_language_to_file_history_blacklist.cs index 4013e3d19..aa151b9d1 100644 --- a/src/NzbDrone.Core/Datastore/Migration/154_add_language_to_file_history_blacklist.cs +++ b/src/NzbDrone.Core/Datastore/Migration/154_add_language_to_file_history_blacklist.cs @@ -31,7 +31,7 @@ protected override void MainDbUpgrade() private void UpdateLanguage(IDbConnection conn, IDbTransaction tran) { - var LanguageConverter = new EmbeddedDocumentConverter(new LanguageIntConverter()); + var LanguageConverter = new EmbeddedDocumentConverter>(new LanguageIntConverter()); var profileLanguages = new Dictionary(); using (IDbCommand getProfileCmd = conn.CreateCommand()) @@ -77,7 +77,7 @@ private void UpdateLanguage(IDbConnection conn, IDbTransaction tran) foreach (var group in movieLanguages.GroupBy(v => v.Value, v => v.Key)) { - var languageJson = LanguageConverter.ToDB(new List { Language.FindById(group.Key) }); + var language = new List { Language.FindById(group.Key) }; var movieIds = group.Select(v => v.ToString()).Join(","); @@ -85,7 +85,8 @@ private void UpdateLanguage(IDbConnection conn, IDbTransaction tran) { updateMovieFilesCmd.Transaction = tran; updateMovieFilesCmd.CommandText = $"UPDATE MovieFiles SET Languages = ? WHERE MovieId IN ({movieIds})"; - updateMovieFilesCmd.AddParameter(languageJson); + var param = updateMovieFilesCmd.CreateParameter(); + LanguageConverter.SetValue(param, language); updateMovieFilesCmd.ExecuteNonQuery(); } @@ -94,7 +95,8 @@ private void UpdateLanguage(IDbConnection conn, IDbTransaction tran) { updateHistoryCmd.Transaction = tran; updateHistoryCmd.CommandText = $"UPDATE History SET Languages = ? WHERE MovieId IN ({movieIds})"; - updateHistoryCmd.AddParameter(languageJson); + var param = updateHistoryCmd.CreateParameter(); + LanguageConverter.SetValue(param, language); updateHistoryCmd.ExecuteNonQuery(); } @@ -103,7 +105,8 @@ private void UpdateLanguage(IDbConnection conn, IDbTransaction tran) { updateBlacklistCmd.Transaction = tran; updateBlacklistCmd.CommandText = $"UPDATE Blacklist SET Languages = ? WHERE MovieId IN ({movieIds})"; - updateBlacklistCmd.AddParameter(languageJson); + var param = updateBlacklistCmd.CreateParameter(); + LanguageConverter.SetValue(param, language); updateBlacklistCmd.ExecuteNonQuery(); } diff --git a/src/NzbDrone.Core/Datastore/Migration/162_fix_profile_format_default.cs b/src/NzbDrone.Core/Datastore/Migration/162_fix_profile_format_default.cs new file mode 100644 index 000000000..868e3d923 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/162_fix_profile_format_default.cs @@ -0,0 +1,20 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(162)] + public class fix_profile_format_default : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + // badValue was set as default in 147 but it's invalid JSON + // so System.Text.Json refuses to parse it (even though Newtonsoft allows it) + var badValue = "[{format:0, allowed:true}]"; + var defaultValue = "[{\"format\":0, \"allowed\":true}]"; + Alter.Column("FormatItems").OnTable("Profiles").AsString().WithDefaultValue(defaultValue); + + Update.Table("Profiles").Set(new { FormatItems = defaultValue }).Where( new { FormatItems = badValue }); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/TableMapper.cs b/src/NzbDrone.Core/Datastore/TableMapper.cs new file mode 100644 index 000000000..73a37d1e6 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/TableMapper.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using Dapper; +using NzbDrone.Common.Reflection; + +namespace NzbDrone.Core.Datastore +{ + public static class MappingExtensions + { + public static PropertyInfo GetMemberName(this Expression> member) + { + var memberExpression = (member.Body as MemberExpression); + if (memberExpression == null) + { + memberExpression = (member.Body as UnaryExpression).Operand as MemberExpression; + } + + return (PropertyInfo) memberExpression.Member; + } + } + + public class TableMapper + { + public TableMapper() + { + IgnoreList = new Dictionary>(); + TableMap = new Dictionary(); + } + + public Dictionary> IgnoreList { get; set; } + public Dictionary TableMap { get; set; } + + public ColumnMapper Entity(string tableName) + { + TableMap.Add(typeof(TEntity), tableName); + + if (IgnoreList.TryGetValue(typeof(TEntity), out var list)) + { + return new ColumnMapper(list); + } + + list = new List(); + IgnoreList[typeof(TEntity)] = list; + return new ColumnMapper(list); + } + + public List ExcludeProperties(Type x) + { + return IgnoreList.ContainsKey(x) ? IgnoreList[x] : new List(); + } + + public string TableNameMapping(Type x) + { + return TableMap.ContainsKey(x) ? TableMap[x] : null; + } + } + + public class ColumnMapper + { + private readonly List _ignoreList; + + public ColumnMapper(List ignoreList) + { + _ignoreList = ignoreList; + } + + public ColumnMapper AutoMapPropertiesWhere(Func predicate) + { + Type entityType = typeof(T); + var properties = entityType.GetProperties(); + _ignoreList.AddRange(properties.Where(x => !predicate(x))); + + return this; + } + + public ColumnMapper RegisterModel() + { + return AutoMapPropertiesWhere(IsMappableProperty); + } + + public ColumnMapper Ignore(Expression> property) + { + _ignoreList.Add(property.GetMemberName()); + return this; + } + + public static bool IsMappableProperty(MemberInfo memberInfo) + { + var propertyInfo = memberInfo as PropertyInfo; + + if (propertyInfo == null) return false; + + + if (!propertyInfo.IsReadable() || !propertyInfo.IsWritable()) + { + return false; + } + + // This is a bit of a hack but is the only way to see if a type has a handler set in Dapper +#pragma warning disable 618 + SqlMapper.LookupDbType(propertyInfo.PropertyType, "", false, out var handler); +#pragma warning restore 618 + if (propertyInfo.PropertyType.IsSimpleType() || handler != null) + { + return true; + } + + return false; + } + } +} diff --git a/src/NzbDrone.Core/Datastore/TableMapping.cs b/src/NzbDrone.Core/Datastore/TableMapping.cs index 4a8afc288..d2898a8aa 100644 --- a/src/NzbDrone.Core/Datastore/TableMapping.cs +++ b/src/NzbDrone.Core/Datastore/TableMapping.cs @@ -1,148 +1,138 @@ using System; using System.Collections.Generic; -using Marr.Data; -using Marr.Data.Mapping; +using Dapper; using NzbDrone.Common.Reflection; +using NzbDrone.Core.Authentication; using NzbDrone.Core.Blacklisting; using NzbDrone.Core.Configuration; -using NzbDrone.Core.Datastore.Converters; -using NzbDrone.Core.Datastore.Extensions; -using NzbDrone.Core.Download; -using NzbDrone.Core.Download.Pending; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Instrumentation; -using NzbDrone.Core.Jobs; -using NzbDrone.Core.MediaFiles; -using NzbDrone.Core.Profiles.Delay; -using NzbDrone.Core.RemotePathMappings; -using NzbDrone.Core.Notifications; -using NzbDrone.Core.Organizer; -using NzbDrone.Core.Parser.Model; -using NzbDrone.Core.Profiles; -using NzbDrone.Core.Qualities; -using NzbDrone.Core.Restrictions; -using NzbDrone.Core.RootFolders; -using NzbDrone.Core.Tags; -using NzbDrone.Core.ThingiProvider; -using NzbDrone.Core.Movies; -using NzbDrone.Common.Disk; -using NzbDrone.Core.Authentication; using NzbDrone.Core.CustomFilters; using NzbDrone.Core.CustomFormats; +using NzbDrone.Core.Datastore.Converters; +using NzbDrone.Core.Download; +using NzbDrone.Core.Download.Pending; using NzbDrone.Core.Extras.Metadata; using NzbDrone.Core.Extras.Metadata.Files; using NzbDrone.Core.Extras.Others; using NzbDrone.Core.Extras.Subtitles; +using NzbDrone.Core.Indexers; +using NzbDrone.Core.Instrumentation; +using NzbDrone.Core.Jobs; +using NzbDrone.Core.Languages; +using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Movies; +using NzbDrone.Core.Movies.AlternativeTitles; using NzbDrone.Core.NetImport; using NzbDrone.Core.NetImport.ImportExclusions; -using NzbDrone.Core.Movies.AlternativeTitles; -using NzbDrone.Core.Languages; +using NzbDrone.Core.Notifications; +using NzbDrone.Core.Organizer; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Profiles; +using NzbDrone.Core.Profiles.Delay; +using NzbDrone.Core.Qualities; +using NzbDrone.Core.RemotePathMappings; +using NzbDrone.Core.Restrictions; +using NzbDrone.Core.RootFolders; +using NzbDrone.Core.Tags; +using NzbDrone.Core.ThingiProvider; +using static Dapper.SqlMapper; namespace NzbDrone.Core.Datastore { public static class TableMapping { - private static readonly FluentMappings Mapper = new FluentMappings(true); + static TableMapping() + { + Mapper = new TableMapper(); + } + + public static TableMapper Mapper { get; private set; } public static void Map() { RegisterMappers(); - Mapper.Entity().RegisterModel("Config"); + Mapper.Entity("Config").RegisterModel(); - Mapper.Entity().RegisterModel("RootFolders") + Mapper.Entity("RootFolders").RegisterModel() .Ignore(r => r.Accessible) .Ignore(r => r.FreeSpace) .Ignore(r => r.TotalSpace); - Mapper.Entity().RegisterModel("ScheduledTasks"); + Mapper.Entity("ScheduledTasks").RegisterModel(); - Mapper.Entity().RegisterDefinition("Indexers") + Mapper.Entity("Indexers").RegisterModel() + .Ignore(x => x.ImplementationName) .Ignore(i => i.Enable) .Ignore(i => i.Protocol) .Ignore(i => i.SupportsRss) .Ignore(i => i.SupportsSearch) .Ignore(d => d.Tags); - Mapper.Entity().RegisterDefinition("NetImport") - .Ignore(i => i.Enable) - .Relationship() - .HasOne(n => n.Profile, n => n.ProfileId); + Mapper.Entity("NetImport").RegisterModel() + .Ignore(x => x.ImplementationName) + .Ignore(i => i.Enable); - Mapper.Entity().RegisterDefinition("Notifications") + Mapper.Entity("Notifications").RegisterModel() + .Ignore(x => x.ImplementationName) .Ignore(i => i.SupportsOnGrab) .Ignore(i => i.SupportsOnDownload) .Ignore(i => i.SupportsOnUpgrade) .Ignore(i => i.SupportsOnRename) .Ignore(i => i.SupportsOnHealthIssue); - Mapper.Entity().RegisterDefinition("Metadata") + Mapper.Entity("Metadata").RegisterModel() + .Ignore(x => x.ImplementationName) .Ignore(d => d.Tags); - Mapper.Entity().RegisterDefinition("DownloadClients") + Mapper.Entity("DownloadClients").RegisterModel() + .Ignore(x => x.ImplementationName) .Ignore(d => d.Protocol) .Ignore(d => d.Tags); - Mapper.Entity().RegisterModel("History") - .AutoMapChildModels(); + Mapper.Entity("History").RegisterModel(); - Mapper.Entity().RegisterModel("MovieFiles") - .Ignore(f => f.Path) - .Relationships.AutoMapICollectionOrComplexProperties() - .For("Movie") - .LazyLoad(condition: parent => parent.Id > 0, - query: (db, parent) => db.Query().Where(c => c.MovieFileId == parent.Id).ToList()) - .HasOne(file => file.Movie, file => file.MovieId); + Mapper.Entity("MovieFiles").RegisterModel() + .Ignore(f => f.Path); - Mapper.Entity().RegisterModel("Movies") - .Ignore(s => s.RootFolderPath) - .Ignore(m => m.Actors) - .Relationship() - .HasOne(s => s.Profile, s => s.ProfileId); - //.HasOne(m => m.MovieFile, m => m.MovieFileId); + Mapper.Entity("Movies").RegisterModel() + .Ignore(s => s.RootFolderPath) + .Ignore(m => m.Actors); - Mapper.Entity().RegisterModel("AlternativeTitles") - .For(t => t.Id) - .SetAltName("AltTitle_Id") - .Relationship() - .HasOne(t => t.Movie, t => t.MovieId); + Mapper.Entity("AlternativeTitles").RegisterModel(); + Mapper.Entity("ImportExclusions").RegisterModel(); - Mapper.Entity().RegisterModel("ImportExclusions"); + Mapper.Entity("QualityDefinitions").RegisterModel() + .Ignore(d => d.GroupName) + .Ignore(d => d.Weight); - Mapper.Entity().RegisterModel("QualityDefinitions") - .Ignore(d => d.GroupName) - .Ignore(d => d.Weight) - .Relationship(); + Mapper.Entity("CustomFormats").RegisterModel(); - Mapper.Entity().RegisterModel("CustomFormats") - .Relationship(); + Mapper.Entity("Profiles").RegisterModel(); + Mapper.Entity("Logs").RegisterModel(); + Mapper.Entity("NamingConfig").RegisterModel(); + Mapper.Entity("Blacklist").RegisterModel(); + Mapper.Entity("MetadataFiles").RegisterModel(); + Mapper.Entity("SubtitleFiles").RegisterModel(); + Mapper.Entity("ExtraFiles").RegisterModel(); - Mapper.Entity().RegisterModel("Profiles"); - Mapper.Entity().RegisterModel("Logs"); - Mapper.Entity().RegisterModel("NamingConfig"); - Mapper.Entity().RegisterModel("Blacklist"); - Mapper.Entity().RegisterModel("MetadataFiles"); - Mapper.Entity().RegisterModel("SubtitleFiles"); - Mapper.Entity().RegisterModel("ExtraFiles"); - - Mapper.Entity().RegisterModel("PendingReleases") + Mapper.Entity("PendingReleases").RegisterModel() .Ignore(e => e.RemoteMovie); - Mapper.Entity().RegisterModel("RemotePathMappings"); - Mapper.Entity().RegisterModel("Tags"); - Mapper.Entity().RegisterModel("Restrictions"); + Mapper.Entity("RemotePathMappings").RegisterModel(); + Mapper.Entity("Tags").RegisterModel(); + Mapper.Entity("Restrictions").RegisterModel(); - Mapper.Entity().RegisterModel("DelayProfiles"); - Mapper.Entity().RegisterModel("Users"); - Mapper.Entity().RegisterModel("Commands") - .Ignore(c => c.Message); + Mapper.Entity("DelayProfiles").RegisterModel(); + Mapper.Entity("Users").RegisterModel(); + Mapper.Entity("Commands").RegisterModel() + .Ignore(c => c.Message); - Mapper.Entity().RegisterModel("IndexerStatus"); - Mapper.Entity().RegisterModel("DownloadClientStatus"); + Mapper.Entity("IndexerStatus").RegisterModel(); + Mapper.Entity("DownloadClientStatus").RegisterModel(); - Mapper.Entity().RegisterModel("CustomFilters"); + Mapper.Entity("CustomFilters").RegisterModel(); } private static void RegisterMappers() @@ -150,32 +140,30 @@ private static void RegisterMappers() RegisterEmbeddedConverter(); RegisterProviderSettingConverter(); - MapRepository.Instance.RegisterTypeConverter(typeof(int), new Int32Converter()); - MapRepository.Instance.RegisterTypeConverter(typeof(double), new DoubleConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(DateTime), new UtcConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(bool), new BooleanIntConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(Enum), new EnumIntConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(Quality), new QualityIntConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(CustomFormat), new CustomFormatIntConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(List), new EmbeddedDocumentConverter(new QualityIntConverter())); - MapRepository.Instance.RegisterTypeConverter(typeof(List), new EmbeddedDocumentConverter(new CustomFormatIntConverter())); - MapRepository.Instance.RegisterTypeConverter(typeof(List), new EmbeddedDocumentConverter(new QualityTagStringConverter())); - MapRepository.Instance.RegisterTypeConverter(typeof(QualityModel), new EmbeddedDocumentConverter(new CustomFormatIntConverter(), new QualityIntConverter())); - MapRepository.Instance.RegisterTypeConverter(typeof(Dictionary), new EmbeddedDocumentConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(IDictionary), new EmbeddedDocumentConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(List), new EmbeddedDocumentConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(List>), new EmbeddedDocumentConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(Language), new LanguageIntConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(List), new EmbeddedDocumentConverter(new LanguageIntConverter())); - MapRepository.Instance.RegisterTypeConverter(typeof(List), new EmbeddedDocumentConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(ParsedMovieInfo), new EmbeddedDocumentConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(ReleaseInfo), new EmbeddedDocumentConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(HashSet), new EmbeddedDocumentConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(OsPath), new OsPathConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(Guid), new GuidConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(Command), new CommandConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(TimeSpan), new TimeSpanConverter()); - MapRepository.Instance.RegisterTypeConverter(typeof(TimeSpan?), new TimeSpanConverter()); + SqlMapper.RemoveTypeMap(typeof(DateTime)); + SqlMapper.AddTypeHandler(new DapperUtcConverter()); + SqlMapper.AddTypeHandler(new DapperQualityIntConverter()); + SqlMapper.AddTypeHandler(new DapperCustomFormatIntConverter()); + SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter>(new QualityIntConverter())); + SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter>(new CustomFormatIntConverter())); + SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter>(new QualityTagStringConverter())); + SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter(new CustomFormatIntConverter(), new QualityIntConverter())); + SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter>()); + SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter>()); + SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter>()); + SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter>>()); + SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter>()); + SqlMapper.AddTypeHandler(new DapperLanguageIntConverter()); + SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter>(new LanguageIntConverter())); + SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter>()); + SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter()); + SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter()); + SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter>()); + SqlMapper.AddTypeHandler(new OsPathConverter()); + SqlMapper.RemoveTypeMap(typeof(Guid)); + SqlMapper.RemoveTypeMap(typeof(Guid?)); + SqlMapper.AddTypeHandler(new GuidConverter()); + SqlMapper.AddTypeHandler(new CommandConverter()); } private static void RegisterProviderSettingConverter() @@ -185,7 +173,7 @@ private static void RegisterProviderSettingConverter() var providerSettingConverter = new ProviderSettingConverter(); foreach (var embeddedType in settingTypes) { - MapRepository.Instance.RegisterTypeConverter(embeddedType, providerSettingConverter); + SqlMapper.AddTypeHandler(embeddedType, providerSettingConverter); } } @@ -193,16 +181,24 @@ private static void RegisterEmbeddedConverter() { var embeddedTypes = typeof(IEmbeddedDocument).Assembly.ImplementationsOf(); - var embeddedConvertor = new EmbeddedDocumentConverter(); + var embeddedConverterDefinition = typeof(EmbeddedDocumentConverter<>).GetGenericTypeDefinition(); var genericListDefinition = typeof(List<>).GetGenericTypeDefinition(); foreach (var embeddedType in embeddedTypes) { var embeddedListType = genericListDefinition.MakeGenericType(embeddedType); - MapRepository.Instance.RegisterTypeConverter(embeddedType, embeddedConvertor); - MapRepository.Instance.RegisterTypeConverter(embeddedListType, embeddedConvertor); + RegisterEmbeddedConverter(embeddedType, embeddedConverterDefinition); + RegisterEmbeddedConverter(embeddedListType, embeddedConverterDefinition); } } + + private static void RegisterEmbeddedConverter(Type embeddedType, Type embeddedConverterDefinition) + { + var embeddedConverterType = embeddedConverterDefinition.MakeGenericType(embeddedType); + var converter = (ITypeHandler) Activator.CreateInstance(embeddedConverterType); + + SqlMapper.AddTypeHandler(embeddedType, converter); + } } } diff --git a/src/NzbDrone.Core/Datastore/WhereBuilder.cs b/src/NzbDrone.Core/Datastore/WhereBuilder.cs new file mode 100644 index 000000000..893356055 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/WhereBuilder.cs @@ -0,0 +1,303 @@ +using System; +using System.Text; +using System.Linq.Expressions; +using System.Reflection; +using Dapper; +using System.Data; +using System.Linq; +using System.Collections.Generic; + +namespace NzbDrone.Core.Datastore +{ + public class WhereBuilder : ExpressionVisitor + { + private const DbType EnumerableMultiParameter = (DbType)(-1); + + private readonly string _paramNamePrefix; + private int _paramCount = 0; + protected StringBuilder _sb; + + public DynamicParameters Parameters { get; private set; } + + public WhereBuilder(Expression filter) + { + _paramNamePrefix = Guid.NewGuid().ToString().Replace("-", "_"); + Parameters = new DynamicParameters(); + _sb = new StringBuilder(); + + if (filter != null) + { + base.Visit(filter); + } + } + + private string AddParameter(object value, DbType? dbType = null) + { + _paramCount++; + var name = _paramNamePrefix + "_P" + _paramCount; + Parameters.Add(name, value, dbType); + return '@' + name; + } + + protected override Expression VisitBinary(BinaryExpression expression) + { + _sb.Append("("); + + Visit(expression.Left); + + _sb.AppendFormat(" {0} ", Decode(expression)); + + Visit(expression.Right); + + _sb.Append(")"); + + return expression; + } + + protected override Expression VisitMethodCall(MethodCallExpression expression) + { + string method = expression.Method.Name; + + switch (expression.Method.Name) + { + case "Contains": + ParseContainsExpression(expression); + break; + + case "StartsWith": + ParseStartsWith(expression); + break; + + case "EndsWith": + ParseEndsWith(expression); + break; + + default: + string msg = string.Format("'{0}' expressions are not yet implemented in the where clause expression tree parser.", method); + throw new NotImplementedException(msg); + } + + return expression; + } + + + protected override Expression VisitMemberAccess(MemberExpression expression) + { + string tableName = TableMapping.Mapper.TableNameMapping(expression.Expression.Type); + + if (tableName != null) + { + _sb.Append($"\"{tableName}\".\"{expression.Member.Name}\""); + } + else + { + object value = GetRightValue(expression); + + // string is IEnumerable but we don't want to pick up that case + var type = value.GetType(); + var typeInfo = type.GetTypeInfo(); + bool isEnumerable = + type != typeof(string) && ( + typeInfo.ImplementedInterfaces.Any(ti => ti.IsGenericType && ti.GetGenericTypeDefinition() == typeof(IEnumerable<>)) || + (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + ); + + string paramName; + if (isEnumerable) + { + paramName = AddParameter(value, EnumerableMultiParameter); + } + else + { + paramName = AddParameter(value); + } + + _sb.Append(paramName); + } + + return expression; + } + + protected override Expression VisitConstant(ConstantExpression expression) + { + if (expression.Value != null) + { + string paramName = AddParameter(expression.Value); + _sb.Append(paramName); + } + else + { + _sb.Append("NULL"); + } + + return expression; + } + + private object GetRightValue(Expression rightExpression) + { + object rightValue = null; + + var right = rightExpression as ConstantExpression; + if (right == null) // Value is not directly passed in as a constant + { + var rightMemberExp = (rightExpression as MemberExpression); + var parentMemberExpression = rightMemberExp.Expression as MemberExpression; + if (parentMemberExpression != null) // Value is passed in as a property on a parent entity + { + var memberInfo = (rightMemberExp.Expression as MemberExpression).Member; + var container = ((rightMemberExp.Expression as MemberExpression).Expression as ConstantExpression).Value; + var entity = GetFieldValue(container, memberInfo); + rightValue = GetFieldValue(entity, rightMemberExp.Member); + } + else // Value is passed in as a variable + { + var parent = (rightMemberExp.Expression as ConstantExpression).Value; + rightValue = GetFieldValue(parent, rightMemberExp.Member); + } + } + else // Value is passed in directly as a constant + { + rightValue = right.Value; + } + + return rightValue; + } + + private object GetFieldValue(object entity, MemberInfo member) + { + if (member.MemberType == MemberTypes.Field) + { + return (member as FieldInfo).GetValue(entity); + } + if (member.MemberType == MemberTypes.Property) + { + return (member as PropertyInfo).GetValue(entity); + } + + throw new ArgumentException(string.Format("WhereBuilder could not get the value for {0}.{1}.", entity.GetType().Name, member.Name)); + } + + private string Decode(BinaryExpression expression) + { + bool isRightSideNullConstant = expression.Right.NodeType == + ExpressionType.Constant && + ((ConstantExpression)expression.Right).Value == null; + + if (isRightSideNullConstant) + { + switch (expression.NodeType) + { + case ExpressionType.Equal: return "IS"; + case ExpressionType.NotEqual: return "IS NOT"; + } + } + + switch (expression.NodeType) + { + case ExpressionType.AndAlso: return "AND"; + case ExpressionType.And: return "AND"; + case ExpressionType.Equal: return "="; + case ExpressionType.GreaterThan: return ">"; + case ExpressionType.GreaterThanOrEqual: return ">="; + case ExpressionType.LessThan: return "<"; + case ExpressionType.LessThanOrEqual: return "<="; + case ExpressionType.NotEqual: return "<>"; + case ExpressionType.OrElse: return "OR"; + case ExpressionType.Or: return "OR"; + default: throw new NotSupportedException(string.Format("{0} statement is not supported", expression.NodeType.ToString())); + } + } + + private void ParseContainsExpression(MethodCallExpression expression) + { + var list = expression.Object; + + if (list != null && list.Type == typeof(string)) + { + ParseStringContains(expression); + return; + } + + ParseEnumerableContains(expression); + } + + private void ParseEnumerableContains(MethodCallExpression body) + { + // Fish out the list and the item to compare + // It's in a different form for arrays and Lists + var list = body.Object; + Expression item; + + if (list != null) + { + // Generic collection + item = body.Arguments[0]; + } + else + { + // Static method + // Must be Enumerable.Contains(source, item) + if (body.Method.DeclaringType != typeof(Enumerable) || body.Arguments.Count != 2) + { + throw new NotSupportedException("Unexpected form of Enumerable.Contains"); + } + list = body.Arguments[0]; + item = body.Arguments[1]; + } + + _sb.Append("("); + + Visit(item); + + _sb.Append(" IN "); + + Visit(list); + + _sb.Append(")"); + } + + private void ParseStringContains(MethodCallExpression body) + { + _sb.Append("("); + + Visit(body.Object); + + _sb.Append(" LIKE '%' || "); + + Visit(body.Arguments[0]); + + _sb.Append(" || '%')"); + } + + private void ParseStartsWith(MethodCallExpression body) + { + _sb.Append("("); + + Visit(body.Object); + + _sb.Append(" LIKE "); + + Visit(body.Arguments[0]); + + _sb.Append(" || '%')"); + } + + private void ParseEndsWith(MethodCallExpression body) + { + _sb.Append("("); + + Visit(body.Object); + + _sb.Append(" LIKE '%' || "); + + Visit(body.Arguments[0]); + + _sb.Append(")"); + } + + public override string ToString() + { + return _sb.ToString(); + } + } +} diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs index a4e171dff..bdc7b988e 100644 --- a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs +++ b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs @@ -62,7 +62,7 @@ private int CompareAll(params int[] comparers) private int CompareQuality(DownloadDecision x, DownloadDecision y) { - return CompareAll(CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.Movie.Profile.Value.GetIndex(remoteMovie.ParsedMovieInfo.Quality.Quality)), + return CompareAll(CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.Movie.Profile.GetIndex(remoteMovie.ParsedMovieInfo.Quality.Quality)), CompareCustomFormats(x, y), CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.ParsedMovieInfo.Quality.Revision.Real), CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.ParsedMovieInfo.Quality.Revision.Version)); @@ -73,8 +73,8 @@ private int CompareCustomFormats(DownloadDecision x, DownloadDecision y) var left = x.RemoteMovie.ParsedMovieInfo.Quality.CustomFormats.WithNone(); var right = y.RemoteMovie.ParsedMovieInfo.Quality.CustomFormats; - var leftIndicies = QualityModelComparer.GetIndicies(left, x.RemoteMovie.Movie.Profile.Value); - var rightIndicies = QualityModelComparer.GetIndicies(right, y.RemoteMovie.Movie.Profile.Value); + var leftIndicies = QualityModelComparer.GetIndicies(left, x.RemoteMovie.Movie.Profile); + var rightIndicies = QualityModelComparer.GetIndicies(right, y.RemoteMovie.Movie.Profile); var leftTotal = leftIndicies.Sum(); var rightTotal = rightIndicies.Sum(); @@ -87,8 +87,7 @@ private int ComparePreferredWords(DownloadDecision x, DownloadDecision y) return CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => { var title = remoteMovie.Release.Title; - remoteMovie.Movie.Profile.LazyLoad(); - var preferredWords = remoteMovie.Movie.Profile.Value.PreferredTags; + var preferredWords = remoteMovie.Movie.Profile.PreferredTags; if (preferredWords == null) { diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/CustomFormatAllowedByProfileSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/CustomFormatAllowedByProfileSpecification.cs index 3369ac0a3..5a5dff4b7 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/CustomFormatAllowedByProfileSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/CustomFormatAllowedByProfileSpecification.cs @@ -22,7 +22,7 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se { var formats = subject.ParsedMovieInfo.Quality.CustomFormats.WithNone(); _logger.Debug("Checking if report meets custom format requirements. {0}", formats.ToExtendedString()); - var notAllowedFormats = subject.Movie.Profile.Value.FormatItems.Where(v => v.Allowed == false).Select(f => f.Format).ToList(); + var notAllowedFormats = subject.Movie.Profile.FormatItems.Where(v => v.Allowed == false).Select(f => f.Format).ToList(); var notWantedFormats = notAllowedFormats.Intersect(formats); if (notWantedFormats.Any()) { diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/CutoffSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/CutoffSpecification.cs index 52a6ae955..541fb1535 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/CutoffSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/CutoffSpecification.cs @@ -22,7 +22,7 @@ public CutoffSpecification(UpgradableSpecification qualityUpgradableSpecificatio public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { - var profile = subject.Movie.Profile.Value; + var profile = subject.Movie.Profile; if (subject.Movie.MovieFile != null) { @@ -33,7 +33,7 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se var qualityCutoffIndex = profile.GetIndex(profile.Cutoff); var qualityCutoff = profile.Items[qualityCutoffIndex.Index]; - return Decision.Reject("Existing file meets cutoff: {0} - {1}", qualityCutoff, subject.Movie.Profile.Value.Cutoff); + return Decision.Reject("Existing file meets cutoff: {0} - {1}", qualityCutoff, subject.Movie.Profile.Cutoff); } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/LanguageSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/LanguageSpecification.cs index 5631328e8..ca0af52b2 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/LanguageSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/LanguageSpecification.cs @@ -19,7 +19,7 @@ public LanguageSpecification(Logger logger) public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { - var wantedLanguage = subject.Movie.Profile.Value.Language; + var wantedLanguage = subject.Movie.Profile.Language; if (wantedLanguage == Language.Any) { diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/QualityAllowedByProfileSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/QualityAllowedByProfileSpecification.cs index 9da9b5c3c..36122fd47 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/QualityAllowedByProfileSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/QualityAllowedByProfileSpecification.cs @@ -20,7 +20,7 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se { _logger.Debug("Checking if report meets quality requirements. {0}", subject.ParsedMovieInfo.Quality); - var profile = subject.Movie.Profile.Value; + var profile = subject.Movie.Profile; var qualityIndex = profile.GetIndex(subject.ParsedMovieInfo.Quality.Quality); var qualityOrGroup = profile.Items[qualityIndex.Index]; diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs index 7edb8c150..7e267a7c2 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs @@ -35,7 +35,7 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit foreach (var queueItem in matchingMovies) { var remoteMovie = queueItem.RemoteMovie; - var qualityProfile = subject.Movie.Profile.Value; + var qualityProfile = subject.Movie.Profile; _logger.Debug("Checking if existing release in queue meets cutoff. Queued quality is: {0}", remoteMovie.ParsedMovieInfo.Quality); diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs index 13a8f01e6..ddbd9dd9b 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs @@ -37,14 +37,14 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se return Decision.Accept(); } - var profile = subject.Movie.Profile.Value; + var profile = subject.Movie.Profile; var delayProfile = _delayProfileService.BestForTags(subject.Movie.Tags); var delay = delayProfile.GetProtocolDelay(subject.Release.DownloadProtocol); var isPreferredProtocol = subject.Release.DownloadProtocol == delayProfile.PreferredProtocol; // Preferred word count var title = subject.Release.Title; - var preferredWords = subject.Movie.Profile?.Value?.PreferredTags; + var preferredWords = subject.Movie.Profile?.PreferredTags; var preferredCount = 0; if (preferredWords == null) diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeAllowedSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeAllowedSpecification.cs index f99f5c2a0..70720acfd 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeAllowedSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeAllowedSpecification.cs @@ -21,7 +21,7 @@ public UpgradeAllowedSpecification(UpgradableSpecification upgradableSpecificati public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { - var qualityProfile = subject.Movie.Profile.Value; + var qualityProfile = subject.Movie.Profile; if (subject.Movie.MovieFileId != 0) { diff --git a/src/NzbDrone.Core/DiskSpace/DiskSpaceService.cs b/src/NzbDrone.Core/DiskSpace/DiskSpaceService.cs index 63402605c..2eb784917 100644 --- a/src/NzbDrone.Core/DiskSpace/DiskSpaceService.cs +++ b/src/NzbDrone.Core/DiskSpace/DiskSpaceService.cs @@ -42,9 +42,9 @@ public List GetFreeSpace() private IEnumerable GetMoviesRootPaths() { - return _movieService.GetAllMovies() - .Where(s => _diskProvider.FolderExists(s.Path)) - .Select(s => _diskProvider.GetPathRoot(s.Path)) + return _movieService.AllMoviePaths() + .Where(s => _diskProvider.FolderExists(s)) + .Select(s => _diskProvider.GetPathRoot(s)) .Distinct(); } diff --git a/src/NzbDrone.Core/Download/DownloadClientRepository.cs b/src/NzbDrone.Core/Download/DownloadClientRepository.cs index 9acebdeb0..1d04b56f3 100644 --- a/src/NzbDrone.Core/Download/DownloadClientRepository.cs +++ b/src/NzbDrone.Core/Download/DownloadClientRepository.cs @@ -16,4 +16,4 @@ public DownloadClientRepository(IMainDatabase database, IEventAggregator eventAg { } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core/Download/Pending/PendingReleaseRepository.cs b/src/NzbDrone.Core/Download/Pending/PendingReleaseRepository.cs index 97869354b..bdade04f9 100644 --- a/src/NzbDrone.Core/Download/Pending/PendingReleaseRepository.cs +++ b/src/NzbDrone.Core/Download/Pending/PendingReleaseRepository.cs @@ -20,17 +20,17 @@ public PendingReleaseRepository(IMainDatabase database, IEventAggregator eventAg public void DeleteByMovieId(int movieId) { - Delete(r => r.MovieId == movieId); + Delete(movieId); } public List AllByMovieId(int movieId) { - return Query.Where(p => p.MovieId == movieId).ToList(); + return Query(x => x.MovieId == movieId); } public List WithoutFallback() { - return Query.Where(p => p.Reason != PendingReleaseReason.Fallback); + return Query(x => x.Reason != PendingReleaseReason.Fallback); } } } diff --git a/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs b/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs index 2e5cb6bfa..3b71d8ee2 100644 --- a/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs +++ b/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs @@ -346,7 +346,7 @@ private void RemoveGrabbed(RemoteMovie remoteMovie) return; } - var profile = remoteMovie.Movie.Profile.Value; + var profile = remoteMovie.Movie.Profile; foreach (var existingReport in existingReports) { diff --git a/src/NzbDrone.Core/Extras/ExtraService.cs b/src/NzbDrone.Core/Extras/ExtraService.cs index 4c4421478..44d8f5f7f 100644 --- a/src/NzbDrone.Core/Extras/ExtraService.cs +++ b/src/NzbDrone.Core/Extras/ExtraService.cs @@ -2,11 +2,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using Marr.Data; using NLog; using NzbDrone.Common.Disk; using NzbDrone.Core.Configuration; -using NzbDrone.Core.Datastore; using NzbDrone.Core.Extras.Files; using NzbDrone.Core.MediaCover; using NzbDrone.Core.MediaFiles; @@ -168,7 +166,7 @@ private List GetMovieFiles(int movieId) foreach (var movieFile in movieFiles) { - movieFile.Movie = new LazyLoaded(_movieService.GetMovie(movieId)); + movieFile.Movie = _movieService.GetMovie(movieId); } return movieFiles; diff --git a/src/NzbDrone.Core/Extras/Files/ExtraFileRepository.cs b/src/NzbDrone.Core/Extras/Files/ExtraFileRepository.cs index c5d7cba47..2498e887e 100644 --- a/src/NzbDrone.Core/Extras/Files/ExtraFileRepository.cs +++ b/src/NzbDrone.Core/Extras/Files/ExtraFileRepository.cs @@ -24,7 +24,7 @@ public ExtraFileRepository(IMainDatabase database, IEventAggregator eventAggrega public void DeleteForMovie(int movieId) { - Delete(c => c.MovieId == movieId); + Delete(movieId); } public void DeleteForMovieFile(int movieFileId) @@ -34,17 +34,17 @@ public void DeleteForMovieFile(int movieFileId) public List GetFilesByMovie(int movieId) { - return Query.Where(c => c.MovieId == movieId).ToList(); + return Query(x => x.MovieId == movieId); } public List GetFilesByMovieFile(int movieFileId) { - return Query.Where(c => c.MovieFileId == movieFileId).ToList(); + return Query(x => x.MovieFileId == movieFileId); } public TExtraFile FindByPath(string path) { - return Query.Where(c => c.RelativePath == path).SingleOrDefault(); + return Query(x => x.RelativePath == path).SingleOrDefault(); } } } diff --git a/src/NzbDrone.Core/HealthCheck/Checks/MountCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/MountCheck.cs index 88e13c12e..8579b73e5 100644 --- a/src/NzbDrone.Core/HealthCheck/Checks/MountCheck.cs +++ b/src/NzbDrone.Core/HealthCheck/Checks/MountCheck.cs @@ -19,11 +19,11 @@ public MountCheck(IDiskProvider diskProvider, IMovieService movieService) public override HealthCheck Check() { // Not best for optimization but due to possible symlinks and junctions, we get mounts based on series path so internals can handle mount resolution. - var mounts = _movieService.GetAllMovies() - .Select(movie => _diskProvider.GetMount(movie.Path)) - .Where(m => m != null && m.MountOptions != null && m.MountOptions.IsReadOnly) - .DistinctBy(m => m.RootDirectory) - .ToList(); + var mounts = _movieService.AllMoviePaths() + .Select(p => _diskProvider.GetMount(p)) + .Where(m => m != null && m.MountOptions != null && m.MountOptions.IsReadOnly) + .DistinctBy(m => m.RootDirectory) + .ToList(); if (mounts.Any()) { diff --git a/src/NzbDrone.Core/HealthCheck/Checks/RootFolderCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/RootFolderCheck.cs index 16bc754f1..076ba64bc 100644 --- a/src/NzbDrone.Core/HealthCheck/Checks/RootFolderCheck.cs +++ b/src/NzbDrone.Core/HealthCheck/Checks/RootFolderCheck.cs @@ -20,11 +20,11 @@ public RootFolderCheck(IMovieService movieService, IDiskProvider diskProvider) public override HealthCheck Check() { - var missingRootFolders = _movieService.GetAllMovies() - .Select(s => _diskProvider.GetParentFolder(s.Path)) - .Distinct() - .Where(s => !_diskProvider.FolderExists(s)) - .ToList(); + var missingRootFolders = _movieService.AllMoviePaths() + .Select(s => _diskProvider.GetParentFolder(s)) + .Distinct() + .Where(s => !_diskProvider.FolderExists(s)) + .ToList(); if (missingRootFolders.Any()) { diff --git a/src/NzbDrone.Core/History/HistoryRepository.cs b/src/NzbDrone.Core/History/HistoryRepository.cs index dde58ccb9..e049278f0 100644 --- a/src/NzbDrone.Core/History/HistoryRepository.cs +++ b/src/NzbDrone.Core/History/HistoryRepository.cs @@ -1,11 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; -using Marr.Data.QGen; +using Dapper; using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; -using NzbDrone.Core.Qualities; using NzbDrone.Core.Movies; +using NzbDrone.Core.Profiles; +using NzbDrone.Core.Qualities; namespace NzbDrone.Core.History { @@ -14,7 +15,7 @@ public interface IHistoryRepository : IBasicRepository List GetBestQualityInHistory(int movieId); History MostRecentForDownloadId(string downloadId); List FindByDownloadId(string downloadId); - List FindDownloadHistory(int idMovieId, QualityModel quality); + List FindDownloadHistory(int movieId, QualityModel quality); List GetByMovieId(int movieId, HistoryEventType? eventType); void DeleteForMovie(int movieId); History MostRecentForMovie(int movieId); @@ -31,37 +32,35 @@ public HistoryRepository(IMainDatabase database, IEventAggregator eventAggregato public List GetBestQualityInHistory(int movieId) { - var history = Query.Where(c => c.MovieId == movieId).ToList(); + var history = Query(x => x.MovieId == movieId); return history.Select(h => h.Quality).ToList(); } public History MostRecentForDownloadId(string downloadId) { - return Query.Where(h => h.DownloadId == downloadId) - .OrderByDescending(h => h.Date) - .FirstOrDefault(); + return FindByDownloadId(downloadId) + .OrderByDescending(h => h.Date) + .FirstOrDefault(); } public List FindByDownloadId(string downloadId) { - return Query.Where(h => h.DownloadId == downloadId).ToList(); + return Query(x => x.DownloadId == downloadId); } - public List FindDownloadHistory(int idMovieId, QualityModel quality) + public List FindDownloadHistory(int movieId, QualityModel quality) { - return Query.Where(h => - h.MovieId == idMovieId && - h.Quality == quality && - (h.EventType == HistoryEventType.Grabbed || - h.EventType == HistoryEventType.DownloadFailed || - h.EventType == HistoryEventType.DownloadFolderImported) - ).ToList(); + var allowed = new [] { HistoryEventType.Grabbed, HistoryEventType.DownloadFailed, HistoryEventType.DownloadFolderImported }; + + return Query(h => h.MovieId == movieId && + h.Quality == quality && + allowed.Contains(h.EventType)); } public List GetByMovieId(int movieId, HistoryEventType? eventType) { - var query = Query.Where(h => h.MovieId == movieId).ToList(); + var query = Query(x => x.MovieId == movieId); if (eventType.HasValue) { @@ -78,32 +77,45 @@ public void DeleteForMovie(int movieId) Delete(c => c.MovieId == movieId); } - protected override SortBuilder GetPagedQuery(QueryBuilder query, PagingSpec pagingSpec) + private IEnumerable SelectJoined(SqlBuilder.Template sql) { - var baseQuery = query.Join(JoinType.Inner, h => h.Movie, (h, e) => h.MovieId == e.Id); - - return base.GetPagedQuery(baseQuery, pagingSpec); + using (var conn = _database.OpenConnection()) + { + return conn.Query( + sql.RawSql, + (hist, movie, profile) => { + hist.Movie = movie; + hist.Movie.Profile = profile; + return hist; + }, + sql.Parameters) + .ToList(); + } } + protected override SqlBuilder PagedBuilder() => new SqlBuilder() + .Join("Movies ON Movies.Id = History.MovieId") + .Join("Profiles ON Profiles.Id = Movies.ProfileId"); + + protected override IEnumerable PagedSelector(SqlBuilder.Template sql) => SelectJoined(sql); + public History MostRecentForMovie(int movieId) { - return Query.Where(h => h.MovieId == movieId) - .OrderByDescending(h => h.Date) - .FirstOrDefault(); + return Query(x => x.MovieId == movieId) + .OrderByDescending(h => h.Date) + .FirstOrDefault(); } public List Since(DateTime date, HistoryEventType? eventType) { - var query = Query.Where(h => h.Date >= date).ToList(); + var builder = Builder().Where(x => x.Date >= date); if (eventType.HasValue) { - query = query.Where(h => h.EventType == eventType).ToList(); + builder.Where(h => h.EventType == eventType); } - query.OrderBy(h => h.Date); - - return query; + return Query(builder).OrderBy(h => h.Date).ToList(); } } } diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAbsolutePathMetadataFiles.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAbsolutePathMetadataFiles.cs index de6dcc0de..1293d4f07 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAbsolutePathMetadataFiles.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAbsolutePathMetadataFiles.cs @@ -1,4 +1,5 @@ -using NzbDrone.Core.Datastore; +using Dapper; +using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers { @@ -13,9 +14,9 @@ public CleanupAbsolutePathMetadataFiles(IMainDatabase database) public void Clean() { - using (var mapper = _database.GetDataMapper()) + using (var mapper = _database.OpenConnection()) { - mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles + mapper.Execute(@"DELETE FROM MetadataFiles WHERE Id IN ( SELECT Id FROM MetadataFiles WHERE RelativePath diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAdditionalNamingSpecs.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAdditionalNamingSpecs.cs index e63fd2dfa..1b8dfa9d2 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAdditionalNamingSpecs.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAdditionalNamingSpecs.cs @@ -1,4 +1,5 @@ -using NzbDrone.Core.Datastore; +using Dapper; +using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers { @@ -13,10 +14,10 @@ public CleanupAdditionalNamingSpecs(IMainDatabase database) public void Clean() { - using (var mapper = _database.GetDataMapper()) + using (var mapper = _database.OpenConnection()) { - mapper.ExecuteNonQuery(@"DELETE FROM NamingConfig + mapper.Execute(@"DELETE FROM NamingConfig WHERE ID NOT IN ( SELECT ID FROM NamingConfig LIMIT 1)"); diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAdditionalUsers.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAdditionalUsers.cs index 9103dc4c1..a35692a7f 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAdditionalUsers.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAdditionalUsers.cs @@ -1,4 +1,5 @@ -using NzbDrone.Core.Datastore; +using Dapper; +using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers { @@ -13,13 +14,13 @@ public CleanupAdditionalUsers(IMainDatabase database) public void Clean() { - using (var mapper = _database.GetDataMapper()) + using (var mapper = _database.OpenConnection()) { - mapper.ExecuteNonQuery(@"DELETE FROM Users - WHERE ID NOT IN ( - SELECT ID FROM Users - LIMIT 1)"); + mapper.Execute(@"DELETE FROM Users + WHERE ID NOT IN ( + SELECT ID FROM Users + LIMIT 1)"); } } } diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupDownloadClientUnavailablePendingReleases.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupDownloadClientUnavailablePendingReleases.cs index 51c3ba3f9..1e035febd 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupDownloadClientUnavailablePendingReleases.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupDownloadClientUnavailablePendingReleases.cs @@ -1,4 +1,5 @@ using System; +using Dapper; using NzbDrone.Core.Datastore; using NzbDrone.Core.Download.Pending; @@ -15,18 +16,16 @@ public CleanupDownloadClientUnavailablePendingReleases(IMainDatabase database) public void Clean() { - var mapper = _database.GetDataMapper(); - var twoWeeksAgo = DateTime.UtcNow.AddDays(-14); + var mapper = _database.OpenConnection(); - mapper.Delete(p => p.Added < twoWeeksAgo && - (p.Reason == PendingReleaseReason.DownloadClientUnavailable || - p.Reason == PendingReleaseReason.Fallback)); - -// mapper.AddParameter("twoWeeksAgo", $"{DateTime.UtcNow.AddDays(-14).ToString("s")}Z"); - -// mapper.ExecuteNonQuery(@"DELETE FROM PendingReleases -// WHERE Added < @twoWeeksAgo -// AND (Reason = 'DownloadClientUnavailable' OR Reason = 'Fallback')"); + mapper.Execute(@"DELETE FROM PendingReleases + WHERE Added < @TwoWeeksAgo + AND REASON IN @Reasons", + new + { + TwoWeeksAgo = DateTime.UtcNow.AddDays(-14), + Reasons = new [] { (int)PendingReleaseReason.DownloadClientUnavailable, (int) PendingReleaseReason.Fallback } + }); } } } diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupDuplicateMetadataFiles.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupDuplicateMetadataFiles.cs index 5a405de77..f57b6138f 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupDuplicateMetadataFiles.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupDuplicateMetadataFiles.cs @@ -1,3 +1,4 @@ +using Dapper; using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers @@ -19,10 +20,10 @@ public void Clean() private void DeleteDuplicateMovieMetadata() { - using (var mapper = _database.GetDataMapper()) + using (var mapper = _database.OpenConnection()) { - mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles + mapper.Execute(@"DELETE FROM MetadataFiles WHERE Id IN ( SELECT Id FROM MetadataFiles WHERE Type = 1 @@ -34,10 +35,10 @@ HAVING COUNT(MovieId) > 1 private void DeleteDuplicateMovieFileMetadata() { - using (var mapper = _database.GetDataMapper()) + using (var mapper = _database.OpenConnection()) { - mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles + mapper.Execute(@"DELETE FROM MetadataFiles WHERE Id IN ( SELECT Id FROM MetadataFiles WHERE Type = 1 diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedAlternativeTitles.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedAlternativeTitles.cs index 6ef15ac2f..eac8aeba2 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedAlternativeTitles.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedAlternativeTitles.cs @@ -1,4 +1,5 @@ -using NzbDrone.Core.Datastore; +using Dapper; +using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers { @@ -13,10 +14,10 @@ public CleanupOrphanedAlternativeTitles(IMainDatabase database) public void Clean() { - using (var mapper = _database.GetDataMapper()) + using (var mapper = _database.OpenConnection()) { - mapper.ExecuteNonQuery(@"DELETE FROM AlternativeTitles + mapper.Execute(@"DELETE FROM AlternativeTitles WHERE Id IN ( SELECT AlternativeTitles.Id FROM AlternativeTitles LEFT OUTER JOIN Movies diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedBlacklist.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedBlacklist.cs index 7816c5ab9..89a1dfd95 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedBlacklist.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedBlacklist.cs @@ -1,4 +1,5 @@ -using NzbDrone.Core.Datastore; +using Dapper; +using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers { @@ -13,10 +14,10 @@ public CleanupOrphanedBlacklist(IMainDatabase database) public void Clean() { - using (var mapper = _database.GetDataMapper()) + using (var mapper = _database.OpenConnection()) { - mapper.ExecuteNonQuery(@"DELETE FROM Blacklist + mapper.Execute(@"DELETE FROM Blacklist WHERE Id IN ( SELECT Blacklist.Id FROM Blacklist LEFT OUTER JOIN Movies diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedDownloadClientStatus.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedDownloadClientStatus.cs index 3bb631eb9..a5f584797 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedDownloadClientStatus.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedDownloadClientStatus.cs @@ -1,4 +1,5 @@ -using NzbDrone.Core.Datastore; +using Dapper; +using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers { @@ -13,9 +14,9 @@ public CleanupOrphanedDownloadClientStatus(IMainDatabase database) public void Clean() { - var mapper = _database.GetDataMapper(); + var mapper = _database.OpenConnection(); - mapper.ExecuteNonQuery(@"DELETE FROM DownloadClientStatus + mapper.Execute(@"DELETE FROM DownloadClientStatus WHERE Id IN ( SELECT DownloadClientStatus.Id FROM DownloadClientStatus LEFT OUTER JOIN DownloadClients diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedHistoryItems.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedHistoryItems.cs index d69112d97..e27db67f9 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedHistoryItems.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedHistoryItems.cs @@ -1,3 +1,4 @@ +using Dapper; using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers @@ -18,10 +19,10 @@ public void Clean() private void CleanupOrphanedByMovie() { - using (var mapper = _database.GetDataMapper()) + using (var mapper = _database.OpenConnection()) { - mapper.ExecuteNonQuery(@"DELETE FROM History + mapper.Execute(@"DELETE FROM History WHERE Id IN ( SELECT History.Id FROM History LEFT OUTER JOIN Movies diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedIndexerStatus.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedIndexerStatus.cs index 60c956b99..24711689b 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedIndexerStatus.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedIndexerStatus.cs @@ -1,3 +1,4 @@ +using Dapper; using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers @@ -13,10 +14,10 @@ public CleanupOrphanedIndexerStatus(IMainDatabase database) public void Clean() { - using (var mapper = _database.GetDataMapper()) + using (var mapper = _database.OpenConnection()) { - mapper.ExecuteNonQuery(@"DELETE FROM IndexerStatus + mapper.Execute(@"DELETE FROM IndexerStatus WHERE Id IN ( SELECT IndexerStatus.Id FROM IndexerStatus LEFT OUTER JOIN Indexers diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedMetadataFiles.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedMetadataFiles.cs index 1c89b1c46..56e12d18a 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedMetadataFiles.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedMetadataFiles.cs @@ -1,3 +1,4 @@ +using Dapper; using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers @@ -20,10 +21,10 @@ public void Clean() private void DeleteOrphanedByMovie() { - using (var mapper = _database.GetDataMapper()) + using (var mapper = _database.OpenConnection()) { - mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles + mapper.Execute(@"DELETE FROM MetadataFiles WHERE Id IN ( SELECT MetadataFiles.Id FROM MetadataFiles LEFT OUTER JOIN Movies @@ -34,10 +35,10 @@ LEFT OUTER JOIN Movies private void DeleteOrphanedByMovieFile() { - using (var mapper = _database.GetDataMapper()) + using (var mapper = _database.OpenConnection()) { - mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles + mapper.Execute(@"DELETE FROM MetadataFiles WHERE Id IN ( SELECT MetadataFiles.Id FROM MetadataFiles LEFT OUTER JOIN MovieFiles @@ -49,10 +50,10 @@ WHERE MetadataFiles.MovieFileId > 0 private void DeleteWhereMovieFileIsZero() { - using (var mapper = _database.GetDataMapper()) + using (var mapper = _database.OpenConnection()) { - mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles + mapper.Execute(@"DELETE FROM MetadataFiles WHERE Id IN ( SELECT Id FROM MetadataFiles WHERE Type IN (1, 2) diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedMovieFiles.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedMovieFiles.cs index d36d1da66..ee94f857e 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedMovieFiles.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedMovieFiles.cs @@ -1,3 +1,4 @@ +using Dapper; using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers @@ -13,14 +14,14 @@ public CleanupOrphanedMovieFiles(IMainDatabase database) public void Clean() { - using (var mapper = _database.GetDataMapper()) + using (var mapper = _database.OpenConnection()) { - mapper.ExecuteNonQuery(@"DELETE FROM MovieFiles - WHERE Id IN ( - SELECT MovieFiles.Id FROM MovieFiles - LEFT OUTER JOIN Movies - ON MovieFiles.Id = Movies.MovieFileId - WHERE Movies.Id IS NULL)"); + mapper.Execute(@"DELETE FROM MovieFiles + WHERE Id IN ( + SELECT MovieFiles.Id FROM MovieFiles + LEFT OUTER JOIN Movies + ON MovieFiles.Id = Movies.MovieFileId + WHERE Movies.Id IS NULL)"); } } } diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedPendingReleases.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedPendingReleases.cs index bed514b93..0a0b12c7c 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedPendingReleases.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedPendingReleases.cs @@ -1,4 +1,5 @@ -using NzbDrone.Core.Datastore; +using Dapper; +using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers { @@ -13,15 +14,15 @@ public CleanupOrphanedPendingReleases(IMainDatabase database) public void Clean() { - using (var mapper = _database.GetDataMapper()) + using (var mapper = _database.OpenConnection()) { - mapper.ExecuteNonQuery(@"DELETE FROM PendingReleases - WHERE Id IN ( - SELECT PendingReleases.Id FROM PendingReleases - LEFT OUTER JOIN Movies - ON PendingReleases.MovieId = Movies.Id - WHERE Movies.Id IS NULL)"); + mapper.Execute(@"DELETE FROM PendingReleases + WHERE Id IN ( + SELECT PendingReleases.Id FROM PendingReleases + LEFT OUTER JOIN Movies + ON PendingReleases.MovieId = Movies.Id + WHERE Movies.Id IS NULL)"); } } } diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupUnusedTags.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupUnusedTags.cs index f8ead0292..b6811ee4b 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupUnusedTags.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupUnusedTags.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; +using System.Data; using System.Linq; -using Marr.Data; -using NzbDrone.Common.Serializer; +using Dapper; using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers @@ -17,7 +17,7 @@ public CleanupUnusedTags(IMainDatabase database) public void Clean() { - using (var mapper = _database.GetDataMapper()) + using (var mapper = _database.OpenConnection()) { var usedTags = new[] {"Movies", "Notifications", "DelayProfiles", "Restrictions", "NetImport"} @@ -27,16 +27,16 @@ public void Clean() var usedTagsList = string.Join(",", usedTags.Select(d => d.ToString()).ToArray()); - mapper.ExecuteNonQuery($"DELETE FROM Tags WHERE NOT Id IN ({usedTagsList})"); + mapper.Execute($"DELETE FROM Tags WHERE NOT Id IN ({usedTagsList})"); } } - private int[] GetUsedTags(string table, IDataMapper mapper) + private int[] GetUsedTags(string table, IDbConnection mapper) { - return mapper.ExecuteReader($"SELECT DISTINCT Tags FROM {table} WHERE NOT Tags = '[]'", reader => reader.GetString(0)) - .SelectMany(Json.Deserialize>) - .Distinct() - .ToArray(); + return mapper.Query>($"SELECT DISTINCT Tags FROM {table} WHERE NOT Tags = '[]'") + .SelectMany(x => x) + .Distinct() + .ToArray(); } } } diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/FixFutureRunScheduledTasks.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/FixFutureRunScheduledTasks.cs index c4ed9f501..82a7af7fa 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/FixFutureRunScheduledTasks.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/FixFutureRunScheduledTasks.cs @@ -1,4 +1,5 @@ using System; +using Dapper; using NLog; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Core.Datastore; @@ -23,13 +24,12 @@ public void Clean() _logger.Debug("Not running scheduled task last execution cleanup during debug"); } - using (var mapper = _database.GetDataMapper()) + using (var mapper = _database.OpenConnection()) { - mapper.AddParameter("time", DateTime.UtcNow); - - mapper.ExecuteNonQuery(@"UPDATE ScheduledTasks - SET LastExecution = @time - WHERE LastExecution > @time"); + mapper.Execute(@"UPDATE ScheduledTasks + SET LastExecution = @time + WHERE LastExecution > @time", + new { time = DateTime.UtcNow }); } } } diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/FixWronglyMatchedMovieFiles.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/FixWronglyMatchedMovieFiles.cs index f6756cce4..185f6e8aa 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/FixWronglyMatchedMovieFiles.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/FixWronglyMatchedMovieFiles.cs @@ -15,7 +15,7 @@ public void Clean() { /*var mapper = _database.GetDataMapper(); - mapper.ExecuteNonQuery(@"UPDATE Movies + mapper.Execute(@"UPDATE Movies SET MovieFileId = (Select Id FROM MovieFiles WHERE Movies.Id == MovieFiles.MovieId) WHERE MovieFileId != diff --git a/src/NzbDrone.Core/Jobs/ScheduledTaskRepository.cs b/src/NzbDrone.Core/Jobs/ScheduledTaskRepository.cs index 648ca0450..a7444beb2 100644 --- a/src/NzbDrone.Core/Jobs/ScheduledTaskRepository.cs +++ b/src/NzbDrone.Core/Jobs/ScheduledTaskRepository.cs @@ -22,7 +22,7 @@ public ScheduledTaskRepository(IMainDatabase database, IEventAggregator eventAgg public ScheduledTask GetDefinition(Type type) { - return Query.Where(c => c.TypeName == type.FullName).Single(); + return Query(x => x.TypeName == type.FullName).Single(); } public void SetLastExecutionTime(int id, DateTime executionTime) diff --git a/src/NzbDrone.Core/MediaFiles/MediaFileRepository.cs b/src/NzbDrone.Core/MediaFiles/MediaFileRepository.cs index 6ef40f409..852a31070 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaFileRepository.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaFileRepository.cs @@ -21,12 +21,12 @@ public MediaFileRepository(IMainDatabase database, IEventAggregator eventAggrega public List GetFilesByMovie(int movieId) { - return Query.Where(c => c.MovieId == movieId).ToList(); + return Query(x => x.MovieId == movieId); } public List GetFilesWithoutMediaInfo() { - return Query.Where(c => c.MediaInfo == null).ToList(); + return Query(x => x.MediaInfo == null); } } } diff --git a/src/NzbDrone.Core/MediaFiles/MediaFileService.cs b/src/NzbDrone.Core/MediaFiles/MediaFileService.cs index 75a6c6d59..5ec3680c3 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaFileService.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaFileService.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using NLog; using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Movies; @@ -25,27 +24,27 @@ public interface IMediaFileService public class MediaFileService : IMediaFileService, IHandleAsync { - private readonly IEventAggregator _eventAggregator; private readonly IMediaFileRepository _mediaFileRepository; - private readonly Logger _logger; + private readonly IMovieRepository _movieRepository; + private readonly IEventAggregator _eventAggregator; public MediaFileService(IMediaFileRepository mediaFileRepository, - IEventAggregator eventAggregator, Logger logger) + IMovieRepository movieRepository, + IEventAggregator eventAggregator) { _mediaFileRepository = mediaFileRepository; + _movieRepository = movieRepository; _eventAggregator = eventAggregator; - _logger = logger; } public MovieFile Add(MovieFile movieFile) { var addedFile = _mediaFileRepository.Insert(movieFile); - addedFile.Movie.LazyLoad(); - if (addedFile.Movie == null || addedFile.Movie.Value == null) + if (addedFile.Movie == null) { - _logger.Error("Movie is null for the file {0}. Please run the houskeeping command to ensure movies and files are linked correctly."); + addedFile.Movie = _movieRepository.Get(movieFile.MovieId); } - //_movieService.SetFileId(addedFile.Movie.Value, addedFile); //Should not be necessary, but sometimes below fails? + _eventAggregator.PublishEvent(new MovieFileAddedEvent(addedFile)); return addedFile; @@ -64,8 +63,11 @@ public void Update(List movieFiles) public void Delete(MovieFile movieFile, DeleteMediaFileReason reason) { //Little hack so we have the movie attached for the event consumers - movieFile.Movie.LazyLoad(); - movieFile.Path = Path.Combine(movieFile.Movie.Value.Path, movieFile.RelativePath); + if (movieFile.Movie == null) + { + movieFile.Movie = _movieRepository.Get(movieFile.MovieId); + } + movieFile.Path = Path.Combine(movieFile.Movie.Path, movieFile.RelativePath); _mediaFileRepository.Delete(movieFile); _eventAggregator.PublishEvent(new MovieFileDeletedEvent(movieFile, reason)); diff --git a/src/NzbDrone.Core/MediaFiles/MovieFile.cs b/src/NzbDrone.Core/MediaFiles/MovieFile.cs index 0fcfe1e89..cfd1c5775 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieFile.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieFile.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using Marr.Data; -using NzbDrone.Core.CustomFormats; using NzbDrone.Core.Datastore; using NzbDrone.Core.Qualities; using NzbDrone.Core.Movies; @@ -24,7 +22,7 @@ public class MovieFile : ModelBase public List Languages { get; set; } public MediaInfoModel MediaInfo { get; set; } public string Edition { get; set; } - public LazyLoaded Movie { get; set; } + public Movie Movie { get; set; } public override string ToString() { diff --git a/src/NzbDrone.Core/Messaging/Commands/CommandRepository.cs b/src/NzbDrone.Core/Messaging/Commands/CommandRepository.cs index 173d6c6eb..3a790ad2d 100644 --- a/src/NzbDrone.Core/Messaging/Commands/CommandRepository.cs +++ b/src/NzbDrone.Core/Messaging/Commands/CommandRepository.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Data.SQLite; +using Dapper; using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; @@ -10,22 +10,16 @@ public interface ICommandRepository : IBasicRepository { void Trim(); void OrphanStarted(); - List FindCommands(string name); - List FindQueuedOrStarted(string name); List Queued(); - List Started(); void Start(CommandModel command); void End(CommandModel command); } public class CommandRepository : BasicRepository, ICommandRepository { - private readonly IMainDatabase _database; - public CommandRepository(IMainDatabase database, IEventAggregator eventAggregator) : base(database, eventAggregator) { - _database = database; } public void Trim() @@ -37,39 +31,23 @@ public void Trim() public void OrphanStarted() { - using (var mapper = _database.GetDataMapper()) + var sql = @"UPDATE Commands SET Status = @Orphaned, EndedAt = @Ended WHERE Status = @Started"; + var args = new + { + Orphaned = (int) CommandStatus.Orphaned, + Started = (int) CommandStatus.Started, + Ended = DateTime.UtcNow + }; + + using (var conn = _database.OpenConnection()) { - - mapper.Parameters.Add(new SQLiteParameter("@orphaned", (int) CommandStatus.Orphaned)); - mapper.Parameters.Add(new SQLiteParameter("@started", (int) CommandStatus.Started)); - mapper.Parameters.Add(new SQLiteParameter("@ended", DateTime.UtcNow)); - - mapper.ExecuteNonQuery(@"UPDATE Commands - SET Status = @orphaned, EndedAt = @ended - WHERE Status = @started"); + conn.Execute(sql, args); } } - public List FindCommands(string name) - { - return Query.Where(c => c.Name == name).ToList(); - } - - public List FindQueuedOrStarted(string name) - { - return Query.Where(c => c.Name == name) - .AndWhere("[Status] IN (0,1)") - .ToList(); - } - public List Queued() { - return Query.Where(c => c.Status == CommandStatus.Queued); - } - - public List Started() - { - return Query.Where(c => c.Status == CommandStatus.Started); + return Query(x => x.Status == CommandStatus.Queued); } public void Start(CommandModel command) diff --git a/src/NzbDrone.Core/Movies/AlternativeTitles/AlternativeTitle.cs b/src/NzbDrone.Core/Movies/AlternativeTitles/AlternativeTitle.cs index c16826bd7..ab2bd6118 100644 --- a/src/NzbDrone.Core/Movies/AlternativeTitles/AlternativeTitle.cs +++ b/src/NzbDrone.Core/Movies/AlternativeTitles/AlternativeTitle.cs @@ -1,8 +1,6 @@ using System; -using Marr.Data; using NzbDrone.Core.Datastore; using NzbDrone.Core.Parser; -using NzbDrone.Core.Movies; using NzbDrone.Core.Languages; namespace NzbDrone.Core.Movies.AlternativeTitles @@ -17,8 +15,7 @@ public class AlternativeTitle : ModelBase public int Votes { get; set; } public int VoteCount { get; set; } public Language Language { get; set; } - public LazyLoaded Movie { get; set; } - + public AlternativeTitle() { diff --git a/src/NzbDrone.Core/Movies/AlternativeTitles/AlternativeTitleRepository.cs b/src/NzbDrone.Core/Movies/AlternativeTitles/AlternativeTitleRepository.cs index f40eb4776..1e1ede2fd 100644 --- a/src/NzbDrone.Core/Movies/AlternativeTitles/AlternativeTitleRepository.cs +++ b/src/NzbDrone.Core/Movies/AlternativeTitles/AlternativeTitleRepository.cs @@ -1,8 +1,5 @@ using System.Collections.Generic; -using System.Data; using System.Linq; -using Marr.Data; -using NzbDrone.Common.Extensions; using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; @@ -17,27 +14,24 @@ public interface IAlternativeTitleRepository : IBasicRepository, IAlternativeTitleRepository { - protected IMainDatabase _database; - public AlternativeTitleRepository(IMainDatabase database, IEventAggregator eventAggregator) : base(database, eventAggregator) { - _database = database; } public AlternativeTitle FindBySourceId(int sourceId) { - return Query.Where(t => t.SourceId == sourceId).FirstOrDefault(); + return Query(x => x.SourceId == sourceId).FirstOrDefault(); } public List FindBySourceIds(List sourceIds) { - return Query.Where(t => t.SourceId.In(sourceIds)).ToList(); + return Query(x => sourceIds.Contains(x.SourceId)); } public List FindByMovieId(int movieId) { - return Query.Where(t => t.MovieId == movieId).ToList(); + return Query(x => x.MovieId == movieId); } } } diff --git a/src/NzbDrone.Core/Movies/Movie.cs b/src/NzbDrone.Core/Movies/Movie.cs index 311127b1b..532748cab 100644 --- a/src/NzbDrone.Core/Movies/Movie.cs +++ b/src/NzbDrone.Core/Movies/Movie.cs @@ -1,12 +1,9 @@ using System; using System.Collections.Generic; -using Marr.Data; using NzbDrone.Common.Extensions; using NzbDrone.Core.Datastore; using NzbDrone.Core.Profiles; using NzbDrone.Core.MediaFiles; -using System.IO; -using NzbDrone.Core.Movies; using NzbDrone.Core.Movies.AlternativeTitles; namespace NzbDrone.Core.Movies @@ -48,7 +45,7 @@ public Movie() public DateTime? InCinemas { get; set; } public DateTime? PhysicalRelease { get; set; } public String PhysicalReleaseNote { get; set; } - public LazyLoaded Profile { get; set; } + public Profile Profile { get; set; } public HashSet Tags { get; set; } public AddMovieOptions AddOptions { get; set; } public MovieFile MovieFile { get; set; } diff --git a/src/NzbDrone.Core/Movies/MovieRepository.cs b/src/NzbDrone.Core/Movies/MovieRepository.cs index e661aefe0..b6324356a 100644 --- a/src/NzbDrone.Core/Movies/MovieRepository.cs +++ b/src/NzbDrone.Core/Movies/MovieRepository.cs @@ -3,23 +3,22 @@ using System.Collections.Generic; using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; -using NzbDrone.Core.Datastore.Extensions; -using Marr.Data.QGen; -using NzbDrone.Core.MediaFiles; -using NzbDrone.Core.Movies.AlternativeTitles; -using NzbDrone.Core.Parser.RomanNumerals; using NzbDrone.Core.Qualities; +using Dapper; +using NzbDrone.Core.Movies.AlternativeTitles; +using NzbDrone.Core.MediaFiles; +using NzbDrone.Core.Profiles; namespace NzbDrone.Core.Movies { public interface IMovieRepository : IBasicRepository { bool MoviePathExists(string path); - Movie FindByTitle(string cleanTitle); - Movie FindByTitle(string cleanTitle, int year); + List FindByTitles(List titles); List FindByTitleInexact(string cleanTitle); Movie FindByImdbId(string imdbid); Movie FindByTmdbId(int tmdbid); + List FindByTmdbId(List tmdbids); Movie FindByTitleSlug(string slug); List MoviesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored); List MoviesWithFiles(int movieId); @@ -28,42 +27,119 @@ public interface IMovieRepository : IBasicRepository void SetFileId(int fileId, int movieId); PagingSpec MoviesWhereCutoffUnmet(PagingSpec pagingSpec, List qualitiesBelowCutoff); Movie FindByPath(string path); + List AllMoviePaths(); } public class MovieRepository : BasicRepository, IMovieRepository { - protected IMainDatabase _database; - - public MovieRepository(IMainDatabase database, IEventAggregator eventAggregator) + private readonly IProfileRepository _profileRepository; + public MovieRepository(IMainDatabase database, + IProfileRepository profileRepository, + IEventAggregator eventAggregator) : base(database, eventAggregator) { - _database = database; + _profileRepository = profileRepository; + } + + protected override SqlBuilder BuilderBase() => new SqlBuilder() + .Join("Profiles ON Profiles.Id = Movies.ProfileId") + .LeftJoin("AlternativeTitles ON AlternativeTitles.MovieId = Movies.Id") + .LeftJoin("MovieFiles ON MovieFiles.MovieId = Movies.Id"); + + private Movie Map(Dictionary dict, Movie movie, Profile profile, AlternativeTitle altTitle, MovieFile movieFile) + { + Movie movieEntry; + + if (!dict.TryGetValue(movie.Id, out movieEntry)) + { + movieEntry = movie; + movieEntry.Profile = profile; + movieEntry.MovieFile = movieFile; + dict.Add(movieEntry.Id, movieEntry); + } + + if (altTitle != null) + { + movieEntry.AlternativeTitles.Add(altTitle); + } + + return movieEntry; + } + + protected override IEnumerable GetResults(SqlBuilder.Template sql) + { + var movieDictionary = new Dictionary(); + + using (var conn = _database.OpenConnection()) + { + conn.Query( + sql.RawSql, + (movie, profile, altTitle, file) => Map(movieDictionary, movie, profile, altTitle, file), + sql.Parameters); + } + + return movieDictionary.Values; + } + + public override IEnumerable All() + { + // the skips the join on profile and populates manually + // to avoid repeatedly deserializing the same profile + var noProfileTemplate = $"SELECT /**select**/ FROM {_table} /**leftjoin**/ /**where**/ /**orderby**/"; + var sql = Builder().AddTemplate(noProfileTemplate); + + var movieDictionary = new Dictionary(); + var profiles = _profileRepository.All(); + + using (var conn = _database.OpenConnection()) + { + conn.Query( + sql.RawSql, + (movie, altTitle, file) => Map(movieDictionary, movie, null, altTitle, file), + sql.Parameters); + + return movieDictionary.Values.Join(profiles, m => m.ProfileId, p => p.Id, (movie, profile) => { + movie.Profile = profile; + return movie; + }).ToList(); + } } public bool MoviePathExists(string path) { - return Query.Where(c => c.Path == path).Any(); + return Query(x => x.Path == path).Any(); } - public Movie FindByTitle(string cleanTitle) + public List FindByTitles(List titles) { - return FindByTitle(cleanTitle, null); + return Query(Builder().OrWhere(x => titles.Contains(x.CleanTitle)) + .OrWhere(x => titles.Contains(x.CleanTitle))); } - public Movie FindByTitle(string cleanTitle, int year) + public List FindByTitleInexact(string cleanTitle) { - return FindByTitle(cleanTitle, year as int?); + return Query(x => cleanTitle.Contains(x.CleanTitle)); } public Movie FindByImdbId(string imdbid) { var imdbIdWithPrefix = Parser.Parser.NormalizeImdbId(imdbid); - return Query.Where(s => s.ImdbId == imdbIdWithPrefix).SingleOrDefault(); + return Query(x => x.ImdbId == imdbIdWithPrefix).FirstOrDefault(); + } + + public Movie FindByTmdbId(int tmdbid) + { + return Query(x => x.TmdbId == tmdbid).FirstOrDefault(); + } + + public List FindByTmdbId(List tmdbids) + { + return Query(x => tmdbids.Contains(x.TmdbId)); } public List GetMoviesByFileId(int fileId) { - return Query.Where(m => m.MovieFileId == fileId).ToList(); + return Query(x => x.MovieFileId == fileId); } public void SetFileId(int fileId, int movieId) @@ -73,66 +149,51 @@ public void SetFileId(int fileId, int movieId) public Movie FindByTitleSlug(string slug) { - return Query.Where(m => m.TitleSlug == slug).FirstOrDefault(); + return Query(x => x.TitleSlug == slug).FirstOrDefault(); } public List MoviesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored) { - var query = Query.Where(m => - (m.InCinemas >= start && m.InCinemas <= end) || - (m.PhysicalRelease >= start && m.PhysicalRelease <= end)); - - if (!includeUnmonitored) - { - query.AndWhere(e => e.Monitored == true); - } + var builder = Builder() + .Where(m => + (m.InCinemas >= start && m.InCinemas <= end) || + (m.PhysicalRelease >= start && m.PhysicalRelease <= end)); - return query.ToList(); + if (!includeUnmonitored) + { + builder.Where(x => x.Monitored); + } + + return Query(builder); } public List MoviesWithFiles(int movieId) { - return Query.Join(JoinType.Inner, m => m.MovieFile, (m, mf) => m.MovieFileId == mf.Id) - .Where(m => m.Id == movieId).ToList(); + return Query(x => x.MovieFileId != 0); } + public SqlBuilder MoviesWithoutFilesBuilder() => BuilderBase().Where(x => x.MovieFileId == 0); + public PagingSpec MoviesWithoutFiles(PagingSpec pagingSpec) { - pagingSpec.TotalRecords = GetMoviesWithoutFilesQuery(pagingSpec).GetRowCount(); - pagingSpec.Records = GetMoviesWithoutFilesQuery(pagingSpec).ToList(); + pagingSpec.Records = GetPagedRecords(MoviesWithoutFilesBuilder().SelectAll(), pagingSpec, PagedSelector); + pagingSpec.TotalRecords = GetPagedRecordCount(MoviesWithoutFilesBuilder().SelectCount(), pagingSpec); return pagingSpec; } - public SortBuilder GetMoviesWithoutFilesQuery(PagingSpec pagingSpec) - { - return Query.Where(pagingSpec.FilterExpressions.FirstOrDefault()) - .AndWhere(m => m.MovieFileId == 0) - .OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection()) - .Skip(pagingSpec.PagingOffset()) - .Take(pagingSpec.PageSize); - } + public SqlBuilder MoviesWhereCutoffUnmetBuilder(List qualitiesBelowCutoff) => BuilderBase() + .Where(x => x.MovieFileId != 0) + .Where(BuildQualityCutoffWhereClause(qualitiesBelowCutoff)); public PagingSpec MoviesWhereCutoffUnmet(PagingSpec pagingSpec, List qualitiesBelowCutoff) { - pagingSpec.TotalRecords = MoviesWhereCutoffUnmetQuery(pagingSpec, qualitiesBelowCutoff).GetRowCount(); - pagingSpec.Records = MoviesWhereCutoffUnmetQuery(pagingSpec, qualitiesBelowCutoff).ToList(); + pagingSpec.Records = GetPagedRecords(MoviesWhereCutoffUnmetBuilder(qualitiesBelowCutoff).SelectAll(), pagingSpec, PagedSelector); + pagingSpec.TotalRecords = GetPagedRecordCount(MoviesWhereCutoffUnmetBuilder(qualitiesBelowCutoff).SelectCount(), pagingSpec); return pagingSpec; } - private SortBuilder MoviesWhereCutoffUnmetQuery(PagingSpec pagingSpec, List qualitiesBelowCutoff) - { - return Query - .Join(JoinType.Left, e => e.MovieFile, (e, s) => e.MovieFileId == s.Id) - .Where(pagingSpec.FilterExpressions.FirstOrDefault()) - .AndWhere(m => m.MovieFileId != 0) - .AndWhere(BuildQualityCutoffWhereClause(qualitiesBelowCutoff)) - .OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection()) - .Skip(pagingSpec.PagingOffset()) - .Take(pagingSpec.PageSize); - } - private string BuildQualityCutoffWhereClause(List qualitiesBelowCutoff) { var clauses = new List(); @@ -141,86 +202,24 @@ private string BuildQualityCutoffWhereClause(List qualitie { foreach (var belowCutoff in profile.QualityIds) { - clauses.Add(string.Format("([t0].[ProfileId] = {0} AND [t2].[Quality] LIKE '%_quality_: {1},%')", profile.ProfileId, belowCutoff)); + clauses.Add(string.Format($"(\"{_table}\".\"ProfileId\" = {profile.ProfileId} AND \"MovieFile\".\"Quality\" LIKE '%_quality_: {belowCutoff},%')")); } } return string.Format("({0})", string.Join(" OR ", clauses)); } - private string BuildQualityCutoffWhereClauseSpecial(List qualitiesBelowCutoff) - { - var clauses = new List(); - - foreach (var profile in qualitiesBelowCutoff) - { - foreach (var belowCutoff in profile.QualityIds) - { - clauses.Add(string.Format("(Movies.ProfileId = {0} AND MovieFiles.Quality LIKE '%_quality_: {1},%')", profile.ProfileId, belowCutoff)); - } - } - - return string.Format("({0})", string.Join(" OR ", clauses)); - } - - private Movie FindByTitle(string cleanTitle, int? year) - { - cleanTitle = cleanTitle.ToLowerInvariant(); - string cleanTitleWithRomanNumbers = cleanTitle; - string cleanTitleWithArabicNumbers = cleanTitle; - - foreach (ArabicRomanNumeral arabicRomanNumeral in RomanNumeralParser.GetArabicRomanNumeralsMapping()) - { - string arabicNumber = arabicRomanNumeral.ArabicNumeralAsString; - string romanNumber = arabicRomanNumeral.RomanNumeral; - cleanTitleWithRomanNumbers = cleanTitleWithRomanNumbers.Replace(arabicNumber, romanNumber); - cleanTitleWithArabicNumbers = cleanTitleWithArabicNumbers.Replace(romanNumber, arabicNumber); - } - - Movie result = Query.Where(s => s.CleanTitle == cleanTitle).FirstWithYear(year); - - if (result == null) - { - result = Query.Where(movie => movie.CleanTitle == cleanTitleWithArabicNumbers || movie.CleanTitle == cleanTitleWithRomanNumbers) - .FirstWithYear(year); - - if (result == null) - { - result = Query.Where(t => t.CleanTitle == cleanTitle || t.CleanTitle == cleanTitleWithArabicNumbers || t.CleanTitle == cleanTitleWithRomanNumbers) - .FirstWithYear(year); - } - } - - return result; - } - - public List FindByTitleInexact(string cleanTitle) - { - var mapper = _database.GetDataMapper(); - mapper.AddParameter("queryTitle", cleanTitle); - - return AddJoinQueries(mapper.Query()).Where($"instr(@queryTitle, [t0].[CleanTitle])"); - } - - public Movie FindByTmdbId(int tmdbid) - { - return Query.Where(m => m.TmdbId == tmdbid).FirstOrDefault(); - } - public Movie FindByPath(string path) { - return Query.Where(s => s.Path == path) - .FirstOrDefault(); + return Query(x => x.Path == path).FirstOrDefault(); } - protected override QueryBuilder AddJoinQueries(QueryBuilder baseQuery) + public List AllMoviePaths() { - baseQuery = base.AddJoinQueries(baseQuery); - baseQuery = baseQuery.Join(JoinType.Left, m => m.AlternativeTitles, - (m, t) => m.Id == t.MovieId); - baseQuery = baseQuery.Join(JoinType.Left, m => m.MovieFile, (m, f) => m.Id == f.MovieId); - - return baseQuery; + using (var conn = _database.OpenConnection()) + { + return conn.Query("SELECT Path FROM Movies").ToList(); + } } } } diff --git a/src/NzbDrone.Core/Movies/MovieService.cs b/src/NzbDrone.Core/Movies/MovieService.cs index 0ff2e1b24..f81bd7a98 100644 --- a/src/NzbDrone.Core/Movies/MovieService.cs +++ b/src/NzbDrone.Core/Movies/MovieService.cs @@ -14,6 +14,7 @@ using NzbDrone.Core.Configuration; using NzbDrone.Core.Movies.Events; using NzbDrone.Core.NetImport.ImportExclusions; +using NzbDrone.Core.Parser.RomanNumerals; namespace NzbDrone.Core.Movies { @@ -26,11 +27,13 @@ public interface IMovieService List AddMovies(List newMovies); Movie FindByImdbId(string imdbid); Movie FindByTmdbId(int tmdbid); + List FindByTmdbId(List tmdbids); Movie FindByTitle(string title); Movie FindByTitle(string title, int year); Movie FindByTitleInexact(string title, int? year); Movie FindByTitleSlug(string slug); Movie FindByPath(string path); + List AllMoviePaths(); bool MovieExists(Movie movie); Movie GetMovieByFileId(int fileId); List GetMoviesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored); @@ -184,7 +187,7 @@ public List AddMovies(List newMovies) newMovies.ForEach(m => { - MoviePathState defaultState = MoviePathState.Static; + MoviePathState defaultState = MoviePathState.Static; if (!_configService.PathsDefaultStatic) { defaultState = MoviePathState.Dynamic; @@ -205,11 +208,13 @@ public List AddMovies(List newMovies) m.Added = DateTime.UtcNow; }); - var existingMovies = GetAllMovies(); + var potentialMovieCount = newMovies.Count; newMovies = newMovies.DistinctBy(movie => movie.TmdbId).ToList(); // Ensure we don't add the same movie twice + var existingMovies = FindByTmdbId(newMovies.Select(x => x.TmdbId).ToList()); + newMovies = newMovies.ExceptBy(n => n.TmdbId, existingMovies, e => e.TmdbId, EqualityComparer.Default).ToList(); // Ensure we don't add a movie that already exists _movieRepository.InsertMany(newMovies); @@ -223,7 +228,49 @@ public List AddMovies(List newMovies) public Movie FindByTitle(string title) { - return _movieRepository.FindByTitle(title.CleanSeriesTitle()); + return FindByTitle(title.CleanSeriesTitle(), null); + } + + public Movie FindByTitle(string title, int year) + { + return FindByTitle(title.CleanSeriesTitle(), year as int?); + } + + private Movie FindByTitle(string cleanTitle, int? year) + { + cleanTitle = cleanTitle.ToLowerInvariant(); + string cleanTitleWithRomanNumbers = cleanTitle; + string cleanTitleWithArabicNumbers = cleanTitle; + + foreach (ArabicRomanNumeral arabicRomanNumeral in RomanNumeralParser.GetArabicRomanNumeralsMapping()) + { + string arabicNumber = arabicRomanNumeral.ArabicNumeralAsString; + string romanNumber = arabicRomanNumeral.RomanNumeral; + cleanTitleWithRomanNumbers = cleanTitleWithRomanNumbers.Replace(arabicNumber, romanNumber); + cleanTitleWithArabicNumbers = cleanTitleWithArabicNumbers.Replace(romanNumber, arabicNumber); + } + + var candidates = _movieRepository.FindByTitles(new List { cleanTitle, cleanTitleWithArabicNumbers, cleanTitleWithRomanNumbers }); + + var result = candidates.Where(x => x.CleanTitle == cleanTitle).FirstWithYear(year); + + if (result == null) + { + result = + candidates.Where(movie => movie.CleanTitle == cleanTitleWithArabicNumbers).FirstWithYear(year) ?? + candidates.Where(movie => movie.CleanTitle == cleanTitleWithRomanNumbers).FirstWithYear(year); + + if (result == null) + { + result = candidates + .Where(m => m.AlternativeTitles.Any(t => t.CleanTitle == cleanTitle || + t.CleanTitle == cleanTitleWithArabicNumbers || + t.CleanTitle == cleanTitleWithRomanNumbers)) + .FirstWithYear(year); + } + } + + return result; } public Movie FindByImdbId(string imdbid) @@ -236,6 +283,11 @@ public Movie FindByTmdbId(int tmdbid) return _movieRepository.FindByTmdbId(tmdbid); } + public List FindByTmdbId(List tmdbids) + { + return _movieRepository.FindByTmdbId(tmdbids); + } + private List FindByTitleInexactAll(string title) { // find any movie clean title within the provided release title @@ -284,16 +336,16 @@ public Movie FindByTitleInexact(string title, int? year) return FindByTitleInexactAll(title).FirstWithYear(year); } - public Movie FindByTitle(string title, int year) - { - return _movieRepository.FindByTitle(title.CleanSeriesTitle(), year); - } - public Movie FindByPath(string path) { return _movieRepository.FindByPath(path); } + public List AllMoviePaths() + { + return _movieRepository.AllMoviePaths(); + } + public void DeleteMovie(int movieId, bool deleteFiles, bool addExclusion = false) { var movie = _movieRepository.Get(movieId); @@ -364,11 +416,11 @@ public void RemoveAddOptions(Movie movie) public void Handle(MovieFileAddedEvent message) { - var movie = message.MovieFile.Movie.Value; + var movie = message.MovieFile.Movie; movie.MovieFileId = message.MovieFile.Id; _movieRepository.Update(movie); //_movieRepository.SetFileId(message.MovieFile.Id, message.MovieFile.Movie.Value.Id); - _logger.Info("Linking [{0}] > [{1}]", message.MovieFile.RelativePath, message.MovieFile.Movie.Value); + _logger.Info("Linking [{0}] > [{1}]", message.MovieFile.RelativePath, message.MovieFile.Movie); } public void SetFileId(Movie movie, MovieFile movieFile) @@ -445,7 +497,7 @@ public bool MovieExists(Movie movie) if (movie.Year > 1850) { - result = _movieRepository.FindByTitle(movie.Title.CleanSeriesTitle(), movie.Year); + result = FindByTitle(movie.Title.CleanSeriesTitle(), movie.Year); if (result != null) { return true; @@ -453,7 +505,7 @@ public bool MovieExists(Movie movie) } else { - result = _movieRepository.FindByTitle(movie.Title.CleanSeriesTitle()); + result = FindByTitle(movie.Title.CleanSeriesTitle()); if (result != null) { return true; diff --git a/src/NzbDrone.Core/Movies/QueryExtensions.cs b/src/NzbDrone.Core/Movies/QueryExtensions.cs index 17847d50a..c2876631a 100644 --- a/src/NzbDrone.Core/Movies/QueryExtensions.cs +++ b/src/NzbDrone.Core/Movies/QueryExtensions.cs @@ -1,17 +1,8 @@ using System.Collections.Generic; using System.Linq; -using Marr.Data.QGen; namespace NzbDrone.Core.Movies { - public static class QueryExtensions - { - public static Movie FirstWithYear(this SortBuilder query, int? year) - { - return year.HasValue ? query.AndWhere(movie => movie.Year == year || movie.SecondaryYear == year).FirstOrDefault() : query.FirstOrDefault(); - } - } - public static class EnumerableExtensions { public static Movie FirstWithYear(this IEnumerable query, int? year) diff --git a/src/NzbDrone.Core/NetImport/ImportExclusions/ImportExclusion.cs b/src/NzbDrone.Core/NetImport/ImportExclusions/ImportExclusion.cs index 1f8f1bdae..7291971b4 100644 --- a/src/NzbDrone.Core/NetImport/ImportExclusions/ImportExclusion.cs +++ b/src/NzbDrone.Core/NetImport/ImportExclusions/ImportExclusion.cs @@ -1,11 +1,4 @@ -using System; -using System.Collections.Generic; -using Marr.Data; -using NzbDrone.Common.Extensions; -using NzbDrone.Core.Datastore; -using NzbDrone.Core.Profiles; -using NzbDrone.Core.MediaFiles; -using System.IO; +using NzbDrone.Core.Datastore; namespace NzbDrone.Core.NetImport.ImportExclusions { diff --git a/src/NzbDrone.Core/NetImport/ImportExclusions/ImportExclusionsRepository.cs b/src/NzbDrone.Core/NetImport/ImportExclusions/ImportExclusionsRepository.cs index 1af12f95e..9c072991b 100644 --- a/src/NzbDrone.Core/NetImport/ImportExclusions/ImportExclusionsRepository.cs +++ b/src/NzbDrone.Core/NetImport/ImportExclusions/ImportExclusionsRepository.cs @@ -1,14 +1,6 @@ -using System; using System.Linq; -using System.Collections.Generic; using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; -using NzbDrone.Core.Datastore.Extensions; -using Marr.Data.QGen; -using NzbDrone.Core.MediaFiles; -using NzbDrone.Core.Parser.RomanNumerals; -using NzbDrone.Core.Qualities; -using CoreParser = NzbDrone.Core.Parser.Parser; namespace NzbDrone.Core.NetImport.ImportExclusions { @@ -20,22 +12,19 @@ public interface IImportExclusionsRepository : IBasicRepository public class ImportExclusionsRepository : BasicRepository, IImportExclusionsRepository { - protected IMainDatabase _database; - public ImportExclusionsRepository(IMainDatabase database, IEventAggregator eventAggregator) : base(database, eventAggregator) { - _database = database; } public bool IsMovieExcluded(int tmdbid) { - return Query.Where(ex => ex.TmdbId == tmdbid).Any(); + return Query(x => x.TmdbId == tmdbid).Any(); } public ImportExclusion GetByTmdbid(int tmdbid) { - return Query.Where(ex => ex.TmdbId == tmdbid).First(); + return Query(x => x.TmdbId == tmdbid).First(); } } } diff --git a/src/NzbDrone.Core/NetImport/NetImportDefinition.cs b/src/NzbDrone.Core/NetImport/NetImportDefinition.cs index d08b71b7f..ef19e7838 100644 --- a/src/NzbDrone.Core/NetImport/NetImportDefinition.cs +++ b/src/NzbDrone.Core/NetImport/NetImportDefinition.cs @@ -1,6 +1,4 @@ using System.Collections.Generic; -using Marr.Data; -using NzbDrone.Core.Profiles; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Movies; @@ -18,7 +16,6 @@ public NetImportDefinition() public bool ShouldMonitor { get; set; } public MovieStatusType MinimumAvailability { get; set; } public int ProfileId { get; set; } - public LazyLoaded Profile { get; set; } public string RootFolderPath { get; set; } public override bool Enable => Enabled; } diff --git a/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs b/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs index 6d0d61464..75977d7b4 100644 --- a/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs +++ b/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs @@ -26,7 +26,7 @@ public class ReleaseInfo public string Codec { get; set; } public string Resolution { get; set; } - public IndexerFlags IndexerFlags { get; set; } + public IndexerFlags IndexerFlags { get; set; } public int Age { diff --git a/src/NzbDrone.Core/Profiles/ProfileRepository.cs b/src/NzbDrone.Core/Profiles/ProfileRepository.cs index b49546dc6..b50d15ed8 100644 --- a/src/NzbDrone.Core/Profiles/ProfileRepository.cs +++ b/src/NzbDrone.Core/Profiles/ProfileRepository.cs @@ -17,7 +17,7 @@ public ProfileRepository(IMainDatabase database, IEventAggregator eventAggregato public bool Exists(int id) { - return DataMapper.Query().Where(p => p.Id == id).GetRowCount() == 1; + return Query(x => x.Id == id).Count == 1; } } } diff --git a/src/NzbDrone.Core/Qualities/QualityDefinitionRepository.cs b/src/NzbDrone.Core/Qualities/QualityDefinitionRepository.cs index 8f79230e5..49941710c 100644 --- a/src/NzbDrone.Core/Qualities/QualityDefinitionRepository.cs +++ b/src/NzbDrone.Core/Qualities/QualityDefinitionRepository.cs @@ -1,7 +1,4 @@ -using System.Collections.Generic; -using System.Linq; -using Marr.Data.QGen; -using NzbDrone.Core.Datastore; +using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; diff --git a/src/NzbDrone.Core/Qualities/Revision.cs b/src/NzbDrone.Core/Qualities/Revision.cs index e8e7108e5..8ece998f8 100644 --- a/src/NzbDrone.Core/Qualities/Revision.cs +++ b/src/NzbDrone.Core/Qualities/Revision.cs @@ -5,8 +5,9 @@ namespace NzbDrone.Core.Qualities { public class Revision : IEquatable, IComparable { - private Revision() + public Revision() { + Version = 1; } public Revision(int version = 1, int real = 0, bool isRepack = false) diff --git a/src/NzbDrone.Core/Radarr.Core.csproj b/src/NzbDrone.Core/Radarr.Core.csproj index 254dd81ce..01e4f6b27 100644 --- a/src/NzbDrone.Core/Radarr.Core.csproj +++ b/src/NzbDrone.Core/Radarr.Core.csproj @@ -3,6 +3,8 @@ net462;netcoreapp3.1 + + @@ -15,9 +17,9 @@ + - diff --git a/src/NzbDrone.Core/Tags/TagRepository.cs b/src/NzbDrone.Core/Tags/TagRepository.cs index a35228094..037027dea 100644 --- a/src/NzbDrone.Core/Tags/TagRepository.cs +++ b/src/NzbDrone.Core/Tags/TagRepository.cs @@ -20,7 +20,7 @@ public TagRepository(IMainDatabase database, IEventAggregator eventAggregator) public Tag GetByLabel(string label) { - var model = Query.Where(c => c.Label == label).SingleOrDefault(); + var model = FindByLabel(label); if (model == null) { @@ -32,7 +32,7 @@ public Tag GetByLabel(string label) public Tag FindByLabel(string label) { - return Query.Where(c => c.Label == label).SingleOrDefault(); + return Query(x => x.Label == label).SingleOrDefault(); } } } diff --git a/src/NzbDrone.Core/ThingiProvider/ProviderRepository.cs b/src/NzbDrone.Core/ThingiProvider/ProviderRepository.cs index 1554ef789..a4f03e6c5 100644 --- a/src/NzbDrone.Core/ThingiProvider/ProviderRepository.cs +++ b/src/NzbDrone.Core/ThingiProvider/ProviderRepository.cs @@ -1,20 +1,52 @@ -using NzbDrone.Core.Datastore; +using System.Collections.Generic; +using System.Text.Json; +using Dapper; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Reflection; +using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; namespace NzbDrone.Core.ThingiProvider { public class ProviderRepository : BasicRepository, IProviderRepository - where TProviderDefinition : ModelBase, - new() + where TProviderDefinition : ProviderDefinition, new() { protected ProviderRepository(IMainDatabase database, IEventAggregator eventAggregator) : base(database, eventAggregator) { } -// public void DeleteImplementations(string implementation) -// { -// DataMapper.Delete(c => c.Implementation == implementation); -// } + protected override IEnumerable GetResults(SqlBuilder.Template sql) + { + var results = new List(); + + using (var conn = _database.OpenConnection()) + using (var reader = conn.ExecuteReader(sql.RawSql, sql.Parameters)) + { + var parser = reader.GetRowParser(typeof(TProviderDefinition)); + var settingsIndex = reader.GetOrdinal("Settings"); + var serializerSettings = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + + while (reader.Read()) + { + var body = reader.IsDBNull(settingsIndex) ? null : reader.GetString(settingsIndex); + var item = parser(reader); + var impType = typeof(IProviderConfig).Assembly.FindTypeByName(item.ConfigContract); + + if (body.IsNullOrWhiteSpace()) + { + item.Settings = NullConfig.Instance; + } + else + { + item.Settings = (IProviderConfig) JsonSerializer.Deserialize(body, impType, serializerSettings); + } + + results.Add(item); + } + } + + return results; + } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core/ThingiProvider/Status/ProviderStatusRepository.cs b/src/NzbDrone.Core/ThingiProvider/Status/ProviderStatusRepository.cs index c2782b409..4484a09bf 100644 --- a/src/NzbDrone.Core/ThingiProvider/Status/ProviderStatusRepository.cs +++ b/src/NzbDrone.Core/ThingiProvider/Status/ProviderStatusRepository.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; using System.Linq; -using System.Text; using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; @@ -24,7 +21,7 @@ public ProviderStatusRepository(IMainDatabase database, IEventAggregator eventAg public TModel FindByProviderId(int providerId) { - return Query.Where(c => c.ProviderId == providerId).SingleOrDefault(); + return Query(x => x.ProviderId == providerId).SingleOrDefault(); } } } diff --git a/src/NzbDrone.Core/Validation/Paths/MovieAncestorValidator.cs b/src/NzbDrone.Core/Validation/Paths/MovieAncestorValidator.cs index 91df0ffa3..ad920cc1d 100644 --- a/src/NzbDrone.Core/Validation/Paths/MovieAncestorValidator.cs +++ b/src/NzbDrone.Core/Validation/Paths/MovieAncestorValidator.cs @@ -19,7 +19,7 @@ protected override bool IsValid(PropertyValidatorContext context) { if (context.PropertyValue == null) return true; - return !_movieService.GetAllMovies().Any(s => context.PropertyValue.ToString().IsParentPath(s.Path)); + return !_movieService.AllMoviePaths().Any(s => context.PropertyValue.ToString().IsParentPath(s)); } } } diff --git a/src/NzbDrone.Host.Test/ContainerFixture.cs b/src/NzbDrone.Host.Test/ContainerFixture.cs index 08bc392b1..d878cef98 100644 --- a/src/NzbDrone.Host.Test/ContainerFixture.cs +++ b/src/NzbDrone.Host.Test/ContainerFixture.cs @@ -17,6 +17,7 @@ using NzbDrone.Core.Download.TrackedDownloads; using NzbDrone.SignalR; using Moq; +using NzbDrone.Core.CustomFormats; namespace NzbDrone.App.Test { @@ -37,6 +38,11 @@ public void SetUp() // set up a dummy broadcaster to allow tests to resolve var mockBroadcaster = new Mock(); _container.Register(mockBroadcaster.Object); + + // A dummy custom format repository since this isn't a DB test + var mockCustomFormat = Mocker.GetMock(); + mockCustomFormat.Setup(x => x.All()).Returns(new List()); + _container.Register(mockCustomFormat.Object); } [Test] diff --git a/src/NzbDrone.Integration.Test/IntegrationTestBase.cs b/src/NzbDrone.Integration.Test/IntegrationTestBase.cs index 30809f45f..7da8593bb 100644 --- a/src/NzbDrone.Integration.Test/IntegrationTestBase.cs +++ b/src/NzbDrone.Integration.Test/IntegrationTestBase.cs @@ -10,7 +10,6 @@ using NLog.Targets; using NUnit.Framework; using Radarr.Api.V3.Blacklist; -using Radarr.Api.V3.Commands; using Radarr.Api.V3.Config; using Radarr.Api.V3.DownloadClient; using Radarr.Api.V3.MovieFiles; @@ -20,17 +19,13 @@ using Radarr.Api.V3.Movies; using Radarr.Api.V3.Tags; using NzbDrone.Common.EnvironmentInfo; -using NzbDrone.Common.Serializer; -using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.Qualities; using NzbDrone.Core.Movies.Commands; using NzbDrone.Integration.Test.Client; using NzbDrone.SignalR; using NzbDrone.Test.Common.Categories; using RestSharp; -using NzbDrone.Test.Common; using System.Threading.Tasks; -using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR.Client; namespace NzbDrone.Integration.Test diff --git a/src/NzbDrone.Test.Common/NzbDroneRunner.cs b/src/NzbDrone.Test.Common/NzbDroneRunner.cs index ee88e6e2c..85a1719f5 100644 --- a/src/NzbDrone.Test.Common/NzbDroneRunner.cs +++ b/src/NzbDrone.Test.Common/NzbDroneRunner.cs @@ -2,9 +2,7 @@ using System.Diagnostics; using System.IO; using System.Threading; -using System.Xml; using System.Xml.Linq; -using System.Xml.XPath; using NLog; using NUnit.Framework; using NzbDrone.Common.EnvironmentInfo; @@ -77,11 +75,11 @@ public void Start() if (statusCall.ResponseStatus == ResponseStatus.Completed) { - Console.WriteLine("Radarr is started. Running Tests"); + TestContext.Progress.WriteLine("Radarr is started. Running Tests"); return; } - Console.WriteLine("Waiting for Radarr to start. Response Status : {0} [{1}] {2}", statusCall.ResponseStatus, statusCall.StatusDescription, statusCall.ErrorException.Message); + TestContext.Progress.WriteLine("Waiting for Radarr to start. Response Status : {0} [{1}] {2}", statusCall.ResponseStatus, statusCall.StatusDescription, statusCall.ErrorException.Message); Thread.Sleep(500); } @@ -116,7 +114,7 @@ private void Start(string outputRadarrConsoleExe) private void OnOutputDataReceived(string data) { - Console.WriteLine(data); + TestContext.Progress.WriteLine(data); if (data.Contains("Press enter to exit")) { diff --git a/src/Radarr.Api.V3/History/HistoryModule.cs b/src/Radarr.Api.V3/History/HistoryModule.cs index 89d2c16f0..f07f8d470 100644 --- a/src/Radarr.Api.V3/History/HistoryModule.cs +++ b/src/Radarr.Api.V3/History/HistoryModule.cs @@ -44,7 +44,7 @@ protected HistoryResource MapToResource(NzbDrone.Core.History.History model, boo if (model.Movie != null) { - resource.QualityCutoffNotMet = _upgradableSpecification.CutoffNotMet(model.Movie.Profile.Value, model.Quality); + resource.QualityCutoffNotMet = _upgradableSpecification.CutoffNotMet(model.Movie.Profile, model.Quality); } return resource; diff --git a/src/Radarr.Api.V3/Indexers/ReleaseModuleBase.cs b/src/Radarr.Api.V3/Indexers/ReleaseModuleBase.cs index e37307f14..65ecadc5c 100644 --- a/src/Radarr.Api.V3/Indexers/ReleaseModuleBase.cs +++ b/src/Radarr.Api.V3/Indexers/ReleaseModuleBase.cs @@ -29,7 +29,7 @@ protected virtual ReleaseResource MapDecision(DownloadDecision decision, int ini if (decision.RemoteMovie.Movie != null) { release.QualityWeight = decision.RemoteMovie.Movie - .Profile.Value.GetIndex(release.Quality.Quality).Index * 100; + .Profile.GetIndex(release.Quality.Quality).Index * 100; } release.QualityWeight += release.Quality.Revision.Real * 10; diff --git a/src/Radarr.Api.V3/MovieFiles/MovieFileResource.cs b/src/Radarr.Api.V3/MovieFiles/MovieFileResource.cs index 2f266671a..397514896 100644 --- a/src/Radarr.Api.V3/MovieFiles/MovieFileResource.cs +++ b/src/Radarr.Api.V3/MovieFiles/MovieFileResource.cs @@ -64,7 +64,7 @@ public static MovieFileResource ToResource(this MovieFile model, NzbDrone.Core.M Quality = model.Quality, Languages = model.Languages, MediaInfo = model.MediaInfo.ToResource(model.SceneName), - // QualityCutoffNotMet = upgradableSpecification.CutoffNotMet(movie.Profile.Value, model.Quality) + // QualityCutoffNotMet = upgradableSpecification.CutoffNotMet(movie.Profile, model.Quality) }; } @@ -85,7 +85,7 @@ public static MovieFileResource ToResource(this MovieFile model, NzbDrone.Core.M Quality = model.Quality, Languages = model.Languages, MediaInfo = model.MediaInfo.ToResource(model.SceneName), - QualityCutoffNotMet = upgradableSpecification.CutoffNotMet(movie.Profile.Value, model.Quality) + QualityCutoffNotMet = upgradableSpecification.CutoffNotMet(movie.Profile, model.Quality) }; } }