diff --git a/src/NzbDrone.Api/History/HistoryModule.cs b/src/NzbDrone.Api/History/HistoryModule.cs index 85a56c576..76325d82e 100644 --- a/src/NzbDrone.Api/History/HistoryModule.cs +++ b/src/NzbDrone.Api/History/HistoryModule.cs @@ -1,11 +1,13 @@ 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 @@ -35,7 +37,7 @@ namespace NzbDrone.Api.History if (model.Movie != null) { - resource.QualityCutoffNotMet = _qualityUpgradableSpecification.CutoffNotMet(model.Movie.Profile, model.Quality); + resource.QualityCutoffNotMet = _qualityUpgradableSpecification.CutoffNotMet(model.Movie.Profile.Value, model.Quality); } return resource; diff --git a/src/NzbDrone.Api/Indexers/ReleaseModuleBase.cs b/src/NzbDrone.Api/Indexers/ReleaseModuleBase.cs index 018228c92..3dca0acce 100644 --- a/src/NzbDrone.Api/Indexers/ReleaseModuleBase.cs +++ b/src/NzbDrone.Api/Indexers/ReleaseModuleBase.cs @@ -29,7 +29,7 @@ namespace NzbDrone.Api.Indexers if (decision.RemoteMovie.Movie != null) { release.QualityWeight = decision.RemoteMovie.Movie - .Profile + .Profile.Value .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 ca18bdecb..8847346c0 100644 --- a/src/NzbDrone.Api/Movies/AlternativeTitleModule.cs +++ b/src/NzbDrone.Api/Movies/AlternativeTitleModule.cs @@ -1,6 +1,19 @@ +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 e14ef90cf..eb40041b4 100644 --- a/src/NzbDrone.Api/Movies/AlternativeYearModule.cs +++ b/src/NzbDrone.Api/Movies/AlternativeYearModule.cs @@ -1,7 +1,20 @@ 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 201ae2507..81a04fa0d 100644 --- a/src/NzbDrone.Api/Movies/MovieBulkImportModule.cs +++ b/src/NzbDrone.Api/Movies/MovieBulkImportModule.cs @@ -1,10 +1,12 @@ 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 96ff4021b..d8c5d26a4 100644 --- a/src/NzbDrone.Common.Test/ServiceFactoryFixture.cs +++ b/src/NzbDrone.Common.Test/ServiceFactoryFixture.cs @@ -7,8 +7,6 @@ using NzbDrone.Core.Lifecycle; using NzbDrone.Core.Messaging.Events; using Radarr.Host; using NzbDrone.Test.Common; -using NzbDrone.Core.CustomFormats; -using System.Collections.Generic; namespace NzbDrone.Common.Test { @@ -22,11 +20,6 @@ namespace NzbDrone.Common.Test 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>() @@ -35,4 +28,4 @@ namespace NzbDrone.Common.Test 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 c7928056c..b06b6d736 100644 --- a/src/NzbDrone.Common/Extensions/IEnumerableExtensions.cs +++ b/src/NzbDrone.Common/Extensions/IEnumerableExtensions.cs @@ -133,6 +133,10 @@ namespace NzbDrone.Common.Extensions 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 be04adebe..fcad2a48e 100644 --- a/src/NzbDrone.Core.Test/BulkImport/AddMultiMoviesFixture.cs +++ b/src/NzbDrone.Core.Test/BulkImport/AddMultiMoviesFixture.cs @@ -5,76 +5,71 @@ using Moq; 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); - Mocker.GetMock().Setup(s => s.FindByTmdbId(It.IsAny>())) - .Returns(new List()); + var movies = Subject.AddMovies(fakeMovies); - var movies = Subject.AddMovies(fakeMovies); + foreach (Movie movie in movies) + { + movie.Path.Should().NotBeNullOrEmpty(); + } - foreach (Movie movie in movies) - { - movie.Path.Should().NotBeNullOrEmpty(); - } + //Subject.GetAllMovies().Should().HaveCount(3); + } - // Subject.GetAllMovies().Should().HaveCount(3); - } + [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); - [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); + Mocker.GetMock().Setup(s => s.All()).Returns(new List { fakeMovies[0] }); - Mocker.GetMock().Setup(s => s.FindByTmdbId(It.IsAny>())) - .Returns(new List { fakeMovies[0] }); + var movies = Subject.AddMovies(fakeMovies); - var movies = Subject.AddMovies(fakeMovies); + Mocker.GetMock().Verify(v => v.InsertMany(It.Is>(l => l.Count == 2))); + } - Mocker.GetMock().Verify(v => v.InsertMany(It.Is>(l => l.Count == 2))); - } + [Test] + public void movies_added_should_ignore_duplicates() + { + Mocker.GetMock() + .Setup(s => s.GetMovieFolder(It.IsAny(), null)) + .Returns((Movie m, NamingConfig n) => m.Title); - [Test] - public void movies_added_should_ignore_duplicates() - { - Mocker.GetMock() - .Setup(s => s.GetMovieFolder(It.IsAny(), null)) - .Returns((Movie m, NamingConfig n) => m.Title); + fakeMovies[2].TmdbId = fakeMovies[0].TmdbId; - Mocker.GetMock().Setup(s => s.FindByTmdbId(It.IsAny>())) - .Returns(new List()); + var movies = Subject.AddMovies(fakeMovies); - fakeMovies[2].TmdbId = fakeMovies[0].TmdbId; + Mocker.GetMock().Verify(v => v.InsertMany(It.Is>(l => l.Count == 2))); + } - var movies = Subject.AddMovies(fakeMovies); - - Mocker.GetMock().Verify(v => v.InsertMany(It.Is>(l => l.Count == 2))); - } - } -} + } +} \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/Datastore/BasicRepositoryFixture.cs b/src/NzbDrone.Core.Test/Datastore/BasicRepositoryFixture.cs index e8b8b0a29..e4775e037 100644 --- a/src/NzbDrone.Core.Test/Datastore/BasicRepositoryFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/BasicRepositoryFixture.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using FizzWare.NBuilder; using FluentAssertions; @@ -16,7 +15,7 @@ namespace NzbDrone.Core.Test.Datastore BasicRepositoryFixture : DbTest, ScheduledTask> { private ScheduledTask _basicType; - private List _basicList; + [SetUp] public void Setup() @@ -26,28 +25,15 @@ namespace NzbDrone.Core.Test.Datastore .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_insert() + public void should_be_able_to_add() { 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() { @@ -61,6 +47,7 @@ namespace NzbDrone.Core.Test.Datastore } + [Test] public void should_be_able_to_delete_model() { @@ -71,16 +58,6 @@ namespace NzbDrone.Core.Test.Datastore 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() { @@ -90,64 +67,6 @@ namespace NzbDrone.Core.Test.Datastore 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() { @@ -167,6 +86,7 @@ namespace NzbDrone.Core.Test.Datastore Assert.Throws(() => Subject.Get(12)); } + [Test] public void get_all_with_empty_db_should_return_empty_list() { @@ -178,6 +98,7 @@ namespace NzbDrone.Core.Test.Datastore 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 new file mode 100644 index 000000000..649a7303e --- /dev/null +++ b/src/NzbDrone.Core.Test/Datastore/Converters/BooleanIntConverterFixture.cs @@ -0,0 +1,59 @@ +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 aa3c4c114..17ce66cfb 100644 --- a/src/NzbDrone.Core.Test/Datastore/Converters/CommandConverterFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/Converters/CommandConverterFixture.cs @@ -1,51 +1,64 @@ +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.SetValue(param, command); - param.Value.Should().BeOfType(); + Subject.ToDB(command).Should().BeOfType(); } [Test] public void should_return_null_for_null_value_when_saving_to_db() { - Subject.SetValue(param, null); - param.Value.Should().BeNull(); + 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_command_when_getting_json_from_db() { - var data = "{\"name\": \"RefreshMovie\"}"; + var dataRecordMock = new Mock(); + dataRecordMock.Setup(s => s.GetOrdinal("Name")).Returns(0); + dataRecordMock.Setup(s => s.GetString(0)).Returns("RefreshMovie"); - Subject.Parse(data).Should().BeOfType(); + var context = new ConverterContext + { + DataRecord = dataRecordMock.Object, + DbValue = new RefreshMovieCommand().ToJson() + }; + + Subject.FromDB(context).Should().BeOfType(); } [Test] public void should_return_null_for_null_value_when_getting_from_db() { - Subject.Parse(null).Should().BeNull(); + var context = new ConverterContext + { + DbValue = DBNull.Value + }; + + Subject.FromDB(context).Should().Be(null); } } } diff --git a/src/NzbDrone.Core.Test/Datastore/Converters/DictionaryConverterFixture.cs b/src/NzbDrone.Core.Test/Datastore/Converters/DictionaryConverterFixture.cs deleted file mode 100644 index f67834116..000000000 --- a/src/NzbDrone.Core.Test/Datastore/Converters/DictionaryConverterFixture.cs +++ /dev/null @@ -1,41 +0,0 @@ -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 new file mode 100644 index 000000000..bf4974124 --- /dev/null +++ b/src/NzbDrone.Core.Test/Datastore/Converters/DoubleConverterFixture.cs @@ -0,0 +1,70 @@ +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 new file mode 100644 index 000000000..a54296855 --- /dev/null +++ b/src/NzbDrone.Core.Test/Datastore/Converters/EnumIntConverterFixture.cs @@ -0,0 +1,58 @@ +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 d8448ca0a..8444fa053 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,21 +10,18 @@ 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.SetValue(param, guid); - param.Value.Should().Be(guid.ToString()); + 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); } [Test] @@ -32,13 +29,23 @@ namespace NzbDrone.Core.Test.Datastore.Converters { var guid = Guid.NewGuid(); - Subject.Parse(guid.ToString()).Should().Be(guid); + var context = new ConverterContext + { + DbValue = guid.ToString() + }; + + Subject.FromDB(context).Should().Be(guid); } [Test] public void should_return_empty_guid_for_db_null_value_when_getting_from_db() { - Subject.Parse(null).Should().Be(Guid.Empty); + var context = new ConverterContext + { + DbValue = DBNull.Value + }; + + Subject.FromDB(context).Should().Be(Guid.Empty); } } } diff --git a/src/NzbDrone.Core.Test/Datastore/Converters/Int32ConverterFixture.cs b/src/NzbDrone.Core.Test/Datastore/Converters/Int32ConverterFixture.cs new file mode 100644 index 000000000..0be50e92a --- /dev/null +++ b/src/NzbDrone.Core.Test/Datastore/Converters/Int32ConverterFixture.cs @@ -0,0 +1,58 @@ +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 d953fac4b..f7f3da0d8 100644 --- a/src/NzbDrone.Core.Test/Datastore/Converters/OsPathConverterFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/Converters/OsPathConverterFixture.cs @@ -1,5 +1,6 @@ -using System.Data.SQLite; +using System; using FluentAssertions; +using Marr.Data.Converters; using NUnit.Framework; using NzbDrone.Common.Disk; using NzbDrone.Core.Datastore.Converters; @@ -11,22 +12,13 @@ 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.SetValue(param, osPath); - param.Value.Should().Be(path); + Subject.ToDB(osPath).Should().Be(path); } [Test] @@ -35,13 +27,23 @@ namespace NzbDrone.Core.Test.Datastore.Converters var path = @"C:\Test\TV".AsOsAgnostic(); var osPath = new OsPath(path); - Subject.Parse(path).Should().Be(osPath); + var context = new ConverterContext + { + DbValue = path + }; + + Subject.FromDB(context).Should().Be(osPath); } [Test] - public void should_return_empty_for_null_value_when_getting_from_db() + public void should_return_db_null_for_db_null_value_when_getting_from_db() { - Subject.Parse(null).IsEmpty.Should().BeTrue(); + var context = new ConverterContext + { + DbValue = DBNull.Value + }; + + Subject.FromDB(context).Should().Be(DBNull.Value); } } } diff --git a/src/NzbDrone.Core.Test/Datastore/Converters/ProviderSettingConverterFixture.cs b/src/NzbDrone.Core.Test/Datastore/Converters/ProviderSettingConverterFixture.cs index 79d4f0ad2..9aafbee73 100644 --- a/src/NzbDrone.Core.Test/Datastore/Converters/ProviderSettingConverterFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/Converters/ProviderSettingConverterFixture.cs @@ -1,5 +1,6 @@ -using System.Data.SQLite; +using System; using FluentAssertions; +using Marr.Data.Converters; using NUnit.Framework; using NzbDrone.Core.Datastore.Converters; using NzbDrone.Core.Test.Framework; @@ -7,29 +8,32 @@ using NzbDrone.Core.ThingiProvider; 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() { - Subject.Parse(null).Should().Be(NullConfig.Instance); + var result = Subject.FromDB(new ConverterContext() + { + DbValue = DBNull.Value + }); + + + result.Should().Be(NullConfig.Instance); } [TestCase(null)] [TestCase("")] public void should_return_null_config_if_config_is_empty(object dbValue) { - Subject.Parse(dbValue).Should().Be(NullConfig.Instance); + var result = Subject.FromDB(new ConverterContext() + { + DbValue = dbValue + }); + + + result.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 f6759ce62..31ae8b6b3 100644 --- a/src/NzbDrone.Core.Test/Datastore/Converters/QualityIntConverterFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/Converters/QualityIntConverterFixture.cs @@ -1,5 +1,6 @@ -using System.Data.SQLite; +using System; using FluentAssertions; +using Marr.Data.Converters; using NUnit.Framework; using NzbDrone.Core.Datastore.Converters; using NzbDrone.Core.Qualities; @@ -8,30 +9,26 @@ using NzbDrone.Core.Test.Framework; 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.SetValue(param, quality); - param.Value.Should().Be(quality.Id); + Subject.ToDB(quality).Should().Be(quality.Id); } [Test] public void should_return_0_when_saving_db_null_to_db() { - Subject.SetValue(param, null); - param.Value.Should().Be(0); + 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")); } [Test] @@ -39,13 +36,23 @@ namespace NzbDrone.Core.Test.Datastore.Converters { var quality = Quality.Bluray1080p; - Subject.Parse(quality.Id).Should().Be(quality); + var context = new ConverterContext + { + DbValue = quality.Id + }; + + Subject.FromDB(context).Should().Be(quality); } [Test] - public void should_return_unknown_for_null_value_when_getting_from_db() + public void should_return_db_null_for_db_null_value_when_getting_from_db() { - Subject.Parse(null).Should().Be(Quality.Unknown); + var context = new ConverterContext + { + DbValue = DBNull.Value + }; + + Subject.FromDB(context).Should().Be(Quality.Unknown); } } } diff --git a/src/NzbDrone.Core.Test/Datastore/Converters/TimeSpanConverterFixture.cs b/src/NzbDrone.Core.Test/Datastore/Converters/TimeSpanConverterFixture.cs new file mode 100644 index 000000000..c96848179 --- /dev/null +++ b/src/NzbDrone.Core.Test/Datastore/Converters/TimeSpanConverterFixture.cs @@ -0,0 +1,65 @@ +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 da6c1a9e6..904f653d3 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,23 +8,20 @@ using NzbDrone.Core.Test.Framework; 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.SetValue(param, dateTime); - param.Value.Should().Be(dateTime.ToUniversalTime()); + 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); } [Test] @@ -32,7 +29,23 @@ namespace NzbDrone.Core.Test.Datastore.Converters { var dateTime = DateTime.Now.ToUniversalTime(); - Subject.Parse(dateTime).Should().Be(dateTime); + 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); } } } diff --git a/src/NzbDrone.Core.Test/Datastore/DatabaseFixture.cs b/src/NzbDrone.Core.Test/Datastore/DatabaseFixture.cs index 47f79ab86..0196db290 100644 --- a/src/NzbDrone.Core.Test/Datastore/DatabaseFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/DatabaseFixture.cs @@ -1,11 +1,10 @@ using System; using System.Linq; -using Dapper; using FluentAssertions; using NUnit.Framework; using NzbDrone.Core.Datastore; -using NzbDrone.Core.Movies; using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Movies; namespace NzbDrone.Core.Test.Datastore { @@ -15,15 +14,15 @@ namespace NzbDrone.Core.Test.Datastore public void SingleOrDefault_should_return_null_on_empty_db() { Mocker.Resolve() - .OpenConnection().Query("SELECT * FROM Movies") - .SingleOrDefault() + .GetDataMapper().Query() + .SingleOrDefault(c => c.CleanTitle == "SomeTitle") .Should() .BeNull(); } [Test] - public void vacuum() + public void vaccume() { Mocker.Resolve().Vacuum(); } diff --git a/src/NzbDrone.Core.Test/Datastore/DatabaseRelationshipFixture.cs b/src/NzbDrone.Core.Test/Datastore/DatabaseRelationshipFixture.cs index 8a4380f48..27810c21d 100644 --- a/src/NzbDrone.Core.Test/Datastore/DatabaseRelationshipFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/DatabaseRelationshipFixture.cs @@ -2,8 +2,10 @@ using System.Linq; 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; @@ -18,6 +20,48 @@ namespace NzbDrone.Core.Test.Datastore // 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/TableMapperFixture.cs b/src/NzbDrone.Core.Test/Datastore/MappingExtentionFixture.cs similarity index 69% rename from src/NzbDrone.Core.Test/Datastore/TableMapperFixture.cs rename to src/NzbDrone.Core.Test/Datastore/MappingExtentionFixture.cs index e71c800a7..a8fc9670f 100644 --- a/src/NzbDrone.Core.Test/Datastore/TableMapperFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/MappingExtentionFixture.cs @@ -1,15 +1,16 @@ 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 TableMapperFixture + public class MappingExtensionFixture { public class EmbeddedType : IEmbeddedDocument @@ -38,16 +39,19 @@ namespace NzbDrone.Core.Test.Datastore [SetUp] public void Setup() { - SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter>()); - SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter()); + MapRepository.Instance.RegisterTypeConverter(typeof(List), new EmbeddedDocumentConverter()); + MapRepository.Instance.RegisterTypeConverter(typeof(EmbeddedType), new EmbeddedDocumentConverter()); + MapRepository.Instance.RegisterTypeConverter(typeof(int), new Int32Converter()); + } + [Test] public void test_mappable_types() { var properties = typeof(TypeWithAllMappableProperties).GetProperties(); properties.Should().NotBeEmpty(); - properties.Should().OnlyContain(c => ColumnMapper.IsMappableProperty(c)); + properties.Should().OnlyContain(c => MappingExtensions.IsMappableProperty(c)); } [Test] @@ -55,7 +59,7 @@ namespace NzbDrone.Core.Test.Datastore { var properties = typeof(TypeWithNoMappableProperties).GetProperties(); properties.Should().NotBeEmpty(); - properties.Should().NotContain(c => ColumnMapper.IsMappableProperty(c)); + properties.Should().NotContain(c => MappingExtensions.IsMappableProperty(c)); } } } diff --git a/src/NzbDrone.Core.Test/Datastore/MarrDataLazyLoadingFixture.cs b/src/NzbDrone.Core.Test/Datastore/MarrDataLazyLoadingFixture.cs new file mode 100644 index 000000000..2200b0d44 --- /dev/null +++ b/src/NzbDrone.Core.Test/Datastore/MarrDataLazyLoadingFixture.cs @@ -0,0 +1,55 @@ +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 new file mode 100644 index 000000000..ad64ff036 --- /dev/null +++ b/src/NzbDrone.Core.Test/Datastore/PagingSpecExtensionsTests/PagingOffsetFixture.cs @@ -0,0 +1,29 @@ +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 new file mode 100644 index 000000000..0067adb1f --- /dev/null +++ b/src/NzbDrone.Core.Test/Datastore/PagingSpecExtensionsTests/ToSortDirectionFixture.cs @@ -0,0 +1,53 @@ +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 new file mode 100644 index 000000000..48a1a3639 --- /dev/null +++ b/src/NzbDrone.Core.Test/Datastore/ReflectionStrategyFixture/Benchmarks.cs @@ -0,0 +1,40 @@ +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/WhereBuilderFixture.cs b/src/NzbDrone.Core.Test/Datastore/WhereBuilderFixture.cs deleted file mode 100644 index 01772c9e1..000000000 --- a/src/NzbDrone.Core.Test/Datastore/WhereBuilderFixture.cs +++ /dev/null @@ -1,125 +0,0 @@ -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 e6eec3a34..970cd52da 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/CustomFormatAllowedByProfileSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/CustomFormatAllowedByProfileSpecificationFixture.cs @@ -1,6 +1,7 @@ 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; @@ -32,7 +33,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests var fakeSeries = Builder.CreateNew() - .With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p.Id }) + .With(c => c.Profile = (LazyLoaded)new Profile { Cutoff = Quality.Bluray1080p.Id }) .Build(); remoteMovie = new RemoteMovie @@ -48,7 +49,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests public void should_allow_if_format_is_defined_in_profile() { remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List {_format1}; - remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); + remoteMovie.Movie.Profile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeTrue(); } @@ -57,7 +58,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests public void should_deny_if_format_is_defined_in_profile() { remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List {_format2}; - remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); + remoteMovie.Movie.Profile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeFalse(); } @@ -66,7 +67,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests public void should_deny_if_one_format_is_defined_in_profile() { remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List {_format2, _format1}; - remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); + remoteMovie.Movie.Profile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeFalse(); } @@ -75,7 +76,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests public void should_allow_if_all_format_is_defined_in_profile() { remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List {_format2, _format1}; - remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name); + remoteMovie.Movie.Profile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeTrue(); } @@ -84,7 +85,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests public void should_deny_if_no_format_was_parsed_and_none_not_in_profile() { remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List {}; - remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name); + remoteMovie.Movie.Profile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeFalse(); } @@ -93,7 +94,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests public void should_allow_if_no_format_was_parsed_and_none_in_profile() { remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List {}; - remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(CustomFormats.CustomFormat.None.Name, _format1.Name, _format2.Name); + remoteMovie.Movie.Profile.Value.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 7418b8d5d..9057b005e 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/LanguageSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/LanguageSpecificationFixture.cs @@ -1,7 +1,9 @@ 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; @@ -27,10 +29,10 @@ namespace NzbDrone.Core.Test.DecisionEngineTests }, Movie = new Movie { - Profile = new Profile - { - Language = Language.English - } + Profile = new LazyLoaded(new Profile + { + Language = Language.English + }) } }; } @@ -64,10 +66,10 @@ namespace NzbDrone.Core.Test.DecisionEngineTests [Test] public void should_return_true_if_allowed_language_any() { - _remoteMovie.Movie.Profile = new Profile + _remoteMovie.Movie.Profile = new LazyLoaded(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 1b36f6b22..980e7363f 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/QualityAllowedByProfileSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/QualityAllowedByProfileSpecificationFixture.cs @@ -1,5 +1,6 @@ using FizzWare.NBuilder; using FluentAssertions; +using Marr.Data; using NUnit.Framework; using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.Parser.Model; @@ -34,7 +35,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests public void Setup() { var fakeSeries = Builder.CreateNew() - .With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p.Id }) + .With(c => c.Profile = (LazyLoaded)new Profile { Cutoff = Quality.Bluray1080p.Id }) .Build(); remoteMovie = new RemoteMovie @@ -48,7 +49,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests public void should_allow_if_quality_is_defined_in_profile(Quality qualityType) { remoteMovie.ParsedMovieInfo.Quality.Quality = qualityType; - remoteMovie.Movie.Profile.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p); + remoteMovie.Movie.Profile.Value.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeTrue(); } @@ -57,7 +58,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests public void should_not_allow_if_quality_is_not_defined_in_profile(Quality qualityType) { remoteMovie.ParsedMovieInfo.Quality.Quality = qualityType; - remoteMovie.Movie.Profile.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p); + remoteMovie.Movie.Profile.Value.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 b4e3feb6c..45b5af5c6 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/QueueSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/QueueSpecificationFixture.cs @@ -83,7 +83,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests [Test] public void should_return_true_when_quality_in_queue_is_lower() { - _movie.Profile.Cutoff = Quality.Bluray1080p.Id; + _movie.Profile.Value.Cutoff = Quality.Bluray1080p.Id; var remoteMovie = Builder.CreateNew() .With(r => r.Movie = _movie) @@ -115,7 +115,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests [Test] public void should_return_false_when_quality_in_queue_is_better() { - _movie.Profile.Cutoff = Quality.Bluray1080p.Id; + _movie.Profile.Value.Cutoff = Quality.Bluray1080p.Id; var remoteMovie = Builder.CreateNew() .With(r => r.Movie = _movie) @@ -132,7 +132,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests [Test] public void should_return_false_if_quality_in_queue_meets_cutoff() { - _movie.Profile.Cutoff = _remoteMovie.ParsedMovieInfo.Quality.Quality.Id; + _movie.Profile.Value.Cutoff = _remoteMovie.ParsedMovieInfo.Quality.Quality.Id; var remoteMovie = Builder.CreateNew() .With(r => r.Movie = _movie) @@ -151,8 +151,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests [Test] public void should_return_false_when_quality_is_better_and_upgrade_allowed_is_false_for_quality_profile() { - _movie.Profile.Cutoff = Quality.Bluray1080p.Id; - _movie.Profile.UpgradeAllowed = false; + _movie.Profile.Value.Cutoff = Quality.Bluray1080p.Id; + _movie.Profile.Value.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 8ec870095..83f009722 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs @@ -1,7 +1,9 @@ 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; @@ -76,7 +78,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync // Quality = quality // }); - _remoteEpisode.Movie.MovieFile = new MovieFile { Quality = quality }; + _remoteEpisode.Movie.MovieFile = new LazyLoaded(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 cec2506b4..154227db6 100644 --- a/src/NzbDrone.Core.Test/DiskSpace/DiskSpaceServiceFixture.cs +++ b/src/NzbDrone.Core.Test/DiskSpace/DiskSpaceServiceFixture.cs @@ -50,8 +50,8 @@ namespace NzbDrone.Core.Test.DiskSpace private void GivenMovies(params Movie[] movies) { Mocker.GetMock() - .Setup(v => v.AllMoviePaths()) - .Returns(movies.Select(x => x.Path).ToList()); + .Setup(v => v.GetAllMovies()) + .Returns(movies.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 964a19c9e..39d526e2c 100644 --- a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/AddFixture.cs +++ b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/AddFixture.cs @@ -1,11 +1,13 @@ 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; @@ -44,7 +46,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests }, }; - _movie.Profile = _profile; + _movie.Profile = new LazyLoaded(_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 fe269bdfe..670865e40 100644 --- a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveGrabbedFixture.cs +++ b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveGrabbedFixture.cs @@ -1,17 +1,19 @@ 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 { @@ -44,7 +46,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests }, }; - _movie.Profile = _profile; + _movie.Profile = new LazyLoaded(_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 5d287ed0e..3c0cc0ef9 100644 --- a/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveRejectedFixture.cs +++ b/src/NzbDrone.Core.Test/Download/Pending/PendingReleaseServiceTests/RemoveRejectedFixture.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using FizzWare.NBuilder; +using Marr.Data; using Moq; using NUnit.Framework; using NzbDrone.Common.Extensions; @@ -46,7 +47,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests }, }; - _movie.Profile = _profile; + _movie.Profile = new LazyLoaded(_profile); _release = Builder.CreateNew().Build(); diff --git a/src/NzbDrone.Core.Test/Framework/DbTest.cs b/src/NzbDrone.Core.Test/Framework/DbTest.cs index cbb40525d..1d7ddd6be 100644 --- a/src/NzbDrone.Core.Test/Framework/DbTest.cs +++ b/src/NzbDrone.Core.Test/Framework/DbTest.cs @@ -1,7 +1,10 @@ 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; @@ -108,7 +111,7 @@ namespace NzbDrone.Core.Test.Framework Mocker.SetConstant(Mocker.Resolve()); Mocker.SetConstant(Mocker.Resolve()); - SqlBuilderExtensions.LogSql = true; + MapRepository.Instance.EnableTraceLogging = true; } [SetUp] diff --git a/src/NzbDrone.Core.Test/Framework/DirectDataMapper.cs b/src/NzbDrone.Core.Test/Framework/DirectDataMapper.cs index 2e7a27271..27b4354c1 100644 --- a/src/NzbDrone.Core.Test/Framework/DirectDataMapper.cs +++ b/src/NzbDrone.Core.Test/Framework/DirectDataMapper.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Data.Common; using System.Linq; using NzbDrone.Common.Serializer; using NzbDrone.Core.Datastore; @@ -15,16 +16,27 @@ namespace NzbDrone.Core.Test.Framework public class DirectDataMapper : IDirectDataMapper { - private readonly IDatabase _database; + private readonly DbProviderFactory _providerFactory; + private readonly string _connectionString; public DirectDataMapper(IDatabase database) { - _database = database; + var dataMapper = database.GetDataMapper(); + _providerFactory = dataMapper.ProviderFactory; + _connectionString = dataMapper.ConnectionString; } + private DbConnection OpenConnection() + { + var connection = _providerFactory.CreateConnection(); + connection.ConnectionString = _connectionString; + connection.Open(); + return connection; + } + public DataTable GetDataTable(string sql) { - using (var connection = _database.OpenConnection()) + using (var connection = 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 7ad2d56c3..bf1a06cf4 100644 --- a/src/NzbDrone.Core.Test/HealthCheck/Checks/RootFolderCheckFixture.cs +++ b/src/NzbDrone.Core.Test/HealthCheck/Checks/RootFolderCheckFixture.cs @@ -20,8 +20,8 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks .ToList(); Mocker.GetMock() - .Setup(s => s.AllMoviePaths()) - .Returns(movies.Select(x => x.Path).ToList()); + .Setup(s => s.GetAllMovies()) + .Returns(movies); Mocker.GetMock() .Setup(s => s.GetParentFolder(movies.First().Path)) @@ -36,8 +36,8 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks public void should_not_return_error_when_no_movie() { Mocker.GetMock() - .Setup(s => s.AllMoviePaths()) - .Returns(new List()); + .Setup(s => s.GetAllMovies()) + .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 df20e02ef..065b9d375 100644 --- a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupAdditionalUsersFixture.cs +++ b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupAdditionalUsersFixture.cs @@ -1,5 +1,4 @@ -using System; -using FizzWare.NBuilder; +using FizzWare.NBuilder; using FluentAssertions; using NUnit.Framework; using NzbDrone.Core.Authentication; @@ -15,11 +14,7 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers public void should_delete_additional_users() { var specs = Builder.CreateListOfSize(5) - .All() - .With(x => x.Id = 0) - .BuildListOfNew(); - - specs.ForEach(x => x.Identifier = Guid.NewGuid()); + .BuildListOfNew(); Db.InsertMany(specs); @@ -31,9 +26,7 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers public void should_not_delete_if_only_one_user() { var spec = Builder.CreateNew() - .With(x => x.Id = 0) - .With(x => x.Identifier = Guid.NewGuid()) - .BuildNew(); + .BuildNew(); Db.Insert(spec); @@ -41,4 +34,4 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers 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 99515c1c6..fa7401ef5 100644 --- a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupUnusedTagsFixture.cs +++ b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupUnusedTagsFixture.cs @@ -14,10 +14,7 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers [Test] public void should_delete_unused_tags() { - var tags = Builder.CreateListOfSize(2) - .All() - .With(x => x.Id = 0) - .BuildList(); + var tags = Builder.CreateListOfSize(2).BuildList(); Db.InsertMany(tags); Subject.Clean(); @@ -27,15 +24,11 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers [Test] public void should_not_delete_used_tags() { - var tags = Builder.CreateListOfSize(2) - .All() - .With(x => x.Id = 0) - .BuildList(); + var tags = Builder.CreateListOfSize(2).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 2863ae5e8..6608bd42f 100644 --- a/src/NzbDrone.Core.Test/InstrumentationTests/DatabaseTargetFixture.cs +++ b/src/NzbDrone.Core.Test/InstrumentationTests/DatabaseTargetFixture.cs @@ -1,6 +1,7 @@ using System; using System.Threading; using FluentAssertions; +using Marr.Data; using NLog; using NUnit.Framework; using NzbDrone.Common.Instrumentation; @@ -9,6 +10,7 @@ using NzbDrone.Core.Instrumentation; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Test.Framework; using NzbDrone.Test.Common; +using NzbDrone.Test.Common.Categories; namespace NzbDrone.Core.Test.InstrumentationTests { @@ -62,6 +64,23 @@ namespace NzbDrone.Core.Test.InstrumentationTests 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 c29e4ae33..344cafaad 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Specifications/SameFileSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Specifications/SameFileSpecificationFixture.cs @@ -1,5 +1,7 @@ +using System.Linq; using FizzWare.NBuilder; using FluentAssertions; +using Marr.Data; using NUnit.Framework; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles.MovieImport.Specifications; @@ -37,11 +39,11 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Specifications { _localMovie.Movie = Builder.CreateNew() .With(e => e.MovieFileId = 1) - .With(e => e.MovieFile = + .With(e => e.MovieFile = new LazyLoaded( new MovieFile { Size = _localMovie.Size + 100.Megabytes() - }) + })) .Build(); Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeTrue(); @@ -52,11 +54,11 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Specifications { _localMovie.Movie = Builder.CreateNew() .With(e => e.MovieFileId = 1) - .With(e => e.MovieFile = + .With(e => e.MovieFile = new LazyLoaded( new MovieFile { Size = _localMovie.Size - }) + })) .Build(); Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeFalse(); @@ -67,7 +69,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Specifications { _localMovie.Movie = Builder.CreateNew() .With(e => e.MovieFileId = 1) - .With(e => e.MovieFile = null) + .With(e => e.MovieFile = new LazyLoaded((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 664e245b5..5dca61469 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Specifications/UpgradeSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/MovieImport/Specifications/UpgradeSpecificationFixture.cs @@ -1,5 +1,7 @@ +using System.Linq; using FizzWare.NBuilder; using FluentAssertions; +using Marr.Data; using NUnit.Framework; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles.MovieImport.Specifications; @@ -46,11 +48,13 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Specifications { _localMovie.Movie.MovieFileId = 1; - _localMovie.Movie.MovieFile = + _localMovie.Movie.MovieFile = new LazyLoaded( new MovieFile { Quality = new QualityModel(Quality.SDTV, new Revision(version: 1)) - }; + } + ); + Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeTrue(); } @@ -60,11 +64,12 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Specifications public void should_return_false_if_not_an_upgrade_for_existing_episodeFile() { _localMovie.Movie.MovieFileId = 1; - _localMovie.Movie.MovieFile = + _localMovie.Movie.MovieFile = new LazyLoaded( 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 ce7e9fc34..e86dc3086 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/UpgradeMediaFileServiceFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/UpgradeMediaFileServiceFixture.cs @@ -1,5 +1,7 @@ +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 dd7fb9a40..b93735053 100644 --- a/src/NzbDrone.Core.Test/MovieTests/MovieRepositoryTests/MovieRepositoryFixture.cs +++ b/src/NzbDrone.Core.Test/MovieTests/MovieRepositoryTests/MovieRepositoryFixture.cs @@ -5,8 +5,6 @@ using NzbDrone.Core.Profiles; using NzbDrone.Core.Qualities; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Movies; -using System; -using System.Linq; namespace NzbDrone.Core.Test.MovieTests.MovieRepositoryTests { @@ -14,17 +12,13 @@ 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_load_quality_profile() + public void should_lazyload_quality_profile() { var profile = new Profile { @@ -35,14 +29,16 @@ namespace NzbDrone.Core.Test.MovieTests.MovieRepositoryTests Name = "TestProfile" }; - _profileRepository.Insert(profile); + + Mocker.Resolve().Insert(profile); var movie = Builder.CreateNew().BuildNew(); movie.ProfileId = profile.Id; Subject.Insert(movie); - Subject.All().Single().Profile.Should().NotBeNull(); + + StoredModel.Profile.Should().NotBeNull(); } } } diff --git a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs index 2d2cdc5e1..d65bb83a8 100644 --- a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs @@ -1,9 +1,11 @@ +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; @@ -34,7 +36,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests .With(m => m.Title = "Fack Ju Göthe 2") .With(m => m.CleanTitle = "fackjugoethe2") .With(m => m.Year = 2015) - .With(m => m.AlternativeTitles = new List {new AlternativeTitle("Fack Ju Göthe 2: Same same")}) + .With(m => m.AlternativeTitles = new LazyList( 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 50e0e2b8e..9dd7c95a7 100644 --- a/src/NzbDrone.Core.Test/Radarr.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/Radarr.Core.Test.csproj @@ -7,7 +7,6 @@ - diff --git a/src/NzbDrone.Core/Authentication/UserRepository.cs b/src/NzbDrone.Core/Authentication/UserRepository.cs index b439149b3..fd5873190 100644 --- a/src/NzbDrone.Core/Authentication/UserRepository.cs +++ b/src/NzbDrone.Core/Authentication/UserRepository.cs @@ -20,12 +20,12 @@ namespace NzbDrone.Core.Authentication public User FindUser(string username) { - return Query(x => x.Username == username).SingleOrDefault(); + return Query.Where(u => u.Username == username).SingleOrDefault(); } public User FindUser(Guid identifier) { - return Query(x => x.Identifier == identifier).SingleOrDefault(); + return Query.Where(u => u.Identifier == identifier).SingleOrDefault(); } } } diff --git a/src/NzbDrone.Core/Backup/MakeDatabaseBackup.cs b/src/NzbDrone.Core/Backup/MakeDatabaseBackup.cs index 44d9186e6..2b82e4bc3 100644 --- a/src/NzbDrone.Core/Backup/MakeDatabaseBackup.cs +++ b/src/NzbDrone.Core/Backup/MakeDatabaseBackup.cs @@ -27,7 +27,7 @@ namespace NzbDrone.Core.Backup public void BackupDatabase(IDatabase database, string targetDirectory) { var sourceConnectionString = ""; - using (var db = database.OpenConnection()) + using (var db = database.GetDataMapper()) { sourceConnectionString = db.ConnectionString; } diff --git a/src/NzbDrone.Core/Blacklisting/BlacklistRepository.cs b/src/NzbDrone.Core/Blacklisting/BlacklistRepository.cs index b0b175264..fc398d89b 100644 --- a/src/NzbDrone.Core/Blacklisting/BlacklistRepository.cs +++ b/src/NzbDrone.Core/Blacklisting/BlacklistRepository.cs @@ -1,8 +1,7 @@ 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 @@ -23,35 +22,26 @@ namespace NzbDrone.Core.Blacklisting public List BlacklistedByTitle(int movieId, string sourceTitle) { - return Query(x => x.MovieId == movieId && x.SourceTitle.Contains(sourceTitle)); + return Query.Where(e => e.MovieId == movieId) + .AndWhere(e => e.SourceTitle.Contains(sourceTitle)).ToList(); } public List BlacklistedByTorrentInfoHash(int movieId, string torrentInfoHash) { - return Query(x => x.MovieId == movieId && x.TorrentInfoHash.Contains(torrentInfoHash)); + return Query.Where(e => e.MovieId == movieId) + .AndWhere(e => e.TorrentInfoHash.Contains(torrentInfoHash)).ToList(); } public List BlacklistedByMovie(int movieId) { - return Query(x => x.MovieId == movieId); + return Query.Where(b => b.MovieId == movieId).ToList(); } - private IEnumerable SelectJoined(SqlBuilder.Template sql) + protected override SortBuilder GetPagedQuery(QueryBuilder query, PagingSpec pagingSpec) { - using (var conn = _database.OpenConnection()) - { - return conn.Query( - sql.RawSql, - (bl, movie) => { - bl.Movie = movie; - return bl; - }, - sql.Parameters) - .ToList(); - } - } + var baseQuery = query.Join(JoinType.Inner, h => h.Movie, (h, s) => h.MovieId == s.Id); - protected override SqlBuilder PagedBuilder() => new SqlBuilder().Join("Movies ON Movies.Id = Blacklist.MovieId"); - protected override IEnumerable PagedSelector(SqlBuilder.Template sql) => SelectJoined(sql); + return base.GetPagedQuery(baseQuery, pagingSpec); + } } } diff --git a/src/NzbDrone.Core/Configuration/ConfigRepository.cs b/src/NzbDrone.Core/Configuration/ConfigRepository.cs index d1beb585d..23d3cbd7b 100644 --- a/src/NzbDrone.Core/Configuration/ConfigRepository.cs +++ b/src/NzbDrone.Core/Configuration/ConfigRepository.cs @@ -18,9 +18,10 @@ namespace NzbDrone.Core.Configuration { } + public Config Get(string key) { - return Query(c => c.Key == key).SingleOrDefault(); + return Query.Where(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 79aaa0c6f..3aa08fda7 100644 --- a/src/NzbDrone.Core/CustomFormats/CustomFormat.cs +++ b/src/NzbDrone.Core/CustomFormats/CustomFormat.cs @@ -7,10 +7,6 @@ namespace NzbDrone.Core.CustomFormats { public class CustomFormat : ModelBase, IEquatable { - public string Name { get; set; } - - public List FormatTags { get; set; } - public CustomFormat() { @@ -22,7 +18,9 @@ namespace NzbDrone.Core.CustomFormats FormatTags = tags.Select(t => new FormatTag(t)).ToList(); } - public static implicit operator CustomFormatDefinition(CustomFormat format) => new CustomFormatDefinition { Id = format.Id, Name = format.Name, FormatTags = format.FormatTags }; + public string Name { get; set; } + + public List FormatTags { get; set; } public override string ToString() { diff --git a/src/NzbDrone.Core/CustomFormats/CustomFormatDefinition.cs b/src/NzbDrone.Core/CustomFormats/CustomFormatDefinition.cs deleted file mode 100644 index 029699c68..000000000 --- a/src/NzbDrone.Core/CustomFormats/CustomFormatDefinition.cs +++ /dev/null @@ -1,14 +0,0 @@ -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 843b090b1..205bdec36 100644 --- a/src/NzbDrone.Core/CustomFormats/CustomFormatRepository.cs +++ b/src/NzbDrone.Core/CustomFormats/CustomFormatRepository.cs @@ -3,12 +3,12 @@ using NzbDrone.Core.Messaging.Events; 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 f57e1da75..8b68980aa 100644 --- a/src/NzbDrone.Core/CustomFormats/CustomFormatService.cs +++ b/src/NzbDrone.Core/CustomFormats/CustomFormatService.cs @@ -7,7 +7,9 @@ using NzbDrone.Common.Composition; 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 @@ -21,7 +23,8 @@ namespace NzbDrone.Core.CustomFormats void Delete(int id); } - public class CustomFormatService : ICustomFormatService + + public class CustomFormatService : ICustomFormatService, IHandle { private readonly ICustomFormatRepository _formatRepository; private IProfileService _profileService; @@ -55,9 +58,6 @@ namespace NzbDrone.Core.CustomFormats _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 @@ namespace NzbDrone.Core.CustomFormats { return _cache.Get("all", () => { - var all = _formatRepository.All().Select(x => (CustomFormat)x).ToDictionary(m => m.Id); + var all = _formatRepository.All().ToDictionary(m => m.Id); AllCustomFormats = all; return all; }); @@ -194,5 +194,11 @@ namespace NzbDrone.Core.CustomFormats }; } } + + 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 4a0684887..344ec01da 100644 --- a/src/NzbDrone.Core/Datastore/BasicRepository.cs +++ b/src/NzbDrone.Core/Datastore/BasicRepository.cs @@ -3,10 +3,11 @@ using System.Collections.Generic; using System.Data; using System.Linq; using System.Linq.Expressions; -using System.Reflection; -using System.Text; -using Dapper; +using Marr.Data; +using Marr.Data.QGen; +using NzbDrone.Common.Extensions; using NzbDrone.Core.Datastore.Events; +using NzbDrone.Core.Datastore.Extensions; using NzbDrone.Core.Messaging.Events; namespace NzbDrone.Core.Datastore @@ -30,89 +31,43 @@ 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 readonly IDatabase _database; - protected readonly string _table; - protected string _selectTemplate; - protected string _deleteTemplate; + protected IDataMapper DataMapper => _database.GetDataMapper(); 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 virtual SqlBuilder BuilderBase() => new SqlBuilder(); - protected virtual SqlBuilder Builder() => BuilderBase().SelectAll(); + protected QueryBuilder Query => AddJoinQueries(DataMapper.Query()); - protected virtual IEnumerable GetResults(SqlBuilder.Template sql) + protected void Delete(Expression> filter) { - using (var conn = _database.OpenConnection()) - { - return conn.Query(sql.RawSql, sql.Parameters); - } + DataMapper.Delete(filter); } - protected List Query(Expression> where) + public IEnumerable All() { - 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(); + return AddJoinQueries(DataMapper.Query()).ToList(); } public int Count() { - using (var conn = _database.OpenConnection()) - { - return conn.ExecuteScalar($"SELECT COUNT(*) FROM {_table}"); - } - } - - public virtual IEnumerable All() - { - return Query(Builder()); + return DataMapper.Query().GetRowCount(); } public TModel Get(int id) { - var model = Query(x => x.Id == id).FirstOrDefault(); + var model = Query.Where(c => c.Id == id).SingleOrDefault(); if (model == null) { @@ -124,16 +79,13 @@ namespace NzbDrone.Core.Datastore public IEnumerable Get(IEnumerable ids) { - if (!ids.Any()) - { - return new List(); - } + var idList = ids.ToList(); + var query = string.Format("Id IN ({0})", string.Join(",", idList)); + var result = Query.Where(m => m.Id.In(idList)).ToList(); - var result = Query(x => ids.Contains(x.Id)); - - if (result.Count != ids.Count()) + if (result.Count != idList.Count()) { - throw new ApplicationException($"Expected query to return {ids.Count()} rows but returned {result.Count}"); + throw new ApplicationException($"Expected query to return {idList.Count} rows but returned {result.Count}"); } return result; @@ -156,70 +108,13 @@ namespace NzbDrone.Core.Datastore throw new InvalidOperationException("Can't insert model with existing ID " + model.Id); } - using (var conn = _database.OpenConnection()) - { - model = Insert(conn, null, model); - } + DataMapper.Insert(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) @@ -227,59 +122,52 @@ namespace NzbDrone.Core.Datastore throw new InvalidOperationException("Can't update model with ID 0"); } - using (var conn = _database.OpenConnection()) - { - UpdateFields(conn, null, model, _properties); - } + DataMapper.Update(model, c => c.Id == model.Id); ModelUpdated(model); return model; } - public void UpdateMany(IList models) - { - if (models.Any(x => x.Id == 0)) - { - throw new InvalidOperationException("Can't update model with ID 0"); - } - - using (var conn = _database.OpenConnection()) - { - UpdateFields(conn, null, models, _properties); - } - } - - protected void Delete(Expression> where) - { - Delete(Builder().Where(where)); - } - - protected void Delete(SqlBuilder builder) - { - var sql = builder.AddTemplate(_deleteTemplate).LogQuery(); - - using (var conn = _database.OpenConnection()) - { - conn.Execute(sql.RawSql, sql.Parameters); - } - } - public void Delete(TModel model) { - Delete(x => x.Id == model.Id); + Delete(model.Id); } - public void Delete(int id) + public void InsertMany(IList models) { - Delete(x => x.Id == id); - } - - public void DeleteMany(IEnumerable ids) - { - if (ids.Any()) + using (var unitOfWork = new UnitOfWork(() => DataMapper)) { - Delete(x => ids.Contains(x.Id)); + unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted); + + foreach (var model in models) + { + unitOfWork.DB.Insert(model); + } + + unitOfWork.Commit(); + } + } + + public void UpdateMany(IList models) + { + using (var unitOfWork = new UnitOfWork(() => DataMapper)) + { + unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted); + + foreach (var model in models) + { + var localModel = model; + + if (model.Id == 0) + { + throw new InvalidOperationException("Can't update model with ID 0"); + } + + unitOfWork.DB.Update(model, c => c.Id == localModel.Id); + } + + unitOfWork.Commit(); } } @@ -299,13 +187,31 @@ namespace NzbDrone.Core.Datastore 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) { - using (var conn = _database.OpenConnection()) - { - conn.Execute($"DELETE FROM [{_table}]"); - } - + DataMapper.Delete(c => c.Id > -1); if (vacuum) { Vacuum(); @@ -326,115 +232,43 @@ namespace NzbDrone.Core.Datastore { if (model.Id == 0) { - throw new InvalidOperationException("Attempted to update model without ID"); + throw new InvalidOperationException("Attempted to updated model without ID"); } - var propertiesToUpdate = properties.Select(x => x.GetMemberName()).ToList(); - - using (var conn = _database.OpenConnection()) - { - UpdateFields(conn, null, model, propertiesToUpdate); - } + DataMapper.Update() + .Where(c => c.Id == model.Id) + .ColumnsIncluding(properties) + .Entity(model) + .Execute(); 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 = GetPagedRecords(PagedBuilder().SelectAll(), pagingSpec, PagedSelector); - pagingSpec.TotalRecords = GetPagedRecordCount(PagedBuilder().SelectCount(), pagingSpec); + pagingSpec.Records = GetPagedQuery(Query, pagingSpec).ToList(); + pagingSpec.TotalRecords = GetPagedQuery(Query, pagingSpec).GetRowCount(); return pagingSpec; } - private void AddFilters(SqlBuilder builder, PagingSpec pagingSpec) + protected virtual SortBuilder GetPagedQuery(QueryBuilder query, PagingSpec pagingSpec) { - var filters = pagingSpec.FilterExpressions; + var filterExpressions = pagingSpec.FilterExpressions; + var sortQuery = query.Where(filterExpressions.FirstOrDefault()); - foreach (var filter in filters) + if (filterExpressions.Count > 1) { - builder.Where(filter); + // Start at the second item for the AndWhere clauses + for (var i = 1; i < filterExpressions.Count; i++) + { + sortQuery.AndWhere(filterExpressions[i]); + } } - } - 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); - } + return sortQuery.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection()) + .Skip(pagingSpec.PagingOffset()) + .Take(pagingSpec.PageSize); } protected void ModelCreated(TModel model) @@ -460,6 +294,11 @@ namespace NzbDrone.Core.Datastore } } + 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 new file mode 100644 index 000000000..397746b23 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Converters/BooleanIntConverter.cs @@ -0,0 +1,51 @@ +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 0a1f6ebca..0bb97f23a 100644 --- a/src/NzbDrone.Core/Datastore/Converters/CommandConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/CommandConverter.cs @@ -1,28 +1,30 @@ -using System.Data; -using System.Text.Json; +using System; +using Marr.Data.Converters; 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 Command Parse(object value) + public override object FromDB(ConverterContext context) { - var stringValue = (string) value; + if (context.DbValue == DBNull.Value) + { + return null; + } + + var stringValue = (string)context.DbValue; if (stringValue.IsNullOrWhiteSpace()) { return null; } - string contract; - using (JsonDocument body = JsonDocument.Parse(stringValue)) - { - contract = body.RootElement.GetProperty("name").GetString(); - } - + var ordinal = context.DataRecord.GetOrdinal("Name"); + var contract = context.DataRecord.GetString(ordinal); var impType = typeof (Command).Assembly.FindTypeByName(contract + "Command"); if (impType == null) @@ -30,12 +32,7 @@ namespace NzbDrone.Core.Datastore.Converters throw new CommandNotFoundException(contract); } - return (Command) JsonSerializer.Deserialize(stringValue, impType, SerializerSettings); - } - - public override void SetValue(IDbDataParameter parameter, Command value) - { - parameter.Value = value == null ? null : JsonSerializer.Serialize(value, SerializerSettings); + return Json.Deserialize(stringValue, impType); } } } diff --git a/src/NzbDrone.Core/Datastore/Converters/CustomFormatIntConverter.cs b/src/NzbDrone.Core/Datastore/Converters/CustomFormatIntConverter.cs index 69db3e979..3875371b9 100644 --- a/src/NzbDrone.Core/Datastore/Converters/CustomFormatIntConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/CustomFormatIntConverter.cs @@ -1,57 +1,91 @@ using System; -using System.Data; -using System.Text.Json; -using System.Text.Json.Serialization; -using Dapper; -using NzbDrone.Common.Serializer; +using System.ServiceModel; +using Marr.Data.Converters; +using Marr.Data.Mapping; +using NzbDrone.Core.Qualities; +using Newtonsoft.Json; using NzbDrone.Core.CustomFormats; namespace NzbDrone.Core.Datastore.Converters { - public class DapperCustomFormatIntConverter : SqlMapper.TypeHandler + public class CustomFormatIntConverter : JsonConverter, IConverter { - public override void SetValue(IDbDataParameter parameter, CustomFormat value) + //TODO think of something better. + public object FromDB(ConverterContext context) { - parameter.Value = value.Id; - } - - public override CustomFormat Parse(object value) - { - Console.WriteLine(value.ToJson()); - - if (value is DBNull) + if (context.DbValue == DBNull.Value) { return null; } - var val = Convert.ToInt32(value); + var val = Convert.ToInt32(context.DbValue); 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 class CustomFormatIntConverter : JsonConverter - { - public override CustomFormat Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public object FromDB(ColumnMap map, object dbValue) { - var val = reader.GetInt32(); + 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); 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 Write(Utf8JsonWriter writer, CustomFormat value, JsonSerializerOptions options) + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { - writer.WriteNumberValue(value.Id); + writer.WriteValue(ToDB(value)); } } } diff --git a/src/NzbDrone.Core/Datastore/Converters/DoubleConverter.cs b/src/NzbDrone.Core/Datastore/Converters/DoubleConverter.cs new file mode 100644 index 000000000..82f50e326 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Converters/DoubleConverter.cs @@ -0,0 +1,46 @@ +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 92a634cdd..d2b9146f2 100644 --- a/src/NzbDrone.Core/Datastore/Converters/EmbeddedDocumentConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/EmbeddedDocumentConverter.cs @@ -1,49 +1,65 @@ -using System.Data; -using System.Text.Json; -using System.Text.Json.Serialization; -using Dapper; +using System; +using Marr.Data.Converters; +using Marr.Data.Mapping; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Converters; namespace NzbDrone.Core.Datastore.Converters { - public class EmbeddedDocumentConverter : SqlMapper.TypeHandler + public class EmbeddedDocumentConverter : IConverter { - protected readonly JsonSerializerOptions SerializerSettings; + private readonly JsonSerializerSettings SerializerSetting; - public EmbeddedDocumentConverter() + public EmbeddedDocumentConverter(params JsonConverter[] converters) { - var serializerSettings = new JsonSerializerOptions + SerializerSetting = new JsonSerializerSettings { - AllowTrailingCommas = true, - IgnoreNullValues = false, - PropertyNameCaseInsensitive = true, - DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - WriteIndented = true + DateTimeZoneHandling = DateTimeZoneHandling.Utc, + NullValueHandling = NullValueHandling.Ignore, + Formatting = Formatting.Indented, + DefaultValueHandling = DefaultValueHandling.Include, + ContractResolver = new CamelCasePropertyNamesContractResolver() }; - serializerSettings.Converters.Add(new NoFlagsStringEnumConverter()); - serializerSettings.Converters.Add(new TimeSpanConverter()); - serializerSettings.Converters.Add(new UtcConverter()); + SerializerSetting.Converters.Add(new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy() }); + SerializerSetting.Converters.Add(new VersionConverter()); - SerializerSettings = serializerSettings; - } - - public EmbeddedDocumentConverter(params JsonConverter[] converters) : this() - { foreach (var converter in converters) { - SerializerSettings.Converters.Add(converter); + SerializerSetting.Converters.Add(converter); } } - public override void SetValue(IDbDataParameter parameter, T value) + public virtual object FromDB(ConverterContext context) { - parameter.Value = JsonSerializer.Serialize(value, SerializerSettings); + 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); } - public override T Parse(object value) + public object FromDB(ColumnMap map, object dbValue) { - return JsonSerializer.Deserialize((string) value, SerializerSettings); + return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); } + + 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 new file mode 100644 index 000000000..22d6b5336 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Converters/EnumIntConverter.cs @@ -0,0 +1,36 @@ +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 256ab6502..1ad387513 100644 --- a/src/NzbDrone.Core/Datastore/Converters/GuidConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/GuidConverter.cs @@ -1,24 +1,40 @@ using System; -using System.Data; -using Dapper; +using Marr.Data.Converters; +using Marr.Data.Mapping; namespace NzbDrone.Core.Datastore.Converters { - public class GuidConverter : SqlMapper.TypeHandler + public class GuidConverter : IConverter { - public override Guid Parse(object value) + public object FromDB(ConverterContext context) { - if (value == null) + if (context.DbValue == DBNull.Value) { return Guid.Empty; } - return new Guid((string)value); + var value = (string)context.DbValue; + + return new Guid(value); } - public override void SetValue(IDbDataParameter parameter, Guid value) + public object FromDB(ColumnMap map, object dbValue) { - parameter.Value = value.ToString(); + return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); } + + 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 new file mode 100644 index 000000000..29aae4c00 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Converters/Int32Converter.cs @@ -0,0 +1,36 @@ +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 e7f935d71..0d71b1b72 100644 --- a/src/NzbDrone.Core/Datastore/Converters/LanguageIntConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/LanguageIntConverter.cs @@ -1,48 +1,65 @@ using System; -using System.Data; -using System.Text.Json; -using System.Text.Json.Serialization; -using Dapper; +using Marr.Data.Converters; +using Marr.Data.Mapping; +using Newtonsoft.Json; using NzbDrone.Core.Languages; namespace NzbDrone.Core.Datastore.Converters { - public class DapperLanguageIntConverter : SqlMapper.TypeHandler + public class LanguageIntConverter : JsonConverter, IConverter { - public override void SetValue(IDbDataParameter parameter, Language value) + public object FromDB(ConverterContext context) { - 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) + if (context.DbValue == DBNull.Value) { return Language.Unknown; } - return (Language) Convert.ToInt32(value); - } - } + var val = Convert.ToInt32(context.DbValue); - public class LanguageIntConverter : JsonConverter - { - public override Language Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var item = reader.GetInt32(); - return (Language)item; + return (Language)val; } - public override void Write(Utf8JsonWriter writer, Language value, JsonSerializerOptions options) + public object FromDB(ColumnMap map, object dbValue) { - writer.WriteNumberValue((int) value); + 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)); } } -} +} \ No newline at end of file diff --git a/src/NzbDrone.Core/Datastore/Converters/NoFlagsStringEnumConverter.cs b/src/NzbDrone.Core/Datastore/Converters/NoFlagsStringEnumConverter.cs deleted file mode 100644 index bd30b659c..000000000 --- a/src/NzbDrone.Core/Datastore/Converters/NoFlagsStringEnumConverter.cs +++ /dev/null @@ -1,17 +0,0 @@ -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 dfb2e52c2..ba2b239fd 100644 --- a/src/NzbDrone.Core/Datastore/Converters/OsPathConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/OsPathConverter.cs @@ -1,25 +1,36 @@ using System; -using System.Data; -using Dapper; +using Marr.Data.Converters; +using Marr.Data.Mapping; using NzbDrone.Common.Disk; namespace NzbDrone.Core.Datastore.Converters { - public class OsPathConverter : SqlMapper.TypeHandler + public class OsPathConverter : IConverter { - public override void SetValue(IDbDataParameter parameter, OsPath value) + public object FromDB(ConverterContext context) { - parameter.Value = value.FullPath; - } - - public override OsPath Parse(object value) - { - if (value == null || value is DBNull) + if (context.DbValue == DBNull.Value) { - return new OsPath(null); + return DBNull.Value; } - return new OsPath((string) value); + var value = (string)context.DbValue; + + return new OsPath(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 1c2edace9..ace64d6af 100644 --- a/src/NzbDrone.Core/Datastore/Converters/ProviderSettingConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/ProviderSettingConverter.cs @@ -1,23 +1,40 @@ -using System.Data; -using System.Text.Json; +using System; +using Marr.Data.Converters; +using NzbDrone.Common.Reflection; +using NzbDrone.Common.Serializer; using NzbDrone.Core.ThingiProvider; namespace NzbDrone.Core.Datastore.Converters { - public class ProviderSettingConverter : EmbeddedDocumentConverter + public class ProviderSettingConverter : EmbeddedDocumentConverter { - public override IProviderConfig Parse(object value) + public override object FromDB(ConverterContext context) { - // We can't deserialize based on another column, happens in ProviderRepository instead - return null; - } + if (context.DbValue == DBNull.Value) + { + return NullConfig.Instance; + } - 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); + 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); } } -} +} \ 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 3c2b70144..72a3cb53a 100644 --- a/src/NzbDrone.Core/Datastore/Converters/QualityIntConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/QualityIntConverter.cs @@ -1,37 +1,59 @@ using System; +using Marr.Data.Converters; +using Marr.Data.Mapping; using NzbDrone.Core.Qualities; -using System.Text.Json; -using System.Text.Json.Serialization; -using Dapper; -using System.Data; +using Newtonsoft.Json; namespace NzbDrone.Core.Datastore.Converters { - public class QualityIntConverter : JsonConverter + public class QualityIntConverter : JsonConverter, IConverter { - public override Quality Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public object FromDB(ConverterContext context) { - var item = reader.GetInt32(); - return (Quality)item; + if (context.DbValue == DBNull.Value) + { + return Quality.Unknown; + } + + var val = Convert.ToInt32(context.DbValue); + + return (Quality)val; } - public override void Write(Utf8JsonWriter writer, Quality value, JsonSerializerOptions options) + public object FromDB(ColumnMap map, object dbValue) { - writer.WriteNumberValue((int) value); + 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)); } } - - 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 21381bf79..fc7e51ea8 100644 --- a/src/NzbDrone.Core/Datastore/Converters/QualityTagStringConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/QualityTagStringConverter.cs @@ -1,41 +1,60 @@ 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 DapperQualityTagStringConverter : SqlMapper.TypeHandler + public class QualityTagStringConverter : JsonConverter, IConverter { - public override void SetValue(IDbDataParameter parameter, FormatTag value) + public object FromDB(ConverterContext context) { - parameter.Value = value.Raw; - } - - public override FormatTag Parse(object value) - { - if (value == null || value is DBNull) + if (context.DbValue == DBNull.Value) { return new FormatTag(""); //Will throw argument exception! } - return new FormatTag(Convert.ToString(value)); - } - } + var val = Convert.ToString(context.DbValue); - public class QualityTagStringConverter : JsonConverter - { - public override FormatTag Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + return new FormatTag(val); + } + + public object FromDB(ColumnMap map, object dbValue) { - var item = reader.GetString(); + 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; return new FormatTag(Convert.ToString(item)); } - public override void Write(Utf8JsonWriter writer, FormatTag value, JsonSerializerOptions options) + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { - writer.WriteStringValue(value.Raw); + writer.WriteValue(ToDB(value)); } } } diff --git a/src/NzbDrone.Core/Datastore/Converters/TimeSpanConverter.cs b/src/NzbDrone.Core/Datastore/Converters/TimeSpanConverter.cs index 07f2d9314..c94551055 100644 --- a/src/NzbDrone.Core/Datastore/Converters/TimeSpanConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/TimeSpanConverter.cs @@ -1,19 +1,43 @@ using System; -using System.Text.Json; -using System.Text.Json.Serialization; +using System.Globalization; +using Marr.Data.Converters; +using Marr.Data.Mapping; +using NzbDrone.Common.Extensions; namespace NzbDrone.Core.Datastore.Converters { - public class TimeSpanConverter : JsonConverter + public class TimeSpanConverter : IConverter { - public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public object FromDB(ConverterContext context) { - return TimeSpan.Parse(reader.GetString()); + if (context.DbValue == DBNull.Value) + { + return TimeSpan.Zero; + } + + if (context.DbValue is TimeSpan) + { + return context.DbValue; + } + + return TimeSpan.Parse(context.DbValue.ToString(), CultureInfo.InvariantCulture); } - public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) + public object FromDB(ColumnMap map, object dbValue) { - writer.WriteStringValue(value.ToString()); + return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); } + + 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 db70f2359..1225f0806 100644 --- a/src/NzbDrone.Core/Datastore/Converters/UtcConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/UtcConverter.cs @@ -1,34 +1,32 @@ -using System; -using System.Data; -using System.Text.Json; -using System.Text.Json.Serialization; -using Dapper; +using System; +using Marr.Data.Converters; +using Marr.Data.Mapping; namespace NzbDrone.Core.Datastore.Converters { - public class DapperUtcConverter : SqlMapper.TypeHandler + public class UtcConverter : IConverter { - public override void SetValue(IDbDataParameter parameter, DateTime value) + public object FromDB(ConverterContext context) { - parameter.Value = value.ToUniversalTime(); + return context.DbValue; } - public override DateTime Parse(object value) + public object FromDB(ColumnMap map, object dbValue) { - return DateTime.SpecifyKind((DateTime)value, DateTimeKind.Utc); + return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); } + + public object ToDB(object clrValue) + { + if (clrValue == DBNull.Value) + { + return clrValue; + } + + var dateTime = (DateTime)clrValue; + return dateTime.ToUniversalTime(); + } + + public Type DbType => typeof(DateTime); } - - 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")); - } - } -} +} \ No newline at end of file diff --git a/src/NzbDrone.Core/Datastore/Database.cs b/src/NzbDrone.Core/Datastore/Database.cs index 609c562b0..1ff4fba49 100644 --- a/src/NzbDrone.Core/Datastore/Database.cs +++ b/src/NzbDrone.Core/Datastore/Database.cs @@ -1,6 +1,5 @@ using System; -using System.Data; -using Dapper; +using Marr.Data; using NLog; using NzbDrone.Common.Instrumentation; @@ -8,7 +7,7 @@ namespace NzbDrone.Core.Datastore { public interface IDatabase { - IDbConnection OpenConnection(); + IDataMapper GetDataMapper(); Version Version { get; } int Migration { get; } void Vacuum(); @@ -17,17 +16,17 @@ namespace NzbDrone.Core.Datastore 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 IDbConnection OpenConnection() + public IDataMapper GetDataMapper() { return _datamapperFactory(); } @@ -38,7 +37,7 @@ namespace NzbDrone.Core.Datastore { using (var db = _datamapperFactory()) { - var version = db.QueryFirstOrDefault("SELECT sqlite_version()"); + var version = db.ExecuteScalar("SELECT sqlite_version()").ToString(); return new Version(version); } } @@ -48,10 +47,9 @@ namespace NzbDrone.Core.Datastore { get { - using (var db = _datamapperFactory()) - { - return db.QueryFirstOrDefault("SELECT version from VersionInfo ORDER BY version DESC LIMIT 1"); - } + var migration = _datamapperFactory() + .ExecuteScalar("SELECT version from VersionInfo ORDER BY version DESC LIMIT 1").ToString(); + return Convert.ToInt32(migration); } } @@ -62,7 +60,7 @@ namespace NzbDrone.Core.Datastore _logger.Info("Vacuuming {0} database", _databaseName); using (var db = _datamapperFactory()) { - db.Execute("Vacuum;"); + db.ExecuteNonQuery("Vacuum;"); } _logger.Info("{0} database compressed", _databaseName); } diff --git a/src/NzbDrone.Core/Datastore/DbFactory.cs b/src/NzbDrone.Core/Datastore/DbFactory.cs index 79924c69a..07e406595 100644 --- a/src/NzbDrone.Core/Datastore/DbFactory.cs +++ b/src/NzbDrone.Core/Datastore/DbFactory.cs @@ -1,5 +1,7 @@ using System; using System.Data.SQLite; +using Marr.Data; +using Marr.Data.Reflection; using NLog; using NzbDrone.Common.Composition; using NzbDrone.Common.Disk; @@ -29,6 +31,7 @@ namespace NzbDrone.Core.Datastore { InitializeEnvironment(); + MapRepository.Instance.ReflectionStrategy = new SimpleReflectionStrategy(); TableMapping.Map(); } @@ -95,11 +98,12 @@ namespace NzbDrone.Core.Datastore var db = new Database(migrationContext.MigrationType.ToString(), () => { - var conn = SQLiteFactory.Instance.CreateConnection(); - conn.ConnectionString = connectionString; - conn.Open(); + var dataMapper = new DataMapper(SQLiteFactory.Instance, connectionString) + { + SqlMode = SqlModes.Text, + }; - return conn; + return dataMapper; }); return db; diff --git a/src/NzbDrone.Core/Datastore/ExpressionVisitor.cs b/src/NzbDrone.Core/Datastore/ExpressionVisitor.cs deleted file mode 100644 index 6e3ab91fd..000000000 --- a/src/NzbDrone.Core/Datastore/ExpressionVisitor.cs +++ /dev/null @@ -1,146 +0,0 @@ -/* 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 deleted file mode 100644 index c3c7a8967..000000000 --- a/src/NzbDrone.Core/Datastore/Extensions/BuilderExtensions.cs +++ /dev/null @@ -1,114 +0,0 @@ -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 new file mode 100644 index 000000000..4d635024a --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Extensions/MappingExtensions.cs @@ -0,0 +1,63 @@ +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 new file mode 100644 index 000000000..39cc5b7a6 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Extensions/PagingSpecExtensions.cs @@ -0,0 +1,44 @@ +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 new file mode 100644 index 000000000..9374e0b97 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Extensions/RelationshipExtensions.cs @@ -0,0 +1,51 @@ +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 new file mode 100644 index 000000000..17d0aba02 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/LazyList.cs @@ -0,0 +1,30 @@ +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 05e152eb0..e1c9c3a20 100644 --- a/src/NzbDrone.Core/Datastore/LogDatabase.cs +++ b/src/NzbDrone.Core/Datastore/LogDatabase.cs @@ -1,5 +1,5 @@ using System; -using System.Data; +using Marr.Data; namespace NzbDrone.Core.Datastore { @@ -17,9 +17,9 @@ namespace NzbDrone.Core.Datastore _database = database; } - public IDbConnection OpenConnection() + public IDataMapper GetDataMapper() { - return _database.OpenConnection(); + return _database.GetDataMapper(); } public Version Version => _database.Version; diff --git a/src/NzbDrone.Core/Datastore/MainDatabase.cs b/src/NzbDrone.Core/Datastore/MainDatabase.cs index 82ab7c716..869c3a6da 100644 --- a/src/NzbDrone.Core/Datastore/MainDatabase.cs +++ b/src/NzbDrone.Core/Datastore/MainDatabase.cs @@ -1,5 +1,5 @@ using System; -using System.Data; +using Marr.Data; namespace NzbDrone.Core.Datastore { @@ -17,9 +17,9 @@ namespace NzbDrone.Core.Datastore _database = database; } - public IDbConnection OpenConnection() + public IDataMapper GetDataMapper() { - return _database.OpenConnection(); + return _database.GetDataMapper(); } 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 df5f49bfc..e98e667a8 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 @@ namespace NzbDrone.Core.Datastore.Migration 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,12 +44,13 @@ namespace NzbDrone.Core.Datastore.Migration 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 = ?"; - var param = updateCmd.CreateParameter(); - qualityProfileItemConverter.SetValue(param, items); + updateCmd.AddParameter(allowedNewJson); updateCmd.AddParameter(id); updateCmd.ExecuteNonQuery(); @@ -69,7 +70,7 @@ namespace NzbDrone.Core.Datastore.Migration 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()) { @@ -88,18 +89,17 @@ namespace NzbDrone.Core.Datastore.Migration continue; } - var qualityNew = new DestinationQualityModel036 - { - Quality = sourceQuality.Quality.Id, - Proper = sourceQuality.Proper - }; + var qualityNewJson = qualityModelConverter.ToDB(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 = ?"; - var param = updateCmd.CreateParameter(); - qualityModelConverter.SetValue(param, qualityNew); + updateCmd.AddParameter(qualityNewJson); 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 f192b7709..bd74367d6 100644 --- a/src/NzbDrone.Core/Datastore/Migration/104_add_moviefiles_table.cs +++ b/src/NzbDrone.Core/Datastore/Migration/104_add_moviefiles_table.cs @@ -1,5 +1,9 @@ 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 af13aeefe..24ebdf709 100644 --- a/src/NzbDrone.Core/Datastore/Migration/147_add_custom_formats.cs +++ b/src/NzbDrone.Core/Datastore/Migration/147_add_custom_formats.cs @@ -1,6 +1,13 @@ -using System.Data; -using FluentMigrator; +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 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 aa151b9d1..4013e3d19 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 @@ namespace NzbDrone.Core.Datastore.Migration 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 @@ namespace NzbDrone.Core.Datastore.Migration foreach (var group in movieLanguages.GroupBy(v => v.Value, v => v.Key)) { - var language = new List { Language.FindById(group.Key) }; + var languageJson = LanguageConverter.ToDB(new List { Language.FindById(group.Key) }); var movieIds = group.Select(v => v.ToString()).Join(","); @@ -85,8 +85,7 @@ namespace NzbDrone.Core.Datastore.Migration { updateMovieFilesCmd.Transaction = tran; updateMovieFilesCmd.CommandText = $"UPDATE MovieFiles SET Languages = ? WHERE MovieId IN ({movieIds})"; - var param = updateMovieFilesCmd.CreateParameter(); - LanguageConverter.SetValue(param, language); + updateMovieFilesCmd.AddParameter(languageJson); updateMovieFilesCmd.ExecuteNonQuery(); } @@ -95,8 +94,7 @@ namespace NzbDrone.Core.Datastore.Migration { updateHistoryCmd.Transaction = tran; updateHistoryCmd.CommandText = $"UPDATE History SET Languages = ? WHERE MovieId IN ({movieIds})"; - var param = updateHistoryCmd.CreateParameter(); - LanguageConverter.SetValue(param, language); + updateHistoryCmd.AddParameter(languageJson); updateHistoryCmd.ExecuteNonQuery(); } @@ -105,8 +103,7 @@ namespace NzbDrone.Core.Datastore.Migration { updateBlacklistCmd.Transaction = tran; updateBlacklistCmd.CommandText = $"UPDATE Blacklist SET Languages = ? WHERE MovieId IN ({movieIds})"; - var param = updateBlacklistCmd.CreateParameter(); - LanguageConverter.SetValue(param, language); + updateBlacklistCmd.AddParameter(languageJson); 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 deleted file mode 100644 index 868e3d923..000000000 --- a/src/NzbDrone.Core/Datastore/Migration/162_fix_profile_format_default.cs +++ /dev/null @@ -1,20 +0,0 @@ -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 deleted file mode 100644 index 73a37d1e6..000000000 --- a/src/NzbDrone.Core/Datastore/TableMapper.cs +++ /dev/null @@ -1,114 +0,0 @@ -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 d2898a8aa..4a8afc288 100644 --- a/src/NzbDrone.Core/Datastore/TableMapping.cs +++ b/src/NzbDrone.Core/Datastore/TableMapping.cs @@ -1,138 +1,148 @@ using System; using System.Collections.Generic; -using Dapper; +using Marr.Data; +using Marr.Data.Mapping; using NzbDrone.Common.Reflection; -using NzbDrone.Core.Authentication; using NzbDrone.Core.Blacklisting; using NzbDrone.Core.Configuration; -using NzbDrone.Core.CustomFilters; -using NzbDrone.Core.CustomFormats; using NzbDrone.Core.Datastore.Converters; +using NzbDrone.Core.Datastore.Extensions; 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.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.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; +using NzbDrone.Core.Movies; +using NzbDrone.Common.Disk; +using NzbDrone.Core.Authentication; +using NzbDrone.Core.CustomFilters; +using NzbDrone.Core.CustomFormats; +using NzbDrone.Core.Extras.Metadata; +using NzbDrone.Core.Extras.Metadata.Files; +using NzbDrone.Core.Extras.Others; +using NzbDrone.Core.Extras.Subtitles; +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.NetImport; +using NzbDrone.Core.NetImport.ImportExclusions; +using NzbDrone.Core.Movies.AlternativeTitles; +using NzbDrone.Core.Languages; namespace NzbDrone.Core.Datastore { public static class TableMapping { - static TableMapping() - { - Mapper = new TableMapper(); - } - - public static TableMapper Mapper { get; private set; } + private static readonly FluentMappings Mapper = new FluentMappings(true); public static void Map() { RegisterMappers(); - Mapper.Entity("Config").RegisterModel(); + Mapper.Entity().RegisterModel("Config"); - Mapper.Entity("RootFolders").RegisterModel() + Mapper.Entity().RegisterModel("RootFolders") .Ignore(r => r.Accessible) .Ignore(r => r.FreeSpace) .Ignore(r => r.TotalSpace); - Mapper.Entity("ScheduledTasks").RegisterModel(); + Mapper.Entity().RegisterModel("ScheduledTasks"); - Mapper.Entity("Indexers").RegisterModel() - .Ignore(x => x.ImplementationName) + Mapper.Entity().RegisterDefinition("Indexers") .Ignore(i => i.Enable) .Ignore(i => i.Protocol) .Ignore(i => i.SupportsRss) .Ignore(i => i.SupportsSearch) .Ignore(d => d.Tags); - Mapper.Entity("NetImport").RegisterModel() - .Ignore(x => x.ImplementationName) - .Ignore(i => i.Enable); + Mapper.Entity().RegisterDefinition("NetImport") + .Ignore(i => i.Enable) + .Relationship() + .HasOne(n => n.Profile, n => n.ProfileId); - Mapper.Entity("Notifications").RegisterModel() - .Ignore(x => x.ImplementationName) + Mapper.Entity().RegisterDefinition("Notifications") .Ignore(i => i.SupportsOnGrab) .Ignore(i => i.SupportsOnDownload) .Ignore(i => i.SupportsOnUpgrade) .Ignore(i => i.SupportsOnRename) .Ignore(i => i.SupportsOnHealthIssue); - Mapper.Entity("Metadata").RegisterModel() - .Ignore(x => x.ImplementationName) + Mapper.Entity().RegisterDefinition("Metadata") .Ignore(d => d.Tags); - Mapper.Entity("DownloadClients").RegisterModel() - .Ignore(x => x.ImplementationName) + Mapper.Entity().RegisterDefinition("DownloadClients") .Ignore(d => d.Protocol) .Ignore(d => d.Tags); - Mapper.Entity("History").RegisterModel(); + Mapper.Entity().RegisterModel("History") + .AutoMapChildModels(); - Mapper.Entity("MovieFiles").RegisterModel() - .Ignore(f => f.Path); + 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("Movies").RegisterModel() - .Ignore(s => s.RootFolderPath) - .Ignore(m => m.Actors); + 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("AlternativeTitles").RegisterModel(); + Mapper.Entity().RegisterModel("AlternativeTitles") + .For(t => t.Id) + .SetAltName("AltTitle_Id") + .Relationship() + .HasOne(t => t.Movie, t => t.MovieId); - Mapper.Entity("ImportExclusions").RegisterModel(); - Mapper.Entity("QualityDefinitions").RegisterModel() - .Ignore(d => d.GroupName) - .Ignore(d => d.Weight); + Mapper.Entity().RegisterModel("ImportExclusions"); - Mapper.Entity("CustomFormats").RegisterModel(); + Mapper.Entity().RegisterModel("QualityDefinitions") + .Ignore(d => d.GroupName) + .Ignore(d => d.Weight) + .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("CustomFormats") + .Relationship(); - Mapper.Entity("PendingReleases").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") .Ignore(e => e.RemoteMovie); - Mapper.Entity("RemotePathMappings").RegisterModel(); - Mapper.Entity("Tags").RegisterModel(); - Mapper.Entity("Restrictions").RegisterModel(); + Mapper.Entity().RegisterModel("RemotePathMappings"); + Mapper.Entity().RegisterModel("Tags"); + Mapper.Entity().RegisterModel("Restrictions"); - Mapper.Entity("DelayProfiles").RegisterModel(); - Mapper.Entity("Users").RegisterModel(); - Mapper.Entity("Commands").RegisterModel() - .Ignore(c => c.Message); + Mapper.Entity().RegisterModel("DelayProfiles"); + Mapper.Entity().RegisterModel("Users"); + Mapper.Entity().RegisterModel("Commands") + .Ignore(c => c.Message); - Mapper.Entity("IndexerStatus").RegisterModel(); - Mapper.Entity("DownloadClientStatus").RegisterModel(); + Mapper.Entity().RegisterModel("IndexerStatus"); + Mapper.Entity().RegisterModel("DownloadClientStatus"); - Mapper.Entity("CustomFilters").RegisterModel(); + Mapper.Entity().RegisterModel("CustomFilters"); } private static void RegisterMappers() @@ -140,30 +150,32 @@ namespace NzbDrone.Core.Datastore RegisterEmbeddedConverter(); RegisterProviderSettingConverter(); - 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()); + 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()); } private static void RegisterProviderSettingConverter() @@ -173,7 +185,7 @@ namespace NzbDrone.Core.Datastore var providerSettingConverter = new ProviderSettingConverter(); foreach (var embeddedType in settingTypes) { - SqlMapper.AddTypeHandler(embeddedType, providerSettingConverter); + MapRepository.Instance.RegisterTypeConverter(embeddedType, providerSettingConverter); } } @@ -181,24 +193,16 @@ namespace NzbDrone.Core.Datastore { var embeddedTypes = typeof(IEmbeddedDocument).Assembly.ImplementationsOf(); - var embeddedConverterDefinition = typeof(EmbeddedDocumentConverter<>).GetGenericTypeDefinition(); + var embeddedConvertor = new EmbeddedDocumentConverter(); var genericListDefinition = typeof(List<>).GetGenericTypeDefinition(); foreach (var embeddedType in embeddedTypes) { var embeddedListType = genericListDefinition.MakeGenericType(embeddedType); - RegisterEmbeddedConverter(embeddedType, embeddedConverterDefinition); - RegisterEmbeddedConverter(embeddedListType, embeddedConverterDefinition); + MapRepository.Instance.RegisterTypeConverter(embeddedType, embeddedConvertor); + MapRepository.Instance.RegisterTypeConverter(embeddedListType, embeddedConvertor); } } - - 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 deleted file mode 100644 index 893356055..000000000 --- a/src/NzbDrone.Core/Datastore/WhereBuilder.cs +++ /dev/null @@ -1,303 +0,0 @@ -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 bdc7b988e..a4e171dff 100644 --- a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs +++ b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs @@ -62,7 +62,7 @@ namespace NzbDrone.Core.DecisionEngine private int CompareQuality(DownloadDecision x, DownloadDecision y) { - return CompareAll(CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.Movie.Profile.GetIndex(remoteMovie.ParsedMovieInfo.Quality.Quality)), + return CompareAll(CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.Movie.Profile.Value.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 @@ namespace NzbDrone.Core.DecisionEngine var left = x.RemoteMovie.ParsedMovieInfo.Quality.CustomFormats.WithNone(); var right = y.RemoteMovie.ParsedMovieInfo.Quality.CustomFormats; - var leftIndicies = QualityModelComparer.GetIndicies(left, x.RemoteMovie.Movie.Profile); - var rightIndicies = QualityModelComparer.GetIndicies(right, y.RemoteMovie.Movie.Profile); + var leftIndicies = QualityModelComparer.GetIndicies(left, x.RemoteMovie.Movie.Profile.Value); + var rightIndicies = QualityModelComparer.GetIndicies(right, y.RemoteMovie.Movie.Profile.Value); var leftTotal = leftIndicies.Sum(); var rightTotal = rightIndicies.Sum(); @@ -87,7 +87,8 @@ namespace NzbDrone.Core.DecisionEngine return CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => { var title = remoteMovie.Release.Title; - var preferredWords = remoteMovie.Movie.Profile.PreferredTags; + remoteMovie.Movie.Profile.LazyLoad(); + var preferredWords = remoteMovie.Movie.Profile.Value.PreferredTags; if (preferredWords == null) { diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/CustomFormatAllowedByProfileSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/CustomFormatAllowedByProfileSpecification.cs index 5a5dff4b7..3369ac0a3 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/CustomFormatAllowedByProfileSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/CustomFormatAllowedByProfileSpecification.cs @@ -22,7 +22,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { var formats = subject.ParsedMovieInfo.Quality.CustomFormats.WithNone(); _logger.Debug("Checking if report meets custom format requirements. {0}", formats.ToExtendedString()); - var notAllowedFormats = subject.Movie.Profile.FormatItems.Where(v => v.Allowed == false).Select(f => f.Format).ToList(); + var notAllowedFormats = subject.Movie.Profile.Value.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 541fb1535..52a6ae955 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/CutoffSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/CutoffSpecification.cs @@ -22,7 +22,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { - var profile = subject.Movie.Profile; + var profile = subject.Movie.Profile.Value; if (subject.Movie.MovieFile != null) { @@ -33,7 +33,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications 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.Cutoff); + return Decision.Reject("Existing file meets cutoff: {0} - {1}", qualityCutoff, subject.Movie.Profile.Value.Cutoff); } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/LanguageSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/LanguageSpecification.cs index ca0af52b2..5631328e8 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/LanguageSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/LanguageSpecification.cs @@ -19,7 +19,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { - var wantedLanguage = subject.Movie.Profile.Language; + var wantedLanguage = subject.Movie.Profile.Value.Language; if (wantedLanguage == Language.Any) { diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/QualityAllowedByProfileSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/QualityAllowedByProfileSpecification.cs index 36122fd47..9da9b5c3c 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/QualityAllowedByProfileSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/QualityAllowedByProfileSpecification.cs @@ -20,7 +20,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { _logger.Debug("Checking if report meets quality requirements. {0}", subject.ParsedMovieInfo.Quality); - var profile = subject.Movie.Profile; + var profile = subject.Movie.Profile.Value; 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 7e267a7c2..7edb8c150 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs @@ -35,7 +35,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications foreach (var queueItem in matchingMovies) { var remoteMovie = queueItem.RemoteMovie; - var qualityProfile = subject.Movie.Profile; + var qualityProfile = subject.Movie.Profile.Value; _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 ddbd9dd9b..13a8f01e6 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs @@ -37,14 +37,14 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync return Decision.Accept(); } - var profile = subject.Movie.Profile; + var profile = subject.Movie.Profile.Value; 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?.PreferredTags; + var preferredWords = subject.Movie.Profile?.Value?.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 70720acfd..f99f5c2a0 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeAllowedSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeAllowedSpecification.cs @@ -21,7 +21,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) { - var qualityProfile = subject.Movie.Profile; + var qualityProfile = subject.Movie.Profile.Value; if (subject.Movie.MovieFileId != 0) { diff --git a/src/NzbDrone.Core/DiskSpace/DiskSpaceService.cs b/src/NzbDrone.Core/DiskSpace/DiskSpaceService.cs index 2eb784917..63402605c 100644 --- a/src/NzbDrone.Core/DiskSpace/DiskSpaceService.cs +++ b/src/NzbDrone.Core/DiskSpace/DiskSpaceService.cs @@ -42,9 +42,9 @@ namespace NzbDrone.Core.DiskSpace private IEnumerable GetMoviesRootPaths() { - return _movieService.AllMoviePaths() - .Where(s => _diskProvider.FolderExists(s)) - .Select(s => _diskProvider.GetPathRoot(s)) + return _movieService.GetAllMovies() + .Where(s => _diskProvider.FolderExists(s.Path)) + .Select(s => _diskProvider.GetPathRoot(s.Path)) .Distinct(); } diff --git a/src/NzbDrone.Core/Download/DownloadClientRepository.cs b/src/NzbDrone.Core/Download/DownloadClientRepository.cs index 1d04b56f3..9acebdeb0 100644 --- a/src/NzbDrone.Core/Download/DownloadClientRepository.cs +++ b/src/NzbDrone.Core/Download/DownloadClientRepository.cs @@ -16,4 +16,4 @@ namespace NzbDrone.Core.Download { } } -} +} \ 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 bdade04f9..97869354b 100644 --- a/src/NzbDrone.Core/Download/Pending/PendingReleaseRepository.cs +++ b/src/NzbDrone.Core/Download/Pending/PendingReleaseRepository.cs @@ -20,17 +20,17 @@ namespace NzbDrone.Core.Download.Pending public void DeleteByMovieId(int movieId) { - Delete(movieId); + Delete(r => r.MovieId == movieId); } public List AllByMovieId(int movieId) { - return Query(x => x.MovieId == movieId); + return Query.Where(p => p.MovieId == movieId).ToList(); } public List WithoutFallback() { - return Query(x => x.Reason != PendingReleaseReason.Fallback); + return Query.Where(p => p.Reason != PendingReleaseReason.Fallback); } } } diff --git a/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs b/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs index 3b71d8ee2..2e5cb6bfa 100644 --- a/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs +++ b/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs @@ -346,7 +346,7 @@ namespace NzbDrone.Core.Download.Pending return; } - var profile = remoteMovie.Movie.Profile; + var profile = remoteMovie.Movie.Profile.Value; foreach (var existingReport in existingReports) { diff --git a/src/NzbDrone.Core/Extras/ExtraService.cs b/src/NzbDrone.Core/Extras/ExtraService.cs index 44d8f5f7f..4c4421478 100644 --- a/src/NzbDrone.Core/Extras/ExtraService.cs +++ b/src/NzbDrone.Core/Extras/ExtraService.cs @@ -2,9 +2,11 @@ using System; 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; @@ -166,7 +168,7 @@ namespace NzbDrone.Core.Extras foreach (var movieFile in movieFiles) { - movieFile.Movie = _movieService.GetMovie(movieId); + movieFile.Movie = new LazyLoaded(_movieService.GetMovie(movieId)); } return movieFiles; diff --git a/src/NzbDrone.Core/Extras/Files/ExtraFileRepository.cs b/src/NzbDrone.Core/Extras/Files/ExtraFileRepository.cs index 2498e887e..c5d7cba47 100644 --- a/src/NzbDrone.Core/Extras/Files/ExtraFileRepository.cs +++ b/src/NzbDrone.Core/Extras/Files/ExtraFileRepository.cs @@ -24,7 +24,7 @@ namespace NzbDrone.Core.Extras.Files public void DeleteForMovie(int movieId) { - Delete(movieId); + Delete(c => c.MovieId == movieId); } public void DeleteForMovieFile(int movieFileId) @@ -34,17 +34,17 @@ namespace NzbDrone.Core.Extras.Files public List GetFilesByMovie(int movieId) { - return Query(x => x.MovieId == movieId); + return Query.Where(c => c.MovieId == movieId).ToList(); } public List GetFilesByMovieFile(int movieFileId) { - return Query(x => x.MovieFileId == movieFileId); + return Query.Where(c => c.MovieFileId == movieFileId).ToList(); } public TExtraFile FindByPath(string path) { - return Query(x => x.RelativePath == path).SingleOrDefault(); + return Query.Where(c => c.RelativePath == path).SingleOrDefault(); } } } diff --git a/src/NzbDrone.Core/HealthCheck/Checks/MountCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/MountCheck.cs index 8579b73e5..88e13c12e 100644 --- a/src/NzbDrone.Core/HealthCheck/Checks/MountCheck.cs +++ b/src/NzbDrone.Core/HealthCheck/Checks/MountCheck.cs @@ -19,11 +19,11 @@ namespace NzbDrone.Core.HealthCheck.Checks 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.AllMoviePaths() - .Select(p => _diskProvider.GetMount(p)) - .Where(m => m != null && m.MountOptions != null && m.MountOptions.IsReadOnly) - .DistinctBy(m => m.RootDirectory) - .ToList(); + 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(); if (mounts.Any()) { diff --git a/src/NzbDrone.Core/HealthCheck/Checks/RootFolderCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/RootFolderCheck.cs index 076ba64bc..16bc754f1 100644 --- a/src/NzbDrone.Core/HealthCheck/Checks/RootFolderCheck.cs +++ b/src/NzbDrone.Core/HealthCheck/Checks/RootFolderCheck.cs @@ -20,11 +20,11 @@ namespace NzbDrone.Core.HealthCheck.Checks public override HealthCheck Check() { - var missingRootFolders = _movieService.AllMoviePaths() - .Select(s => _diskProvider.GetParentFolder(s)) - .Distinct() - .Where(s => !_diskProvider.FolderExists(s)) - .ToList(); + var missingRootFolders = _movieService.GetAllMovies() + .Select(s => _diskProvider.GetParentFolder(s.Path)) + .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 e049278f0..dde58ccb9 100644 --- a/src/NzbDrone.Core/History/HistoryRepository.cs +++ b/src/NzbDrone.Core/History/HistoryRepository.cs @@ -1,12 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using Dapper; +using Marr.Data.QGen; using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; -using NzbDrone.Core.Movies; -using NzbDrone.Core.Profiles; using NzbDrone.Core.Qualities; +using NzbDrone.Core.Movies; namespace NzbDrone.Core.History { @@ -15,7 +14,7 @@ namespace NzbDrone.Core.History List GetBestQualityInHistory(int movieId); History MostRecentForDownloadId(string downloadId); List FindByDownloadId(string downloadId); - List FindDownloadHistory(int movieId, QualityModel quality); + List FindDownloadHistory(int idMovieId, QualityModel quality); List GetByMovieId(int movieId, HistoryEventType? eventType); void DeleteForMovie(int movieId); History MostRecentForMovie(int movieId); @@ -32,35 +31,37 @@ namespace NzbDrone.Core.History public List GetBestQualityInHistory(int movieId) { - var history = Query(x => x.MovieId == movieId); + var history = Query.Where(c => c.MovieId == movieId).ToList(); return history.Select(h => h.Quality).ToList(); } public History MostRecentForDownloadId(string downloadId) { - return FindByDownloadId(downloadId) - .OrderByDescending(h => h.Date) - .FirstOrDefault(); + return Query.Where(h => h.DownloadId == downloadId) + .OrderByDescending(h => h.Date) + .FirstOrDefault(); } public List FindByDownloadId(string downloadId) { - return Query(x => x.DownloadId == downloadId); + return Query.Where(h => h.DownloadId == downloadId).ToList(); } - public List FindDownloadHistory(int movieId, QualityModel quality) + public List FindDownloadHistory(int idMovieId, QualityModel quality) { - var allowed = new [] { HistoryEventType.Grabbed, HistoryEventType.DownloadFailed, HistoryEventType.DownloadFolderImported }; - - return Query(h => h.MovieId == movieId && - h.Quality == quality && - allowed.Contains(h.EventType)); + return Query.Where(h => + h.MovieId == idMovieId && + h.Quality == quality && + (h.EventType == HistoryEventType.Grabbed || + h.EventType == HistoryEventType.DownloadFailed || + h.EventType == HistoryEventType.DownloadFolderImported) + ).ToList(); } public List GetByMovieId(int movieId, HistoryEventType? eventType) { - var query = Query(x => x.MovieId == movieId); + var query = Query.Where(h => h.MovieId == movieId).ToList(); if (eventType.HasValue) { @@ -77,45 +78,32 @@ namespace NzbDrone.Core.History Delete(c => c.MovieId == movieId); } - private IEnumerable SelectJoined(SqlBuilder.Template sql) + protected override SortBuilder GetPagedQuery(QueryBuilder query, PagingSpec 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(); - } + var baseQuery = query.Join(JoinType.Inner, h => h.Movie, (h, e) => h.MovieId == e.Id); + + return base.GetPagedQuery(baseQuery, pagingSpec); } - 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(x => x.MovieId == movieId) - .OrderByDescending(h => h.Date) - .FirstOrDefault(); + return Query.Where(h => h.MovieId == movieId) + .OrderByDescending(h => h.Date) + .FirstOrDefault(); } public List Since(DateTime date, HistoryEventType? eventType) { - var builder = Builder().Where(x => x.Date >= date); + var query = Query.Where(h => h.Date >= date).ToList(); if (eventType.HasValue) { - builder.Where(h => h.EventType == eventType); + query = query.Where(h => h.EventType == eventType).ToList(); } - return Query(builder).OrderBy(h => h.Date).ToList(); + query.OrderBy(h => h.Date); + + return query; } } } diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAbsolutePathMetadataFiles.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAbsolutePathMetadataFiles.cs index 1293d4f07..de6dcc0de 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAbsolutePathMetadataFiles.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAbsolutePathMetadataFiles.cs @@ -1,5 +1,4 @@ -using Dapper; -using NzbDrone.Core.Datastore; +using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers { @@ -14,9 +13,9 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers public void Clean() { - using (var mapper = _database.OpenConnection()) + using (var mapper = _database.GetDataMapper()) { - mapper.Execute(@"DELETE FROM MetadataFiles + mapper.ExecuteNonQuery(@"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 1b8dfa9d2..e63fd2dfa 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAdditionalNamingSpecs.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAdditionalNamingSpecs.cs @@ -1,5 +1,4 @@ -using Dapper; -using NzbDrone.Core.Datastore; +using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers { @@ -14,10 +13,10 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers public void Clean() { - using (var mapper = _database.OpenConnection()) + using (var mapper = _database.GetDataMapper()) { - mapper.Execute(@"DELETE FROM NamingConfig + mapper.ExecuteNonQuery(@"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 a35692a7f..9103dc4c1 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAdditionalUsers.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupAdditionalUsers.cs @@ -1,5 +1,4 @@ -using Dapper; -using NzbDrone.Core.Datastore; +using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers { @@ -14,13 +13,13 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers public void Clean() { - using (var mapper = _database.OpenConnection()) + using (var mapper = _database.GetDataMapper()) { - mapper.Execute(@"DELETE FROM Users - WHERE ID NOT IN ( - SELECT ID FROM Users - LIMIT 1)"); + mapper.ExecuteNonQuery(@"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 1e035febd..51c3ba3f9 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupDownloadClientUnavailablePendingReleases.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupDownloadClientUnavailablePendingReleases.cs @@ -1,5 +1,4 @@ using System; -using Dapper; using NzbDrone.Core.Datastore; using NzbDrone.Core.Download.Pending; @@ -16,16 +15,18 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers public void Clean() { - var mapper = _database.OpenConnection(); + var mapper = _database.GetDataMapper(); + var twoWeeksAgo = DateTime.UtcNow.AddDays(-14); - 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 } - }); + 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')"); } } } diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupDuplicateMetadataFiles.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupDuplicateMetadataFiles.cs index f57b6138f..5a405de77 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupDuplicateMetadataFiles.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupDuplicateMetadataFiles.cs @@ -1,4 +1,3 @@ -using Dapper; using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers @@ -20,10 +19,10 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers private void DeleteDuplicateMovieMetadata() { - using (var mapper = _database.OpenConnection()) + using (var mapper = _database.GetDataMapper()) { - mapper.Execute(@"DELETE FROM MetadataFiles + mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles WHERE Id IN ( SELECT Id FROM MetadataFiles WHERE Type = 1 @@ -35,10 +34,10 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers private void DeleteDuplicateMovieFileMetadata() { - using (var mapper = _database.OpenConnection()) + using (var mapper = _database.GetDataMapper()) { - mapper.Execute(@"DELETE FROM MetadataFiles + mapper.ExecuteNonQuery(@"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 eac8aeba2..6ef15ac2f 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedAlternativeTitles.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedAlternativeTitles.cs @@ -1,5 +1,4 @@ -using Dapper; -using NzbDrone.Core.Datastore; +using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers { @@ -14,10 +13,10 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers public void Clean() { - using (var mapper = _database.OpenConnection()) + using (var mapper = _database.GetDataMapper()) { - mapper.Execute(@"DELETE FROM AlternativeTitles + mapper.ExecuteNonQuery(@"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 89a1dfd95..7816c5ab9 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedBlacklist.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedBlacklist.cs @@ -1,5 +1,4 @@ -using Dapper; -using NzbDrone.Core.Datastore; +using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers { @@ -14,10 +13,10 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers public void Clean() { - using (var mapper = _database.OpenConnection()) + using (var mapper = _database.GetDataMapper()) { - mapper.Execute(@"DELETE FROM Blacklist + mapper.ExecuteNonQuery(@"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 a5f584797..3bb631eb9 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedDownloadClientStatus.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedDownloadClientStatus.cs @@ -1,5 +1,4 @@ -using Dapper; -using NzbDrone.Core.Datastore; +using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers { @@ -14,9 +13,9 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers public void Clean() { - var mapper = _database.OpenConnection(); + var mapper = _database.GetDataMapper(); - mapper.Execute(@"DELETE FROM DownloadClientStatus + mapper.ExecuteNonQuery(@"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 e27db67f9..d69112d97 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedHistoryItems.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedHistoryItems.cs @@ -1,4 +1,3 @@ -using Dapper; using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers @@ -19,10 +18,10 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers private void CleanupOrphanedByMovie() { - using (var mapper = _database.OpenConnection()) + using (var mapper = _database.GetDataMapper()) { - mapper.Execute(@"DELETE FROM History + mapper.ExecuteNonQuery(@"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 24711689b..60c956b99 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedIndexerStatus.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedIndexerStatus.cs @@ -1,4 +1,3 @@ -using Dapper; using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers @@ -14,10 +13,10 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers public void Clean() { - using (var mapper = _database.OpenConnection()) + using (var mapper = _database.GetDataMapper()) { - mapper.Execute(@"DELETE FROM IndexerStatus + mapper.ExecuteNonQuery(@"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 56e12d18a..1c89b1c46 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedMetadataFiles.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedMetadataFiles.cs @@ -1,4 +1,3 @@ -using Dapper; using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers @@ -21,10 +20,10 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers private void DeleteOrphanedByMovie() { - using (var mapper = _database.OpenConnection()) + using (var mapper = _database.GetDataMapper()) { - mapper.Execute(@"DELETE FROM MetadataFiles + mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles WHERE Id IN ( SELECT MetadataFiles.Id FROM MetadataFiles LEFT OUTER JOIN Movies @@ -35,10 +34,10 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers private void DeleteOrphanedByMovieFile() { - using (var mapper = _database.OpenConnection()) + using (var mapper = _database.GetDataMapper()) { - mapper.Execute(@"DELETE FROM MetadataFiles + mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles WHERE Id IN ( SELECT MetadataFiles.Id FROM MetadataFiles LEFT OUTER JOIN MovieFiles @@ -50,10 +49,10 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers private void DeleteWhereMovieFileIsZero() { - using (var mapper = _database.OpenConnection()) + using (var mapper = _database.GetDataMapper()) { - mapper.Execute(@"DELETE FROM MetadataFiles + mapper.ExecuteNonQuery(@"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 ee94f857e..d36d1da66 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedMovieFiles.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedMovieFiles.cs @@ -1,4 +1,3 @@ -using Dapper; using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers @@ -14,14 +13,14 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers public void Clean() { - using (var mapper = _database.OpenConnection()) + using (var mapper = _database.GetDataMapper()) { - 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)"); + 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)"); } } } diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedPendingReleases.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedPendingReleases.cs index 0a0b12c7c..bed514b93 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedPendingReleases.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedPendingReleases.cs @@ -1,5 +1,4 @@ -using Dapper; -using NzbDrone.Core.Datastore; +using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers { @@ -14,15 +13,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers public void Clean() { - using (var mapper = _database.OpenConnection()) + using (var mapper = _database.GetDataMapper()) { - 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)"); + 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)"); } } } diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupUnusedTags.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupUnusedTags.cs index b6811ee4b..f8ead0292 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 Dapper; +using Marr.Data; +using NzbDrone.Common.Serializer; using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers @@ -17,7 +17,7 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers public void Clean() { - using (var mapper = _database.OpenConnection()) + using (var mapper = _database.GetDataMapper()) { var usedTags = new[] {"Movies", "Notifications", "DelayProfiles", "Restrictions", "NetImport"} @@ -27,16 +27,16 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers var usedTagsList = string.Join(",", usedTags.Select(d => d.ToString()).ToArray()); - mapper.Execute($"DELETE FROM Tags WHERE NOT Id IN ({usedTagsList})"); + mapper.ExecuteNonQuery($"DELETE FROM Tags WHERE NOT Id IN ({usedTagsList})"); } } - private int[] GetUsedTags(string table, IDbConnection mapper) + private int[] GetUsedTags(string table, IDataMapper mapper) { - return mapper.Query>($"SELECT DISTINCT Tags FROM {table} WHERE NOT Tags = '[]'") - .SelectMany(x => x) - .Distinct() - .ToArray(); + return mapper.ExecuteReader($"SELECT DISTINCT Tags FROM {table} WHERE NOT Tags = '[]'", reader => reader.GetString(0)) + .SelectMany(Json.Deserialize>) + .Distinct() + .ToArray(); } } } diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/FixFutureRunScheduledTasks.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/FixFutureRunScheduledTasks.cs index 82a7af7fa..c4ed9f501 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/FixFutureRunScheduledTasks.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/FixFutureRunScheduledTasks.cs @@ -1,5 +1,4 @@ using System; -using Dapper; using NLog; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Core.Datastore; @@ -24,12 +23,13 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers _logger.Debug("Not running scheduled task last execution cleanup during debug"); } - using (var mapper = _database.OpenConnection()) + using (var mapper = _database.GetDataMapper()) { - mapper.Execute(@"UPDATE ScheduledTasks - SET LastExecution = @time - WHERE LastExecution > @time", - new { time = DateTime.UtcNow }); + mapper.AddParameter("time", DateTime.UtcNow); + + mapper.ExecuteNonQuery(@"UPDATE ScheduledTasks + SET LastExecution = @time + WHERE LastExecution > @time"); } } } diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/FixWronglyMatchedMovieFiles.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/FixWronglyMatchedMovieFiles.cs index 185f6e8aa..f6756cce4 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/FixWronglyMatchedMovieFiles.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/FixWronglyMatchedMovieFiles.cs @@ -15,7 +15,7 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers { /*var mapper = _database.GetDataMapper(); - mapper.Execute(@"UPDATE Movies + mapper.ExecuteNonQuery(@"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 7dbe32f80..90a3e6ecf 100644 --- a/src/NzbDrone.Core/Jobs/ScheduledTaskRepository.cs +++ b/src/NzbDrone.Core/Jobs/ScheduledTaskRepository.cs @@ -22,7 +22,7 @@ namespace NzbDrone.Core.Jobs public ScheduledTask GetDefinition(Type type) { - return Query(x => x.TypeName == type.FullName).Single(); + return Query.Where(c => c.TypeName == type.FullName).Single(); } public void SetLastExecutionTime(int id, DateTime executionTime, DateTime startTime) diff --git a/src/NzbDrone.Core/MediaFiles/MediaFileRepository.cs b/src/NzbDrone.Core/MediaFiles/MediaFileRepository.cs index 852a31070..6ef40f409 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaFileRepository.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaFileRepository.cs @@ -21,12 +21,12 @@ namespace NzbDrone.Core.MediaFiles public List GetFilesByMovie(int movieId) { - return Query(x => x.MovieId == movieId); + return Query.Where(c => c.MovieId == movieId).ToList(); } public List GetFilesWithoutMediaInfo() { - return Query(x => x.MediaInfo == null); + return Query.Where(c => c.MediaInfo == null).ToList(); } } } diff --git a/src/NzbDrone.Core/MediaFiles/MediaFileService.cs b/src/NzbDrone.Core/MediaFiles/MediaFileService.cs index 5ec3680c3..75a6c6d59 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaFileService.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaFileService.cs @@ -1,6 +1,7 @@ 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; @@ -24,27 +25,27 @@ namespace NzbDrone.Core.MediaFiles public class MediaFileService : IMediaFileService, IHandleAsync { - private readonly IMediaFileRepository _mediaFileRepository; - private readonly IMovieRepository _movieRepository; private readonly IEventAggregator _eventAggregator; + private readonly IMediaFileRepository _mediaFileRepository; + private readonly Logger _logger; public MediaFileService(IMediaFileRepository mediaFileRepository, - IMovieRepository movieRepository, - IEventAggregator eventAggregator) + IEventAggregator eventAggregator, Logger logger) { _mediaFileRepository = mediaFileRepository; - _movieRepository = movieRepository; _eventAggregator = eventAggregator; + _logger = logger; } public MovieFile Add(MovieFile movieFile) { var addedFile = _mediaFileRepository.Insert(movieFile); - if (addedFile.Movie == null) + addedFile.Movie.LazyLoad(); + if (addedFile.Movie == null || addedFile.Movie.Value == null) { - addedFile.Movie = _movieRepository.Get(movieFile.MovieId); + _logger.Error("Movie is null for the file {0}. Please run the houskeeping command to ensure movies and files are linked correctly."); } - + //_movieService.SetFileId(addedFile.Movie.Value, addedFile); //Should not be necessary, but sometimes below fails? _eventAggregator.PublishEvent(new MovieFileAddedEvent(addedFile)); return addedFile; @@ -63,11 +64,8 @@ namespace NzbDrone.Core.MediaFiles public void Delete(MovieFile movieFile, DeleteMediaFileReason reason) { //Little hack so we have the movie attached for the event consumers - if (movieFile.Movie == null) - { - movieFile.Movie = _movieRepository.Get(movieFile.MovieId); - } - movieFile.Path = Path.Combine(movieFile.Movie.Path, movieFile.RelativePath); + movieFile.Movie.LazyLoad(); + movieFile.Path = Path.Combine(movieFile.Movie.Value.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 cfd1c5775..0fcfe1e89 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieFile.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieFile.cs @@ -1,5 +1,7 @@ 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; @@ -22,7 +24,7 @@ namespace NzbDrone.Core.MediaFiles public List Languages { get; set; } public MediaInfoModel MediaInfo { get; set; } public string Edition { get; set; } - public Movie Movie { get; set; } + public LazyLoaded 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 3a790ad2d..173d6c6eb 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 Dapper; +using System.Data.SQLite; using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; @@ -10,16 +10,22 @@ namespace NzbDrone.Core.Messaging.Commands { 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() @@ -31,23 +37,39 @@ namespace NzbDrone.Core.Messaging.Commands public void OrphanStarted() { - 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()) + using (var mapper = _database.GetDataMapper()) { - conn.Execute(sql, args); + + 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"); } } + 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(x => x.Status == CommandStatus.Queued); + return Query.Where(c => c.Status == CommandStatus.Queued); + } + + public List Started() + { + return Query.Where(c => c.Status == CommandStatus.Started); } public void Start(CommandModel command) diff --git a/src/NzbDrone.Core/Movies/AlternativeTitles/AlternativeTitle.cs b/src/NzbDrone.Core/Movies/AlternativeTitles/AlternativeTitle.cs index ab2bd6118..c16826bd7 100644 --- a/src/NzbDrone.Core/Movies/AlternativeTitles/AlternativeTitle.cs +++ b/src/NzbDrone.Core/Movies/AlternativeTitles/AlternativeTitle.cs @@ -1,6 +1,8 @@ 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 @@ -15,7 +17,8 @@ namespace NzbDrone.Core.Movies.AlternativeTitles 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 1e1ede2fd..f40eb4776 100644 --- a/src/NzbDrone.Core/Movies/AlternativeTitles/AlternativeTitleRepository.cs +++ b/src/NzbDrone.Core/Movies/AlternativeTitles/AlternativeTitleRepository.cs @@ -1,5 +1,8 @@ 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; @@ -14,24 +17,27 @@ namespace NzbDrone.Core.Movies.AlternativeTitles public class AlternativeTitleRepository : BasicRepository, IAlternativeTitleRepository { + protected IMainDatabase _database; + public AlternativeTitleRepository(IMainDatabase database, IEventAggregator eventAggregator) : base(database, eventAggregator) { + _database = database; } public AlternativeTitle FindBySourceId(int sourceId) { - return Query(x => x.SourceId == sourceId).FirstOrDefault(); + return Query.Where(t => t.SourceId == sourceId).FirstOrDefault(); } public List FindBySourceIds(List sourceIds) { - return Query(x => sourceIds.Contains(x.SourceId)); + return Query.Where(t => t.SourceId.In(sourceIds)).ToList(); } public List FindByMovieId(int movieId) { - return Query(x => x.MovieId == movieId); + return Query.Where(t => t.MovieId == movieId).ToList(); } } } diff --git a/src/NzbDrone.Core/Movies/Movie.cs b/src/NzbDrone.Core/Movies/Movie.cs index 532748cab..311127b1b 100644 --- a/src/NzbDrone.Core/Movies/Movie.cs +++ b/src/NzbDrone.Core/Movies/Movie.cs @@ -1,9 +1,12 @@ 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 @@ -45,7 +48,7 @@ namespace NzbDrone.Core.Movies public DateTime? InCinemas { get; set; } public DateTime? PhysicalRelease { get; set; } public String PhysicalReleaseNote { get; set; } - public Profile Profile { get; set; } + public LazyLoaded 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 b6324356a..e661aefe0 100644 --- a/src/NzbDrone.Core/Movies/MovieRepository.cs +++ b/src/NzbDrone.Core/Movies/MovieRepository.cs @@ -3,22 +3,23 @@ using System.Linq; using System.Collections.Generic; using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; -using NzbDrone.Core.Qualities; -using Dapper; -using NzbDrone.Core.Movies.AlternativeTitles; +using NzbDrone.Core.Datastore.Extensions; +using Marr.Data.QGen; using NzbDrone.Core.MediaFiles; -using NzbDrone.Core.Profiles; +using NzbDrone.Core.Movies.AlternativeTitles; +using NzbDrone.Core.Parser.RomanNumerals; +using NzbDrone.Core.Qualities; namespace NzbDrone.Core.Movies { public interface IMovieRepository : IBasicRepository { bool MoviePathExists(string path); - List FindByTitles(List titles); + Movie FindByTitle(string cleanTitle); + Movie FindByTitle(string cleanTitle, int year); 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); @@ -27,119 +28,42 @@ namespace NzbDrone.Core.Movies void SetFileId(int fileId, int movieId); PagingSpec MoviesWhereCutoffUnmet(PagingSpec pagingSpec, List qualitiesBelowCutoff); Movie FindByPath(string path); - List AllMoviePaths(); } public class MovieRepository : BasicRepository, IMovieRepository { - private readonly IProfileRepository _profileRepository; - public MovieRepository(IMainDatabase database, - IProfileRepository profileRepository, - IEventAggregator eventAggregator) + protected IMainDatabase _database; + + public MovieRepository(IMainDatabase database, IEventAggregator eventAggregator) : base(database, eventAggregator) { - _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(); - } + _database = database; } public bool MoviePathExists(string path) { - return Query(x => x.Path == path).Any(); + return Query.Where(c => c.Path == path).Any(); } - public List FindByTitles(List titles) + public Movie FindByTitle(string cleanTitle) { - return Query(Builder().OrWhere(x => titles.Contains(x.CleanTitle)) - .OrWhere(x => titles.Contains(x.CleanTitle))); + return FindByTitle(cleanTitle, null); } - public List FindByTitleInexact(string cleanTitle) + public Movie FindByTitle(string cleanTitle, int year) { - return Query(x => cleanTitle.Contains(x.CleanTitle)); + return FindByTitle(cleanTitle, year as int?); } public Movie FindByImdbId(string imdbid) { var imdbIdWithPrefix = Parser.Parser.NormalizeImdbId(imdbid); - 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)); + return Query.Where(s => s.ImdbId == imdbIdWithPrefix).SingleOrDefault(); } public List GetMoviesByFileId(int fileId) { - return Query(x => x.MovieFileId == fileId); + return Query.Where(m => m.MovieFileId == fileId).ToList(); } public void SetFileId(int fileId, int movieId) @@ -149,51 +73,66 @@ namespace NzbDrone.Core.Movies public Movie FindByTitleSlug(string slug) { - return Query(x => x.TitleSlug == slug).FirstOrDefault(); + return Query.Where(m => m.TitleSlug == slug).FirstOrDefault(); } public List MoviesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored) { - var builder = Builder() - .Where(m => - (m.InCinemas >= start && m.InCinemas <= end) || - (m.PhysicalRelease >= start && m.PhysicalRelease <= end)); + 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); + } - if (!includeUnmonitored) - { - builder.Where(x => x.Monitored); - } - - return Query(builder); + return query.ToList(); } public List MoviesWithFiles(int movieId) { - return Query(x => x.MovieFileId != 0); + return Query.Join(JoinType.Inner, m => m.MovieFile, (m, mf) => m.MovieFileId == mf.Id) + .Where(m => m.Id == movieId).ToList(); } - public SqlBuilder MoviesWithoutFilesBuilder() => BuilderBase().Where(x => x.MovieFileId == 0); - public PagingSpec MoviesWithoutFiles(PagingSpec pagingSpec) { - pagingSpec.Records = GetPagedRecords(MoviesWithoutFilesBuilder().SelectAll(), pagingSpec, PagedSelector); - pagingSpec.TotalRecords = GetPagedRecordCount(MoviesWithoutFilesBuilder().SelectCount(), pagingSpec); + pagingSpec.TotalRecords = GetMoviesWithoutFilesQuery(pagingSpec).GetRowCount(); + pagingSpec.Records = GetMoviesWithoutFilesQuery(pagingSpec).ToList(); return pagingSpec; } - public SqlBuilder MoviesWhereCutoffUnmetBuilder(List qualitiesBelowCutoff) => BuilderBase() - .Where(x => x.MovieFileId != 0) - .Where(BuildQualityCutoffWhereClause(qualitiesBelowCutoff)); + 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 PagingSpec MoviesWhereCutoffUnmet(PagingSpec pagingSpec, List qualitiesBelowCutoff) { - pagingSpec.Records = GetPagedRecords(MoviesWhereCutoffUnmetBuilder(qualitiesBelowCutoff).SelectAll(), pagingSpec, PagedSelector); - pagingSpec.TotalRecords = GetPagedRecordCount(MoviesWhereCutoffUnmetBuilder(qualitiesBelowCutoff).SelectCount(), pagingSpec); + pagingSpec.TotalRecords = MoviesWhereCutoffUnmetQuery(pagingSpec, qualitiesBelowCutoff).GetRowCount(); + pagingSpec.Records = MoviesWhereCutoffUnmetQuery(pagingSpec, qualitiesBelowCutoff).ToList(); 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(); @@ -202,24 +141,86 @@ namespace NzbDrone.Core.Movies { foreach (var belowCutoff in profile.QualityIds) { - clauses.Add(string.Format($"(\"{_table}\".\"ProfileId\" = {profile.ProfileId} AND \"MovieFile\".\"Quality\" LIKE '%_quality_: {belowCutoff},%')")); + clauses.Add(string.Format("([t0].[ProfileId] = {0} AND [t2].[Quality] LIKE '%_quality_: {1},%')", profile.ProfileId, belowCutoff)); } } return string.Format("({0})", string.Join(" OR ", clauses)); } - public Movie FindByPath(string path) + 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) { - return Query(x => x.Path == path).FirstOrDefault(); + 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 AllMoviePaths() + public List FindByTitleInexact(string cleanTitle) { - using (var conn = _database.OpenConnection()) - { - return conn.Query("SELECT Path FROM Movies").ToList(); - } + 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(); + } + + protected override QueryBuilder AddJoinQueries(QueryBuilder baseQuery) + { + 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; } } } diff --git a/src/NzbDrone.Core/Movies/MovieService.cs b/src/NzbDrone.Core/Movies/MovieService.cs index f81bd7a98..0ff2e1b24 100644 --- a/src/NzbDrone.Core/Movies/MovieService.cs +++ b/src/NzbDrone.Core/Movies/MovieService.cs @@ -14,7 +14,6 @@ using NzbDrone.Core.Datastore; using NzbDrone.Core.Configuration; using NzbDrone.Core.Movies.Events; using NzbDrone.Core.NetImport.ImportExclusions; -using NzbDrone.Core.Parser.RomanNumerals; namespace NzbDrone.Core.Movies { @@ -27,13 +26,11 @@ namespace NzbDrone.Core.Movies 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); @@ -187,7 +184,7 @@ namespace NzbDrone.Core.Movies newMovies.ForEach(m => { - MoviePathState defaultState = MoviePathState.Static; + MoviePathState defaultState = MoviePathState.Static; if (!_configService.PathsDefaultStatic) { defaultState = MoviePathState.Dynamic; @@ -208,13 +205,11 @@ namespace NzbDrone.Core.Movies 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); @@ -228,49 +223,7 @@ namespace NzbDrone.Core.Movies public Movie FindByTitle(string title) { - 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; + return _movieRepository.FindByTitle(title.CleanSeriesTitle()); } public Movie FindByImdbId(string imdbid) @@ -283,11 +236,6 @@ namespace NzbDrone.Core.Movies 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 @@ -336,16 +284,16 @@ namespace NzbDrone.Core.Movies 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); @@ -416,11 +364,11 @@ namespace NzbDrone.Core.Movies public void Handle(MovieFileAddedEvent message) { - var movie = message.MovieFile.Movie; + var movie = message.MovieFile.Movie.Value; 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); + _logger.Info("Linking [{0}] > [{1}]", message.MovieFile.RelativePath, message.MovieFile.Movie.Value); } public void SetFileId(Movie movie, MovieFile movieFile) @@ -497,7 +445,7 @@ namespace NzbDrone.Core.Movies if (movie.Year > 1850) { - result = FindByTitle(movie.Title.CleanSeriesTitle(), movie.Year); + result = _movieRepository.FindByTitle(movie.Title.CleanSeriesTitle(), movie.Year); if (result != null) { return true; @@ -505,7 +453,7 @@ namespace NzbDrone.Core.Movies } else { - result = FindByTitle(movie.Title.CleanSeriesTitle()); + result = _movieRepository.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 c2876631a..17847d50a 100644 --- a/src/NzbDrone.Core/Movies/QueryExtensions.cs +++ b/src/NzbDrone.Core/Movies/QueryExtensions.cs @@ -1,8 +1,17 @@ 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 7291971b4..1f8f1bdae 100644 --- a/src/NzbDrone.Core/NetImport/ImportExclusions/ImportExclusion.cs +++ b/src/NzbDrone.Core/NetImport/ImportExclusions/ImportExclusion.cs @@ -1,4 +1,11 @@ -using NzbDrone.Core.Datastore; +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; namespace NzbDrone.Core.NetImport.ImportExclusions { diff --git a/src/NzbDrone.Core/NetImport/ImportExclusions/ImportExclusionsRepository.cs b/src/NzbDrone.Core/NetImport/ImportExclusions/ImportExclusionsRepository.cs index 9c072991b..1af12f95e 100644 --- a/src/NzbDrone.Core/NetImport/ImportExclusions/ImportExclusionsRepository.cs +++ b/src/NzbDrone.Core/NetImport/ImportExclusions/ImportExclusionsRepository.cs @@ -1,6 +1,14 @@ +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 { @@ -12,19 +20,22 @@ namespace NzbDrone.Core.NetImport.ImportExclusions 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(x => x.TmdbId == tmdbid).Any(); + return Query.Where(ex => ex.TmdbId == tmdbid).Any(); } public ImportExclusion GetByTmdbid(int tmdbid) { - return Query(x => x.TmdbId == tmdbid).First(); + return Query.Where(ex => ex.TmdbId == tmdbid).First(); } } } diff --git a/src/NzbDrone.Core/NetImport/NetImportDefinition.cs b/src/NzbDrone.Core/NetImport/NetImportDefinition.cs index ef19e7838..d08b71b7f 100644 --- a/src/NzbDrone.Core/NetImport/NetImportDefinition.cs +++ b/src/NzbDrone.Core/NetImport/NetImportDefinition.cs @@ -1,4 +1,6 @@ using System.Collections.Generic; +using Marr.Data; +using NzbDrone.Core.Profiles; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Movies; @@ -16,6 +18,7 @@ namespace NzbDrone.Core.NetImport 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 75977d7b4..6d0d61464 100644 --- a/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs +++ b/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs @@ -26,7 +26,7 @@ namespace NzbDrone.Core.Parser.Model 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 b50d15ed8..b49546dc6 100644 --- a/src/NzbDrone.Core/Profiles/ProfileRepository.cs +++ b/src/NzbDrone.Core/Profiles/ProfileRepository.cs @@ -17,7 +17,7 @@ namespace NzbDrone.Core.Profiles public bool Exists(int id) { - return Query(x => x.Id == id).Count == 1; + return DataMapper.Query().Where(p => p.Id == id).GetRowCount() == 1; } } } diff --git a/src/NzbDrone.Core/Qualities/QualityDefinitionRepository.cs b/src/NzbDrone.Core/Qualities/QualityDefinitionRepository.cs index 49941710c..8f79230e5 100644 --- a/src/NzbDrone.Core/Qualities/QualityDefinitionRepository.cs +++ b/src/NzbDrone.Core/Qualities/QualityDefinitionRepository.cs @@ -1,4 +1,7 @@ -using NzbDrone.Core.Datastore; +using System.Collections.Generic; +using System.Linq; +using Marr.Data.QGen; +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 8ece998f8..e8e7108e5 100644 --- a/src/NzbDrone.Core/Qualities/Revision.cs +++ b/src/NzbDrone.Core/Qualities/Revision.cs @@ -5,9 +5,8 @@ namespace NzbDrone.Core.Qualities { public class Revision : IEquatable, IComparable { - public Revision() + private 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 01e4f6b27..254dd81ce 100644 --- a/src/NzbDrone.Core/Radarr.Core.csproj +++ b/src/NzbDrone.Core/Radarr.Core.csproj @@ -3,8 +3,6 @@ net462;netcoreapp3.1 - - @@ -17,9 +15,9 @@ - + diff --git a/src/NzbDrone.Core/Tags/TagRepository.cs b/src/NzbDrone.Core/Tags/TagRepository.cs index 037027dea..a35228094 100644 --- a/src/NzbDrone.Core/Tags/TagRepository.cs +++ b/src/NzbDrone.Core/Tags/TagRepository.cs @@ -20,7 +20,7 @@ namespace NzbDrone.Core.Tags public Tag GetByLabel(string label) { - var model = FindByLabel(label); + var model = Query.Where(c => c.Label == label).SingleOrDefault(); if (model == null) { @@ -32,7 +32,7 @@ namespace NzbDrone.Core.Tags public Tag FindByLabel(string label) { - return Query(x => x.Label == label).SingleOrDefault(); + return Query.Where(c => c.Label == label).SingleOrDefault(); } } } diff --git a/src/NzbDrone.Core/ThingiProvider/ProviderRepository.cs b/src/NzbDrone.Core/ThingiProvider/ProviderRepository.cs index a4f03e6c5..1554ef789 100644 --- a/src/NzbDrone.Core/ThingiProvider/ProviderRepository.cs +++ b/src/NzbDrone.Core/ThingiProvider/ProviderRepository.cs @@ -1,52 +1,20 @@ -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.Datastore; using NzbDrone.Core.Messaging.Events; namespace NzbDrone.Core.ThingiProvider { public class ProviderRepository : BasicRepository, IProviderRepository - where TProviderDefinition : ProviderDefinition, new() + where TProviderDefinition : ModelBase, + new() { protected ProviderRepository(IMainDatabase database, IEventAggregator eventAggregator) : base(database, eventAggregator) { } - 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; - } +// public void DeleteImplementations(string implementation) +// { +// DataMapper.Delete(c => c.Implementation == implementation); +// } } -} +} \ 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 4484a09bf..c2782b409 100644 --- a/src/NzbDrone.Core/ThingiProvider/Status/ProviderStatusRepository.cs +++ b/src/NzbDrone.Core/ThingiProvider/Status/ProviderStatusRepository.cs @@ -1,4 +1,7 @@ +using System; +using System.Collections.Generic; using System.Linq; +using System.Text; using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; @@ -21,7 +24,7 @@ namespace NzbDrone.Core.ThingiProvider.Status public TModel FindByProviderId(int providerId) { - return Query(x => x.ProviderId == providerId).SingleOrDefault(); + return Query.Where(c => c.ProviderId == providerId).SingleOrDefault(); } } } diff --git a/src/NzbDrone.Core/Validation/Paths/MovieAncestorValidator.cs b/src/NzbDrone.Core/Validation/Paths/MovieAncestorValidator.cs index ad920cc1d..91df0ffa3 100644 --- a/src/NzbDrone.Core/Validation/Paths/MovieAncestorValidator.cs +++ b/src/NzbDrone.Core/Validation/Paths/MovieAncestorValidator.cs @@ -19,7 +19,7 @@ namespace NzbDrone.Core.Validation.Paths { if (context.PropertyValue == null) return true; - return !_movieService.AllMoviePaths().Any(s => context.PropertyValue.ToString().IsParentPath(s)); + return !_movieService.GetAllMovies().Any(s => context.PropertyValue.ToString().IsParentPath(s.Path)); } } } diff --git a/src/NzbDrone.Host.Test/ContainerFixture.cs b/src/NzbDrone.Host.Test/ContainerFixture.cs index d878cef98..08bc392b1 100644 --- a/src/NzbDrone.Host.Test/ContainerFixture.cs +++ b/src/NzbDrone.Host.Test/ContainerFixture.cs @@ -17,7 +17,6 @@ using NzbDrone.Core.Datastore; using NzbDrone.Core.Download.TrackedDownloads; using NzbDrone.SignalR; using Moq; -using NzbDrone.Core.CustomFormats; namespace NzbDrone.App.Test { @@ -38,11 +37,6 @@ namespace NzbDrone.App.Test // 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 7da8593bb..30809f45f 100644 --- a/src/NzbDrone.Integration.Test/IntegrationTestBase.cs +++ b/src/NzbDrone.Integration.Test/IntegrationTestBase.cs @@ -10,6 +10,7 @@ using NLog.Config; 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; @@ -19,13 +20,17 @@ using Radarr.Api.V3.RootFolders; 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 85a1719f5..ee88e6e2c 100644 --- a/src/NzbDrone.Test.Common/NzbDroneRunner.cs +++ b/src/NzbDrone.Test.Common/NzbDroneRunner.cs @@ -2,7 +2,9 @@ using System; 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; @@ -75,11 +77,11 @@ namespace NzbDrone.Test.Common if (statusCall.ResponseStatus == ResponseStatus.Completed) { - TestContext.Progress.WriteLine("Radarr is started. Running Tests"); + Console.WriteLine("Radarr is started. Running Tests"); return; } - TestContext.Progress.WriteLine("Waiting for Radarr to start. Response Status : {0} [{1}] {2}", statusCall.ResponseStatus, statusCall.StatusDescription, statusCall.ErrorException.Message); + Console.WriteLine("Waiting for Radarr to start. Response Status : {0} [{1}] {2}", statusCall.ResponseStatus, statusCall.StatusDescription, statusCall.ErrorException.Message); Thread.Sleep(500); } @@ -114,7 +116,7 @@ namespace NzbDrone.Test.Common private void OnOutputDataReceived(string data) { - TestContext.Progress.WriteLine(data); + Console.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 f07f8d470..89d2c16f0 100644 --- a/src/Radarr.Api.V3/History/HistoryModule.cs +++ b/src/Radarr.Api.V3/History/HistoryModule.cs @@ -44,7 +44,7 @@ namespace Radarr.Api.V3.History if (model.Movie != null) { - resource.QualityCutoffNotMet = _upgradableSpecification.CutoffNotMet(model.Movie.Profile, model.Quality); + resource.QualityCutoffNotMet = _upgradableSpecification.CutoffNotMet(model.Movie.Profile.Value, model.Quality); } return resource; diff --git a/src/Radarr.Api.V3/Indexers/ReleaseModuleBase.cs b/src/Radarr.Api.V3/Indexers/ReleaseModuleBase.cs index 65ecadc5c..e37307f14 100644 --- a/src/Radarr.Api.V3/Indexers/ReleaseModuleBase.cs +++ b/src/Radarr.Api.V3/Indexers/ReleaseModuleBase.cs @@ -29,7 +29,7 @@ namespace Radarr.Api.V3.Indexers if (decision.RemoteMovie.Movie != null) { release.QualityWeight = decision.RemoteMovie.Movie - .Profile.GetIndex(release.Quality.Quality).Index * 100; + .Profile.Value.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 397514896..2f266671a 100644 --- a/src/Radarr.Api.V3/MovieFiles/MovieFileResource.cs +++ b/src/Radarr.Api.V3/MovieFiles/MovieFileResource.cs @@ -64,7 +64,7 @@ namespace Radarr.Api.V3.MovieFiles Quality = model.Quality, Languages = model.Languages, MediaInfo = model.MediaInfo.ToResource(model.SceneName), - // QualityCutoffNotMet = upgradableSpecification.CutoffNotMet(movie.Profile, model.Quality) + // QualityCutoffNotMet = upgradableSpecification.CutoffNotMet(movie.Profile.Value, model.Quality) }; } @@ -85,7 +85,7 @@ namespace Radarr.Api.V3.MovieFiles Quality = model.Quality, Languages = model.Languages, MediaInfo = model.MediaInfo.ToResource(model.SceneName), - QualityCutoffNotMet = upgradableSpecification.CutoffNotMet(movie.Profile, model.Quality) + QualityCutoffNotMet = upgradableSpecification.CutoffNotMet(movie.Profile.Value, model.Quality) }; } }