1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-11-05 02:22:31 +01:00

Swap to dapper and system.text.json for database backend

This commit is contained in:
ta264 2019-12-15 15:04:42 +00:00
parent 7b17c3e36c
commit d2065bfa1b
155 changed files with 2333 additions and 2340 deletions

View File

@ -1,13 +1,11 @@
using System; using System;
using System.Linq; using System.Linq;
using Nancy; using Nancy;
using Radarr.Http.Extensions;
using NzbDrone.Api.Movies; using NzbDrone.Api.Movies;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.History; using NzbDrone.Core.History;
using Radarr.Http; using Radarr.Http;
using Radarr.Http.REST;
using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.DecisionEngine.Specifications;
namespace NzbDrone.Api.History namespace NzbDrone.Api.History
@ -37,7 +35,7 @@ protected HistoryResource MapToResource(Core.History.History model)
if (model.Movie != null) if (model.Movie != null)
{ {
resource.QualityCutoffNotMet = _qualityUpgradableSpecification.CutoffNotMet(model.Movie.Profile.Value, model.Quality); resource.QualityCutoffNotMet = _qualityUpgradableSpecification.CutoffNotMet(model.Movie.Profile, model.Quality);
} }
return resource; return resource;

View File

@ -29,7 +29,7 @@ protected virtual ReleaseResource MapDecision(DownloadDecision decision, int ini
if (decision.RemoteMovie.Movie != null) if (decision.RemoteMovie.Movie != null)
{ {
release.QualityWeight = decision.RemoteMovie.Movie release.QualityWeight = decision.RemoteMovie.Movie
.Profile.Value .Profile
.Items.FindIndex(v => v.Quality == release.Quality.Quality) * 100; .Items.FindIndex(v => v.Quality == release.Quality.Quality) * 100;
} }

View File

@ -1,19 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Marr.Data;
using Nancy;
using NzbDrone.Api;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.MovieImport;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.MetadataSource;
using NzbDrone.Core.MetadataSource.RadarrAPI; using NzbDrone.Core.MetadataSource.RadarrAPI;
using NzbDrone.Core.Movies.AlternativeTitles; using NzbDrone.Core.Movies.AlternativeTitles;
using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Movies; using NzbDrone.Core.Movies;
using NzbDrone.Core.Movies.Events; using NzbDrone.Core.Movies.Events;
using Radarr.Http; using Radarr.Http;

View File

@ -1,20 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using Marr.Data;
using Nancy;
using NzbDrone.Api;
using NzbDrone.Common.Cache; 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.Messaging.Events;
using NzbDrone.Core.MetadataSource;
using NzbDrone.Core.MetadataSource.RadarrAPI; using NzbDrone.Core.MetadataSource.RadarrAPI;
using NzbDrone.Core.Movies.AlternativeTitles;
using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Movies; using NzbDrone.Core.Movies;
using NzbDrone.Core.Movies.Events; using NzbDrone.Core.Movies.Events;
using Radarr.Http; using Radarr.Http;

View File

@ -1,12 +1,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using Nancy; using Nancy;
using Radarr.Http.Extensions;
using NzbDrone.Core.MediaCover; using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MetadataSource; using NzbDrone.Core.MetadataSource;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using System.Linq; using System.Linq;
using System; using System;
using Marr.Data;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;

View File

@ -7,6 +7,8 @@
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using Radarr.Host; using Radarr.Host;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
using NzbDrone.Core.CustomFormats;
using System.Collections.Generic;
namespace NzbDrone.Common.Test namespace NzbDrone.Common.Test
{ {
@ -20,6 +22,11 @@ public void event_handlers_should_be_unique()
container.Register<IMainDatabase>(new MainDatabase(null)); container.Register<IMainDatabase>(new MainDatabase(null));
container.Resolve<IAppFolderFactory>().Register(); container.Resolve<IAppFolderFactory>().Register();
// A dummy custom format repository since this isn't a DB test
var mockCustomFormat = Mocker.GetMock<ICustomFormatRepository>();
mockCustomFormat.Setup(x => x.All()).Returns(new List<CustomFormatDefinition>());
container.Register<ICustomFormatRepository>(mockCustomFormat.Object);
Mocker.SetConstant(container); Mocker.SetConstant(container);
var handlers = Subject.BuildAll<IHandle<ApplicationStartedEvent>>() var handlers = Subject.BuildAll<IHandle<ApplicationStartedEvent>>()

View File

@ -133,10 +133,6 @@ private static IEnumerable<T> InternalDropLast<T>(IEnumerable<T> source, int n)
yield return buffer.Dequeue(); yield return buffer.Dequeue();
} }
} }
public static bool In<T>(this T source, List<T> list)
{
return list.Contains(source);
}
public static string ConcatToString<TSource>(this IEnumerable<TSource> source, string separator = ", ") public static string ConcatToString<TSource>(this IEnumerable<TSource> source, string separator = ", ")
{ {

View File

@ -5,7 +5,6 @@
using NzbDrone.Core.Organizer; using NzbDrone.Core.Organizer;
using NzbDrone.Core.Movies; using NzbDrone.Core.Movies;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Movies.Events;
using System.Collections.Generic; using System.Collections.Generic;
namespace NzbDrone.Core.Test.BulkImport namespace NzbDrone.Core.Test.BulkImport
@ -33,6 +32,9 @@ public void movies_added_event_should_have_proper_path()
.Setup(s => s.GetMovieFolder(It.IsAny<Movie>(), null)) .Setup(s => s.GetMovieFolder(It.IsAny<Movie>(), null))
.Returns((Movie m, NamingConfig n) => m.Title); .Returns((Movie m, NamingConfig n) => m.Title);
Mocker.GetMock<IMovieRepository>().Setup(s => s.FindByTmdbId(It.IsAny<List<int>>()))
.Returns(new List<Movie>());
var movies = Subject.AddMovies(fakeMovies); var movies = Subject.AddMovies(fakeMovies);
foreach (Movie movie in movies) foreach (Movie movie in movies)
@ -50,7 +52,8 @@ public void movies_added_should_ignore_already_added()
.Setup(s => s.GetMovieFolder(It.IsAny<Movie>(), null)) .Setup(s => s.GetMovieFolder(It.IsAny<Movie>(), null))
.Returns((Movie m, NamingConfig n) => m.Title); .Returns((Movie m, NamingConfig n) => m.Title);
Mocker.GetMock<IMovieRepository>().Setup(s => s.All()).Returns(new List<Movie> { fakeMovies[0] }); Mocker.GetMock<IMovieRepository>().Setup(s => s.FindByTmdbId(It.IsAny<List<int>>()))
.Returns(new List<Movie> { fakeMovies[0] });
var movies = Subject.AddMovies(fakeMovies); var movies = Subject.AddMovies(fakeMovies);
@ -64,12 +67,14 @@ public void movies_added_should_ignore_duplicates()
.Setup(s => s.GetMovieFolder(It.IsAny<Movie>(), null)) .Setup(s => s.GetMovieFolder(It.IsAny<Movie>(), null))
.Returns((Movie m, NamingConfig n) => m.Title); .Returns((Movie m, NamingConfig n) => m.Title);
Mocker.GetMock<IMovieRepository>().Setup(s => s.FindByTmdbId(It.IsAny<List<int>>()))
.Returns(new List<Movie>());
fakeMovies[2].TmdbId = fakeMovies[0].TmdbId; fakeMovies[2].TmdbId = fakeMovies[0].TmdbId;
var movies = Subject.AddMovies(fakeMovies); var movies = Subject.AddMovies(fakeMovies);
Mocker.GetMock<IMovieRepository>().Verify(v => v.InsertMany(It.Is<List<Movie>>(l => l.Count == 2))); Mocker.GetMock<IMovieRepository>().Verify(v => v.InsertMany(It.Is<List<Movie>>(l => l.Count == 2)));
} }
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
@ -15,7 +16,7 @@ public class
BasicRepositoryFixture : DbTest<BasicRepository<ScheduledTask>, ScheduledTask> BasicRepositoryFixture : DbTest<BasicRepository<ScheduledTask>, ScheduledTask>
{ {
private ScheduledTask _basicType; private ScheduledTask _basicType;
private List<ScheduledTask> _basicList;
[SetUp] [SetUp]
public void Setup() public void Setup()
@ -25,15 +26,28 @@ public void Setup()
.With(c => c.Id = 0) .With(c => c.Id = 0)
.With(c => c.LastExecution = DateTime.UtcNow) .With(c => c.LastExecution = DateTime.UtcNow)
.Build(); .Build();
_basicList = Builder<ScheduledTask>
.CreateListOfSize(5)
.All()
.With(x => x.Id = 0)
.BuildList();
} }
[Test] [Test]
public void should_be_able_to_add() public void should_be_able_to_insert()
{ {
Subject.Insert(_basicType); Subject.Insert(_basicType);
Subject.All().Should().HaveCount(1); Subject.All().Should().HaveCount(1);
} }
[Test]
public void should_be_able_to_insert_many()
{
Subject.InsertMany(_basicList);
Subject.All().Should().HaveCount(5);
}
[Test] [Test]
public void purge_should_delete_all() public void purge_should_delete_all()
{ {
@ -47,7 +61,6 @@ public void purge_should_delete_all()
} }
[Test] [Test]
public void should_be_able_to_delete_model() public void should_be_able_to_delete_model()
{ {
@ -58,6 +71,16 @@ public void should_be_able_to_delete_model()
Subject.All().Should().BeEmpty(); 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] [Test]
public void should_be_able_to_find_by_id() public void should_be_able_to_find_by_id()
{ {
@ -67,6 +90,64 @@ public void should_be_able_to_find_by_id()
storeObject.Should().BeEquivalentTo(_basicType, o=>o.IncludingAllRuntimeProperties()); 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] [Test]
public void should_be_able_to_get_single() public void should_be_able_to_get_single()
{ {
@ -86,7 +167,6 @@ public void getting_model_with_invalid_id_should_throw()
Assert.Throws<ModelNotFoundException>(() => Subject.Get(12)); Assert.Throws<ModelNotFoundException>(() => Subject.Get(12));
} }
[Test] [Test]
public void get_all_with_empty_db_should_return_empty_list() public void get_all_with_empty_db_should_return_empty_list()
{ {
@ -98,7 +178,6 @@ public void get_all_with_empty_db_should_return_empty_list()
public void should_be_able_to_call_ToList_on_empty_quariable() public void should_be_able_to_call_ToList_on_empty_quariable()
{ {
Subject.All().ToList().Should().BeEmpty(); Subject.All().ToList().Should().BeEmpty();
} }
} }
} }

View File

@ -1,59 +0,0 @@
using System;
using FluentAssertions;
using Marr.Data.Converters;
using NUnit.Framework;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Converters
{
[TestFixture]
public class BooleanIntConverterFixture : CoreTest<Core.Datastore.Converters.BooleanIntConverter>
{
[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<ConversionException>(() => Subject.FromDB(context));
}
}
}

View File

@ -1,64 +1,51 @@
using System;
using System.Data;
using FluentAssertions;
using Marr.Data.Converters;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore.Converters; using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Movies.Commands; using NzbDrone.Core.Movies.Commands;
using NUnit.Framework;
using FluentAssertions;
using System.Data.SQLite;
namespace NzbDrone.Core.Test.Datastore.Converters namespace NzbDrone.Core.Test.Datastore.Converters
{ {
[TestFixture] [TestFixture]
public class CommandConverterFixture : CoreTest<CommandConverter> public class CommandConverterFixture : CoreTest<CommandConverter>
{ {
SQLiteParameter param;
[SetUp]
public void Setup()
{
param = new SQLiteParameter();
}
[Test] [Test]
public void should_return_json_string_when_saving_boolean_to_db() public void should_return_json_string_when_saving_boolean_to_db()
{ {
var command = new RefreshMovieCommand(); var command = new RefreshMovieCommand();
Subject.ToDB(command).Should().BeOfType<string>(); Subject.SetValue(param, command);
param.Value.Should().BeOfType<string>();
} }
[Test] [Test]
public void should_return_null_for_null_value_when_saving_to_db() public void should_return_null_for_null_value_when_saving_to_db()
{ {
Subject.ToDB(null).Should().Be(null); Subject.SetValue(param, null);
} param.Value.Should().BeNull();
[Test]
public void should_return_db_null_for_db_null_value_when_saving_to_db()
{
Subject.ToDB(DBNull.Value).Should().Be(DBNull.Value);
} }
[Test] [Test]
public void should_return_command_when_getting_json_from_db() public void should_return_command_when_getting_json_from_db()
{ {
var dataRecordMock = new Mock<IDataRecord>(); var data = "{\"name\": \"RefreshMovie\"}";
dataRecordMock.Setup(s => s.GetOrdinal("Name")).Returns(0);
dataRecordMock.Setup(s => s.GetString(0)).Returns("RefreshMovie");
var context = new ConverterContext Subject.Parse(data).Should().BeOfType<RefreshMovieCommand>();
{
DataRecord = dataRecordMock.Object,
DbValue = new RefreshMovieCommand().ToJson()
};
Subject.FromDB(context).Should().BeOfType<RefreshMovieCommand>();
} }
[Test] [Test]
public void should_return_null_for_null_value_when_getting_from_db() public void should_return_null_for_null_value_when_getting_from_db()
{ {
var context = new ConverterContext Subject.Parse(null).Should().BeNull();
{
DbValue = DBNull.Value
};
Subject.FromDB(context).Should().Be(null);
} }
} }
} }

View File

@ -0,0 +1,41 @@
using System.Collections.Generic;
using System.Data.SQLite;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Converters
{
[TestFixture]
public class DictionaryConverterFixture : CoreTest<EmbeddedDocumentConverter<Dictionary<string, string>>>
{
SQLiteParameter param;
[SetUp]
public void Setup()
{
param = new SQLiteParameter();
}
[Test]
public void should_serialize_in_camel_case()
{
var dict = new Dictionary<string, string>
{
{ "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");
}
}
}

View File

@ -1,70 +0,0 @@
using System;
using FluentAssertions;
using Marr.Data.Converters;
using NUnit.Framework;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Converters
{
[TestFixture]
public class DoubleConverterFixture : CoreTest<DoubleConverter>
{
[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);
}
}
}

View File

@ -1,58 +0,0 @@
using System;
using System.Reflection;
using FluentAssertions;
using Marr.Data.Converters;
using Marr.Data.Mapping;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Download.Pending;
namespace NzbDrone.Core.Test.Datastore.Converters
{
[TestFixture]
public class EnumIntConverterFixture : CoreTest<Core.Datastore.Converters.EnumIntConverter>
{
[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<MemberInfo>();
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);
}
}
}

View File

@ -1,6 +1,6 @@
using System; using System;
using System.Data.SQLite;
using FluentAssertions; using FluentAssertions;
using Marr.Data.Converters;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Datastore.Converters; using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -10,18 +10,21 @@ namespace NzbDrone.Core.Test.Datastore.Converters
[TestFixture] [TestFixture]
public class GuidConverterFixture : CoreTest<GuidConverter> public class GuidConverterFixture : CoreTest<GuidConverter>
{ {
SQLiteParameter param;
[SetUp]
public void Setup()
{
param = new SQLiteParameter();
}
[Test] [Test]
public void should_return_string_when_saving_guid_to_db() public void should_return_string_when_saving_guid_to_db()
{ {
var guid = Guid.NewGuid(); var guid = Guid.NewGuid();
Subject.ToDB(guid).Should().Be(guid.ToString()); Subject.SetValue(param, guid);
} param.Value.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] [Test]
@ -29,23 +32,13 @@ public void should_return_guid_when_getting_string_from_db()
{ {
var guid = Guid.NewGuid(); var guid = Guid.NewGuid();
var context = new ConverterContext Subject.Parse(guid.ToString()).Should().Be(guid);
{
DbValue = guid.ToString()
};
Subject.FromDB(context).Should().Be(guid);
} }
[Test] [Test]
public void should_return_empty_guid_for_db_null_value_when_getting_from_db() public void should_return_empty_guid_for_db_null_value_when_getting_from_db()
{ {
var context = new ConverterContext Subject.Parse(null).Should().Be(Guid.Empty);
{
DbValue = DBNull.Value
};
Subject.FromDB(context).Should().Be(Guid.Empty);
} }
} }
} }

View File

@ -1,58 +0,0 @@
using System;
using FluentAssertions;
using Marr.Data.Converters;
using NUnit.Framework;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Converters
{
[TestFixture]
public class Int32ConverterFixture : CoreTest<Int32Converter>
{
[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);
}
}
}

View File

@ -1,6 +1,5 @@
using System; using System.Data.SQLite;
using FluentAssertions; using FluentAssertions;
using Marr.Data.Converters;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Core.Datastore.Converters; using NzbDrone.Core.Datastore.Converters;
@ -12,13 +11,22 @@ namespace NzbDrone.Core.Test.Datastore.Converters
[TestFixture] [TestFixture]
public class OsPathConverterFixture : CoreTest<OsPathConverter> public class OsPathConverterFixture : CoreTest<OsPathConverter>
{ {
SQLiteParameter param;
[SetUp]
public void Setup()
{
param = new SQLiteParameter();
}
[Test] [Test]
public void should_return_string_when_saving_os_path_to_db() public void should_return_string_when_saving_os_path_to_db()
{ {
var path = @"C:\Test\TV".AsOsAgnostic(); var path = @"C:\Test\TV".AsOsAgnostic();
var osPath = new OsPath(path); var osPath = new OsPath(path);
Subject.ToDB(osPath).Should().Be(path); Subject.SetValue(param, osPath);
param.Value.Should().Be(path);
} }
[Test] [Test]
@ -27,23 +35,13 @@ public void should_return_os_path_when_getting_string_from_db()
var path = @"C:\Test\TV".AsOsAgnostic(); var path = @"C:\Test\TV".AsOsAgnostic();
var osPath = new OsPath(path); var osPath = new OsPath(path);
var context = new ConverterContext Subject.Parse(path).Should().Be(osPath);
{
DbValue = path
};
Subject.FromDB(context).Should().Be(osPath);
} }
[Test] [Test]
public void should_return_db_null_for_db_null_value_when_getting_from_db() public void should_return_empty_for_null_value_when_getting_from_db()
{ {
var context = new ConverterContext Subject.Parse(null).IsEmpty.Should().BeTrue();
{
DbValue = DBNull.Value
};
Subject.FromDB(context).Should().Be(DBNull.Value);
} }
} }
} }

View File

@ -1,6 +1,5 @@
using System; using System.Data.SQLite;
using FluentAssertions; using FluentAssertions;
using Marr.Data.Converters;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Datastore.Converters; using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -8,32 +7,29 @@
namespace NzbDrone.Core.Test.Datastore.Converters namespace NzbDrone.Core.Test.Datastore.Converters
{ {
[Ignore("To reinstate once dapper changes worked out")]
[TestFixture] [TestFixture]
public class ProviderSettingConverterFixture : CoreTest<ProviderSettingConverter> public class ProviderSettingConverterFixture : CoreTest<ProviderSettingConverter>
{ {
SQLiteParameter param;
[SetUp]
public void Setup()
{
param = new SQLiteParameter();
}
[Test] [Test]
public void should_return_null_config_if_config_is_null() public void should_return_null_config_if_config_is_null()
{ {
var result = Subject.FromDB(new ConverterContext() Subject.Parse(null).Should().Be(NullConfig.Instance);
{
DbValue = DBNull.Value
});
result.Should().Be(NullConfig.Instance);
} }
[TestCase(null)] [TestCase(null)]
[TestCase("")] [TestCase("")]
public void should_return_null_config_if_config_is_empty(object dbValue) public void should_return_null_config_if_config_is_empty(object dbValue)
{ {
var result = Subject.FromDB(new ConverterContext() Subject.Parse(dbValue).Should().Be(NullConfig.Instance);
{
DbValue = dbValue
});
result.Should().Be(NullConfig.Instance);
} }
} }
} }

View File

@ -1,6 +1,5 @@
using System; using System.Data.SQLite;
using FluentAssertions; using FluentAssertions;
using Marr.Data.Converters;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Datastore.Converters; using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
@ -9,26 +8,30 @@
namespace NzbDrone.Core.Test.Datastore.Converters namespace NzbDrone.Core.Test.Datastore.Converters
{ {
[TestFixture] [TestFixture]
public class QualityIntConverterFixture : CoreTest<QualityIntConverter> public class QualityIntConverterFixture : CoreTest<DapperQualityIntConverter>
{ {
SQLiteParameter param;
[SetUp]
public void Setup()
{
param = new SQLiteParameter();
}
[Test] [Test]
public void should_return_int_when_saving_quality_to_db() public void should_return_int_when_saving_quality_to_db()
{ {
var quality = Quality.Bluray1080p; var quality = Quality.Bluray1080p;
Subject.ToDB(quality).Should().Be(quality.Id); Subject.SetValue(param, quality);
param.Value.Should().Be(quality.Id);
} }
[Test] [Test]
public void should_return_0_when_saving_db_null_to_db() public void should_return_0_when_saving_db_null_to_db()
{ {
Subject.ToDB(DBNull.Value).Should().Be(0); Subject.SetValue(param, null);
} param.Value.Should().Be(0);
[Test]
public void should_throw_when_saving_another_object_to_db()
{
Assert.Throws<InvalidOperationException>(() => Subject.ToDB("Not a quality"));
} }
[Test] [Test]
@ -36,23 +39,13 @@ public void should_return_quality_when_getting_string_from_db()
{ {
var quality = Quality.Bluray1080p; var quality = Quality.Bluray1080p;
var context = new ConverterContext Subject.Parse(quality.Id).Should().Be(quality);
{
DbValue = quality.Id
};
Subject.FromDB(context).Should().Be(quality);
} }
[Test] [Test]
public void should_return_db_null_for_db_null_value_when_getting_from_db() public void should_return_unknown_for_null_value_when_getting_from_db()
{ {
var context = new ConverterContext Subject.Parse(null).Should().Be(Quality.Unknown);
{
DbValue = DBNull.Value
};
Subject.FromDB(context).Should().Be(Quality.Unknown);
} }
} }
} }

View File

@ -1,65 +0,0 @@
using System;
using System.Globalization;
using FluentAssertions;
using Marr.Data.Converters;
using NUnit.Framework;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Converters
{
[TestFixture]
public class TimeSpanConverterFixture : CoreTest<TimeSpanConverter>
{
[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);
}
}
}

View File

@ -1,6 +1,6 @@
using System; using System;
using System.Data.SQLite;
using FluentAssertions; using FluentAssertions;
using Marr.Data.Converters;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Datastore.Converters; using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -8,20 +8,23 @@
namespace NzbDrone.Core.Test.Datastore.Converters namespace NzbDrone.Core.Test.Datastore.Converters
{ {
[TestFixture] [TestFixture]
public class UtcConverterFixture : CoreTest<UtcConverter> public class UtcConverterFixture : CoreTest<DapperUtcConverter>
{ {
SQLiteParameter param;
[SetUp]
public void Setup()
{
param = new SQLiteParameter();
}
[Test] [Test]
public void should_return_date_time_when_saving_date_time_to_db() public void should_return_date_time_when_saving_date_time_to_db()
{ {
var dateTime = DateTime.Now; var dateTime = DateTime.Now;
Subject.ToDB(dateTime).Should().Be(dateTime.ToUniversalTime()); Subject.SetValue(param, dateTime);
} param.Value.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] [Test]
@ -29,23 +32,7 @@ public void should_return_time_span_when_getting_time_span_from_db()
{ {
var dateTime = DateTime.Now.ToUniversalTime(); var dateTime = DateTime.Now.ToUniversalTime();
var context = new ConverterContext Subject.Parse(dateTime).Should().Be(dateTime);
{
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);
} }
} }
} }

View File

@ -1,10 +1,11 @@
using System; using System;
using System.Linq; using System.Linq;
using Dapper;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Movies; using NzbDrone.Core.Movies;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore namespace NzbDrone.Core.Test.Datastore
{ {
@ -14,15 +15,15 @@ public class DatabaseFixture : DbTest
public void SingleOrDefault_should_return_null_on_empty_db() public void SingleOrDefault_should_return_null_on_empty_db()
{ {
Mocker.Resolve<IDatabase>() Mocker.Resolve<IDatabase>()
.GetDataMapper().Query<Movie>() .OpenConnection().Query<Movie>("SELECT * FROM Movies")
.SingleOrDefault(c => c.CleanTitle == "SomeTitle") .SingleOrDefault()
.Should() .Should()
.BeNull(); .BeNull();
} }
[Test] [Test]
public void vaccume() public void vacuum()
{ {
Mocker.Resolve<IDatabase>().Vacuum(); Mocker.Resolve<IDatabase>().Vacuum();
} }

View File

@ -2,10 +2,8 @@
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Movies;
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Core.Languages; using NzbDrone.Core.Languages;
@ -20,48 +18,6 @@ public void Setup()
// This is kinda hacky here, since we are kinda testing if the QualityDef converter works as well. // 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<MovieFile>.CreateNew()
.With(c => c.Quality = new QualityModel())
.BuildNew();
Db.Insert(episodeFile);
var episode = Builder<Movie>.CreateNew()
.With(c => c.MovieFileId = episodeFile.Id)
.BuildNew();
Db.Insert(episode);
var loadedEpisode = Db.Single<Movie>();
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<Movie>.CreateNew()
.With(c => c.MovieFileId = 0)
.BuildNew();
Db.Insert(episode);
Db.Single<Movie>().MovieFile.Should().BeNull();
}
[Test] [Test]
public void embedded_document_as_json() public void embedded_document_as_json()
{ {

View File

@ -1,55 +0,0 @@
using FizzWare.NBuilder;
using NUnit.Framework;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.MediaFiles;
namespace NzbDrone.Core.Test.Datastore
{
[TestFixture]
public class MarrDataLazyLoadingFixture : DbTest
{
[SetUp]
public void Setup()
{
var profile = new Profile
{
Name = "Test",
Cutoff = Quality.WEBDL720p.Id,
Items = Qualities.QualityFixture.GetDefaultQualities()
};
profile = Db.Insert(profile);
var series = Builder<Movie>.CreateListOfSize(1)
.All()
.With(v => v.ProfileId = profile.Id)
.BuildListOfNew();
Db.InsertMany(series);
var episodeFiles = Builder<MovieFile>.CreateListOfSize(1)
.All()
.With(v => v.MovieId = series[0].Id)
.With(v => v.Quality = new QualityModel())
.BuildListOfNew();
Db.InsertMany(episodeFiles);
var episodes = Builder<Movie>.CreateListOfSize(10)
.All()
.With(v => v.Monitored = true)
.With(v => v.MovieFileId = episodeFiles[0].Id)
.BuildListOfNew();
Db.InsertMany(episodes);
}
}
}

View File

@ -1,29 +0,0 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore.Extensions;
using NzbDrone.Core.Movies;
namespace NzbDrone.Core.Test.Datastore.PagingSpecExtensionsTests
{
public class PagingOffsetFixture
{
[TestCase(1, 10, 0)]
[TestCase(2, 10, 10)]
[TestCase(3, 20, 40)]
[TestCase(1, 100, 0)]
public void should_calcuate_expected_offset(int page, int pageSize, int expected)
{
var pagingSpec = new PagingSpec<Movie>
{
Page = page,
PageSize = pageSize,
SortDirection = SortDirection.Ascending,
SortKey = "AirDate"
};
pagingSpec.PagingOffset().Should().Be(expected);
}
}
}

View File

@ -1,53 +0,0 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore.Extensions;
using NzbDrone.Core.Movies;
namespace NzbDrone.Core.Test.Datastore.PagingSpecExtensionsTests
{
public class ToSortDirectionFixture
{
[Test]
public void should_convert_default_to_asc()
{
var pagingSpec = new PagingSpec<Movie>
{
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<Movie>
{
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<Movie>
{
Page = 1,
PageSize = 10,
SortDirection = SortDirection.Descending,
SortKey = "AirDate"
};
pagingSpec.ToSortDirection().Should().Be(Marr.Data.QGen.SortDirection.Desc);
}
}
}

View File

@ -1,40 +0,0 @@
using NUnit.Framework;
namespace NzbDrone.Core.Test.Datastore.ReflectionStrategyFixture
{
[TestFixture]
public class Benchmarks
{
/* private const int iterations = 5000000;
private object _target;
private IReflectionStrategy _simpleReflectionStrategy;
[SetUp]
public void Setup()
{
// _simpleReflectionStrategy = new DelegateReflectionStrategy();
}
[Test]
public void clr_reflection_test()
{
_target = new Series();
var del = _simpleReflectionStrategy.BuildSetter(typeof(Series), "Title");
for (int i = 0; i < iterations; i++)
{
del(_target, "TestTile");
//_simpleReflectionStrategy.SetFieldValue(_target, "Title", "TestTile");
}
}
private void SetField()
{
}*/
}
}

View File

@ -1,16 +1,15 @@
using System.Collections.Generic; using System.Collections.Generic;
using Dapper;
using FluentAssertions; using FluentAssertions;
using Marr.Data;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore.Converters; using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Datastore.Extensions;
using NzbDrone.Core.Movies; using NzbDrone.Core.Movies;
namespace NzbDrone.Core.Test.Datastore namespace NzbDrone.Core.Test.Datastore
{ {
[TestFixture] [TestFixture]
public class MappingExtensionFixture public class TableMapperFixture
{ {
public class EmbeddedType : IEmbeddedDocument public class EmbeddedType : IEmbeddedDocument
@ -39,19 +38,16 @@ public class TypeWithNoMappableProperties
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
MapRepository.Instance.RegisterTypeConverter(typeof(List<EmbeddedType>), new EmbeddedDocumentConverter()); SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<EmbeddedType>>());
MapRepository.Instance.RegisterTypeConverter(typeof(EmbeddedType), new EmbeddedDocumentConverter()); SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<EmbeddedType>());
MapRepository.Instance.RegisterTypeConverter(typeof(int), new Int32Converter());
} }
[Test] [Test]
public void test_mappable_types() public void test_mappable_types()
{ {
var properties = typeof(TypeWithAllMappableProperties).GetProperties(); var properties = typeof(TypeWithAllMappableProperties).GetProperties();
properties.Should().NotBeEmpty(); properties.Should().NotBeEmpty();
properties.Should().OnlyContain(c => MappingExtensions.IsMappableProperty(c)); properties.Should().OnlyContain(c => ColumnMapper<int>.IsMappableProperty(c));
} }
[Test] [Test]
@ -59,7 +55,7 @@ public void test_un_mappable_types()
{ {
var properties = typeof(TypeWithNoMappableProperties).GetProperties(); var properties = typeof(TypeWithNoMappableProperties).GetProperties();
properties.Should().NotBeEmpty(); properties.Should().NotBeEmpty();
properties.Should().NotContain(c => MappingExtensions.IsMappableProperty(c)); properties.Should().NotContain(c => ColumnMapper<int>.IsMappableProperty(c));
} }
} }
} }

View File

@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore
{
[TestFixture]
public class WhereBuilderFixture : CoreTest
{
private WhereBuilder Subject;
[OneTimeSetUp]
public void MapTables()
{
// Generate table mapping
Mocker.Resolve<DbFactory>();
}
private WhereBuilder Where(Expression<Func<Movie, bool>> 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<int>(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<int>(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<string>(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<string>(name).Should().Be(test);
}
[Test]
public void where_in_list()
{
var list = new List<int> {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<List<int>>(name);
param.Should().BeEquivalentTo(list);
}
[Test]
public void where_in_list_2()
{
var list = new List<int> {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> { 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})");
}
}
}

View File

@ -1,7 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Marr.Data;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@ -33,7 +32,7 @@ public void Setup()
var fakeSeries = Builder<Movie>.CreateNew() var fakeSeries = Builder<Movie>.CreateNew()
.With(c => c.Profile = (LazyLoaded<Profile>)new Profile { Cutoff = Quality.Bluray1080p.Id }) .With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p.Id })
.Build(); .Build();
remoteMovie = new RemoteMovie remoteMovie = new RemoteMovie
@ -49,7 +48,7 @@ public void Setup()
public void should_allow_if_format_is_defined_in_profile() public void should_allow_if_format_is_defined_in_profile()
{ {
remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<CustomFormats.CustomFormat> {_format1}; remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<CustomFormats.CustomFormat> {_format1};
remoteMovie.Movie.Profile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name);
Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeTrue();
} }
@ -58,7 +57,7 @@ public void should_allow_if_format_is_defined_in_profile()
public void should_deny_if_format_is_defined_in_profile() public void should_deny_if_format_is_defined_in_profile()
{ {
remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<CustomFormats.CustomFormat> {_format2}; remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<CustomFormats.CustomFormat> {_format2};
remoteMovie.Movie.Profile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name);
Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeFalse(); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeFalse();
} }
@ -67,7 +66,7 @@ public void should_deny_if_format_is_defined_in_profile()
public void should_deny_if_one_format_is_defined_in_profile() public void should_deny_if_one_format_is_defined_in_profile()
{ {
remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<CustomFormats.CustomFormat> {_format2, _format1}; remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<CustomFormats.CustomFormat> {_format2, _format1};
remoteMovie.Movie.Profile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name);
Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeFalse(); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeFalse();
} }
@ -76,7 +75,7 @@ public void should_deny_if_one_format_is_defined_in_profile()
public void should_allow_if_all_format_is_defined_in_profile() public void should_allow_if_all_format_is_defined_in_profile()
{ {
remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<CustomFormats.CustomFormat> {_format2, _format1}; remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<CustomFormats.CustomFormat> {_format2, _format1};
remoteMovie.Movie.Profile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name); remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name);
Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeTrue();
} }
@ -85,7 +84,7 @@ public void should_allow_if_all_format_is_defined_in_profile()
public void should_deny_if_no_format_was_parsed_and_none_not_in_profile() public void should_deny_if_no_format_was_parsed_and_none_not_in_profile()
{ {
remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<CustomFormats.CustomFormat> {}; remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<CustomFormats.CustomFormat> {};
remoteMovie.Movie.Profile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name); remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name);
Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeFalse(); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeFalse();
} }
@ -94,7 +93,7 @@ public void should_deny_if_no_format_was_parsed_and_none_not_in_profile()
public void should_allow_if_no_format_was_parsed_and_none_in_profile() public void should_allow_if_no_format_was_parsed_and_none_in_profile()
{ {
remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<CustomFormats.CustomFormat> {}; remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<CustomFormats.CustomFormat> {};
remoteMovie.Movie.Profile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(CustomFormats.CustomFormat.None.Name, _format1.Name, _format2.Name); remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(CustomFormats.CustomFormat.None.Name, _format1.Name, _format2.Name);
Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeTrue();
} }

View File

@ -1,9 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using FluentAssertions; using FluentAssertions;
using Marr.Data;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -29,10 +27,10 @@ public void Setup()
}, },
Movie = new Movie Movie = new Movie
{ {
Profile = new LazyLoaded<Profile>(new Profile Profile = new Profile
{ {
Language = Language.English Language = Language.English
}) }
} }
}; };
} }
@ -66,10 +64,10 @@ public void should_return_false_if_language_is_german()
[Test] [Test]
public void should_return_true_if_allowed_language_any() public void should_return_true_if_allowed_language_any()
{ {
_remoteMovie.Movie.Profile = new LazyLoaded<Profile>(new Profile _remoteMovie.Movie.Profile = new Profile
{ {
Language = Language.Any Language = Language.Any
}); };
WithGermanRelease(); WithGermanRelease();

View File

@ -1,6 +1,5 @@
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Marr.Data;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@ -35,7 +34,7 @@ public class QualityAllowedByProfileSpecificationFixture : CoreTest<QualityAllow
public void Setup() public void Setup()
{ {
var fakeSeries = Builder<Movie>.CreateNew() var fakeSeries = Builder<Movie>.CreateNew()
.With(c => c.Profile = (LazyLoaded<Profile>)new Profile { Cutoff = Quality.Bluray1080p.Id }) .With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p.Id })
.Build(); .Build();
remoteMovie = new RemoteMovie remoteMovie = new RemoteMovie
@ -49,7 +48,7 @@ public void Setup()
public void should_allow_if_quality_is_defined_in_profile(Quality qualityType) public void should_allow_if_quality_is_defined_in_profile(Quality qualityType)
{ {
remoteMovie.ParsedMovieInfo.Quality.Quality = qualityType; remoteMovie.ParsedMovieInfo.Quality.Quality = qualityType;
remoteMovie.Movie.Profile.Value.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p); remoteMovie.Movie.Profile.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p);
Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeTrue();
} }
@ -58,7 +57,7 @@ public void should_allow_if_quality_is_defined_in_profile(Quality qualityType)
public void should_not_allow_if_quality_is_not_defined_in_profile(Quality qualityType) public void should_not_allow_if_quality_is_not_defined_in_profile(Quality qualityType)
{ {
remoteMovie.ParsedMovieInfo.Quality.Quality = qualityType; remoteMovie.ParsedMovieInfo.Quality.Quality = qualityType;
remoteMovie.Movie.Profile.Value.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p); remoteMovie.Movie.Profile.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p);
Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeFalse(); Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeFalse();
} }

View File

@ -83,7 +83,7 @@ public void should_return_true_when_movie_doesnt_match()
[Test] [Test]
public void should_return_true_when_quality_in_queue_is_lower() public void should_return_true_when_quality_in_queue_is_lower()
{ {
_movie.Profile.Value.Cutoff = Quality.Bluray1080p.Id; _movie.Profile.Cutoff = Quality.Bluray1080p.Id;
var remoteMovie = Builder<RemoteMovie>.CreateNew() var remoteMovie = Builder<RemoteMovie>.CreateNew()
.With(r => r.Movie = _movie) .With(r => r.Movie = _movie)
@ -115,7 +115,7 @@ public void should_return_false_when_qualities_are_the_same()
[Test] [Test]
public void should_return_false_when_quality_in_queue_is_better() public void should_return_false_when_quality_in_queue_is_better()
{ {
_movie.Profile.Value.Cutoff = Quality.Bluray1080p.Id; _movie.Profile.Cutoff = Quality.Bluray1080p.Id;
var remoteMovie = Builder<RemoteMovie>.CreateNew() var remoteMovie = Builder<RemoteMovie>.CreateNew()
.With(r => r.Movie = _movie) .With(r => r.Movie = _movie)
@ -132,7 +132,7 @@ public void should_return_false_when_quality_in_queue_is_better()
[Test] [Test]
public void should_return_false_if_quality_in_queue_meets_cutoff() public void should_return_false_if_quality_in_queue_meets_cutoff()
{ {
_movie.Profile.Value.Cutoff = _remoteMovie.ParsedMovieInfo.Quality.Quality.Id; _movie.Profile.Cutoff = _remoteMovie.ParsedMovieInfo.Quality.Quality.Id;
var remoteMovie = Builder<RemoteMovie>.CreateNew() var remoteMovie = Builder<RemoteMovie>.CreateNew()
.With(r => r.Movie = _movie) .With(r => r.Movie = _movie)
@ -151,8 +151,8 @@ public void should_return_false_if_quality_in_queue_meets_cutoff()
[Test] [Test]
public void should_return_false_when_quality_is_better_and_upgrade_allowed_is_false_for_quality_profile() public void should_return_false_when_quality_is_better_and_upgrade_allowed_is_false_for_quality_profile()
{ {
_movie.Profile.Value.Cutoff = Quality.Bluray1080p.Id; _movie.Profile.Cutoff = Quality.Bluray1080p.Id;
_movie.Profile.Value.UpgradeAllowed = false; _movie.Profile.UpgradeAllowed = false;
var remoteMovie = Builder<RemoteMovie>.CreateNew() var remoteMovie = Builder<RemoteMovie>.CreateNew()
.With(r => r.Movie = _movie) .With(r => r.Movie = _movie)

View File

@ -1,9 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Marr.Data;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.DecisionEngine.Specifications;
@ -78,7 +76,7 @@ private void GivenExistingFile(QualityModel quality)
// Quality = quality // Quality = quality
// }); // });
_remoteEpisode.Movie.MovieFile = new LazyLoaded<MovieFile>(new MovieFile { Quality = quality }); _remoteEpisode.Movie.MovieFile = new MovieFile { Quality = quality };
} }
private void GivenUpgradeForExistingFile() private void GivenUpgradeForExistingFile()

View File

@ -50,8 +50,8 @@ public void SetUp()
private void GivenMovies(params Movie[] movies) private void GivenMovies(params Movie[] movies)
{ {
Mocker.GetMock<IMovieService>() Mocker.GetMock<IMovieService>()
.Setup(v => v.GetAllMovies()) .Setup(v => v.AllMoviePaths())
.Returns(movies.ToList()); .Returns(movies.Select(x => x.Path).ToList());
} }
private void GivenExistingFolder(string folder) private void GivenExistingFolder(string folder)

View File

@ -1,13 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using Marr.Data;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download.Pending; using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
@ -46,7 +44,7 @@ public void Setup()
}, },
}; };
_movie.Profile = new LazyLoaded<Profile>(_profile); _movie.Profile = _profile;
_release = Builder<ReleaseInfo>.CreateNew().Build(); _release = Builder<ReleaseInfo>.CreateNew().Build();

View File

@ -1,19 +1,17 @@
using System.Collections.Generic; 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.DecisionEngine;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Pending; using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Movies; using NzbDrone.Core.Movies;
using System.Linq; using System.Linq;
using FizzWare.NBuilder;
using Moq;
using NzbDrone.Common.Extensions;
using NUnit.Framework;
namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
{ {
@ -46,7 +44,7 @@ public void Setup()
}, },
}; };
_movie.Profile = new LazyLoaded<Profile>(_profile); _movie.Profile = _profile;
_release = Builder<ReleaseInfo>.CreateNew().Build(); _release = Builder<ReleaseInfo>.CreateNew().Build();

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using Marr.Data;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
@ -47,7 +46,7 @@ public void Setup()
}, },
}; };
_movie.Profile = new LazyLoaded<Profile>(_profile); _movie.Profile = _profile;
_release = Builder<ReleaseInfo>.CreateNew().Build(); _release = Builder<ReleaseInfo>.CreateNew().Build();

View File

@ -1,10 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data.SQLite; using System.Data.SQLite;
using System.IO;
using System.Linq; using System.Linq;
using FluentMigrator.Runner;
using Marr.Data;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using NUnit.Framework; using NUnit.Framework;
@ -111,7 +108,7 @@ protected void SetupContainer()
Mocker.SetConstant<IConnectionStringFactory>(Mocker.Resolve<ConnectionStringFactory>()); Mocker.SetConstant<IConnectionStringFactory>(Mocker.Resolve<ConnectionStringFactory>());
Mocker.SetConstant<IMigrationController>(Mocker.Resolve<MigrationController>()); Mocker.SetConstant<IMigrationController>(Mocker.Resolve<MigrationController>());
MapRepository.Instance.EnableTraceLogging = true; SqlBuilderExtensions.LogSql = true;
} }
[SetUp] [SetUp]

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
using System.Data.Common;
using System.Linq; using System.Linq;
using NzbDrone.Common.Serializer; using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
@ -16,27 +15,16 @@ public interface IDirectDataMapper
public class DirectDataMapper : IDirectDataMapper public class DirectDataMapper : IDirectDataMapper
{ {
private readonly DbProviderFactory _providerFactory; private readonly IDatabase _database;
private readonly string _connectionString;
public DirectDataMapper(IDatabase database) public DirectDataMapper(IDatabase database)
{ {
var dataMapper = database.GetDataMapper(); _database = database;
_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) public DataTable GetDataTable(string sql)
{ {
using (var connection = OpenConnection()) using (var connection = _database.OpenConnection())
{ {
using (var cmd = connection.CreateCommand()) using (var cmd = connection.CreateCommand())
{ {

View File

@ -20,8 +20,8 @@ private void GivenMissingRootFolder()
.ToList(); .ToList();
Mocker.GetMock<IMovieService>() Mocker.GetMock<IMovieService>()
.Setup(s => s.GetAllMovies()) .Setup(s => s.AllMoviePaths())
.Returns(movies); .Returns(movies.Select(x => x.Path).ToList());
Mocker.GetMock<IDiskProvider>() Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetParentFolder(movies.First().Path)) .Setup(s => s.GetParentFolder(movies.First().Path))
@ -36,8 +36,8 @@ private void GivenMissingRootFolder()
public void should_not_return_error_when_no_movie() public void should_not_return_error_when_no_movie()
{ {
Mocker.GetMock<IMovieService>() Mocker.GetMock<IMovieService>()
.Setup(s => s.GetAllMovies()) .Setup(s => s.AllMoviePaths())
.Returns(new List<Movie>()); .Returns(new List<string>());
Subject.Check().ShouldBeOk(); Subject.Check().ShouldBeOk();
} }

View File

@ -1,4 +1,5 @@
using FizzWare.NBuilder; using System;
using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Authentication; using NzbDrone.Core.Authentication;
@ -14,8 +15,12 @@ public class CleanupAdditionalUsersFixture : DbTest<CleanupAdditionalUsers, User
public void should_delete_additional_users() public void should_delete_additional_users()
{ {
var specs = Builder<User>.CreateListOfSize(5) var specs = Builder<User>.CreateListOfSize(5)
.All()
.With(x => x.Id = 0)
.BuildListOfNew(); .BuildListOfNew();
specs.ForEach(x => x.Identifier = Guid.NewGuid());
Db.InsertMany(specs); Db.InsertMany(specs);
Subject.Clean(); Subject.Clean();
@ -26,6 +31,8 @@ public void should_delete_additional_users()
public void should_not_delete_if_only_one_user() public void should_not_delete_if_only_one_user()
{ {
var spec = Builder<User>.CreateNew() var spec = Builder<User>.CreateNew()
.With(x => x.Id = 0)
.With(x => x.Identifier = Guid.NewGuid())
.BuildNew(); .BuildNew();
Db.Insert(spec); Db.Insert(spec);

View File

@ -14,7 +14,10 @@ public class CleanupUnusedTagsFixture : DbTest<CleanupUnusedTags, Tag>
[Test] [Test]
public void should_delete_unused_tags() public void should_delete_unused_tags()
{ {
var tags = Builder<Tag>.CreateListOfSize(2).BuildList(); var tags = Builder<Tag>.CreateListOfSize(2)
.All()
.With(x => x.Id = 0)
.BuildList();
Db.InsertMany(tags); Db.InsertMany(tags);
Subject.Clean(); Subject.Clean();
@ -24,11 +27,15 @@ public void should_delete_unused_tags()
[Test] [Test]
public void should_not_delete_used_tags() public void should_not_delete_used_tags()
{ {
var tags = Builder<Tag>.CreateListOfSize(2).BuildList(); var tags = Builder<Tag>.CreateListOfSize(2)
.All()
.With(x => x.Id = 0)
.BuildList();
Db.InsertMany(tags); Db.InsertMany(tags);
var restrictions = Builder<Restriction>.CreateListOfSize(2) var restrictions = Builder<Restriction>.CreateListOfSize(2)
.All() .All()
.With(v => v.Id = 0)
.With(v => v.Tags.Add(tags[0].Id)) .With(v => v.Tags.Add(tags[0].Id))
.BuildList(); .BuildList();
Db.InsertMany(restrictions); Db.InsertMany(restrictions);

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Threading; using System.Threading;
using FluentAssertions; using FluentAssertions;
using Marr.Data;
using NLog; using NLog;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Instrumentation; using NzbDrone.Common.Instrumentation;
@ -10,7 +9,6 @@
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
using NzbDrone.Test.Common.Categories;
namespace NzbDrone.Core.Test.InstrumentationTests namespace NzbDrone.Core.Test.InstrumentationTests
{ {
@ -64,23 +62,6 @@ public void write_long_log()
VerifyLog(StoredModel, LogLevel.Info); 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] [Test]
public void write_log_exception() public void write_log_exception()
{ {

View File

@ -1,7 +1,5 @@
using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Marr.Data;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.MovieImport.Specifications; using NzbDrone.Core.MediaFiles.MovieImport.Specifications;
@ -39,11 +37,11 @@ public void should_be_accepted_if_file_size_is_different()
{ {
_localMovie.Movie = Builder<Movie>.CreateNew() _localMovie.Movie = Builder<Movie>.CreateNew()
.With(e => e.MovieFileId = 1) .With(e => e.MovieFileId = 1)
.With(e => e.MovieFile = new LazyLoaded<MovieFile>( .With(e => e.MovieFile =
new MovieFile new MovieFile
{ {
Size = _localMovie.Size + 100.Megabytes() Size = _localMovie.Size + 100.Megabytes()
})) })
.Build(); .Build();
Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeTrue();
@ -54,11 +52,11 @@ public void should_be_reject_if_file_size_is_the_same()
{ {
_localMovie.Movie = Builder<Movie>.CreateNew() _localMovie.Movie = Builder<Movie>.CreateNew()
.With(e => e.MovieFileId = 1) .With(e => e.MovieFileId = 1)
.With(e => e.MovieFile = new LazyLoaded<MovieFile>( .With(e => e.MovieFile =
new MovieFile new MovieFile
{ {
Size = _localMovie.Size Size = _localMovie.Size
})) })
.Build(); .Build();
Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeFalse(); Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeFalse();
@ -69,7 +67,7 @@ public void should_be_accepted_if_file_cannot_be_fetched()
{ {
_localMovie.Movie = Builder<Movie>.CreateNew() _localMovie.Movie = Builder<Movie>.CreateNew()
.With(e => e.MovieFileId = 1) .With(e => e.MovieFileId = 1)
.With(e => e.MovieFile = new LazyLoaded<MovieFile>((MovieFile)null)) .With(e => e.MovieFile = null)
.Build(); .Build();
Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeTrue();

View File

@ -1,7 +1,5 @@
using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Marr.Data;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.MovieImport.Specifications; using NzbDrone.Core.MediaFiles.MovieImport.Specifications;
@ -48,13 +46,11 @@ public void should_return_true_if_upgrade_for_existing_episodeFile()
{ {
_localMovie.Movie.MovieFileId = 1; _localMovie.Movie.MovieFileId = 1;
_localMovie.Movie.MovieFile = new LazyLoaded<MovieFile>( _localMovie.Movie.MovieFile =
new MovieFile new MovieFile
{ {
Quality = new QualityModel(Quality.SDTV, new Revision(version: 1)) Quality = new QualityModel(Quality.SDTV, new Revision(version: 1))
} };
);
Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeTrue();
} }
@ -64,12 +60,11 @@ public void should_return_true_if_upgrade_for_existing_episodeFile()
public void should_return_false_if_not_an_upgrade_for_existing_episodeFile() public void should_return_false_if_not_an_upgrade_for_existing_episodeFile()
{ {
_localMovie.Movie.MovieFileId = 1; _localMovie.Movie.MovieFileId = 1;
_localMovie.Movie.MovieFile = new LazyLoaded<MovieFile>( _localMovie.Movie.MovieFile =
new MovieFile new MovieFile
{ {
Quality = new QualityModel(Quality.Bluray720p, new Revision(version: 1)) Quality = new QualityModel(Quality.Bluray720p, new Revision(version: 1))
} };
);
Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeFalse(); Subject.IsSatisfiedBy(_localMovie, null).Accepted.Should().BeFalse();
} }

View File

@ -1,7 +1,5 @@
using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Marr.Data;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;

View File

@ -5,6 +5,8 @@
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Movies; using NzbDrone.Core.Movies;
using System;
using System.Linq;
namespace NzbDrone.Core.Test.MovieTests.MovieRepositoryTests namespace NzbDrone.Core.Test.MovieTests.MovieRepositoryTests
{ {
@ -12,13 +14,17 @@ namespace NzbDrone.Core.Test.MovieTests.MovieRepositoryTests
public class MovieRepositoryFixture : DbTest<MovieRepository, Movie> public class MovieRepositoryFixture : DbTest<MovieRepository, Movie>
{ {
private IProfileRepository _profileRepository;
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
_profileRepository = Mocker.Resolve<ProfileRepository>();
Mocker.SetConstant<IProfileRepository>(_profileRepository);
} }
[Test] [Test]
public void should_lazyload_quality_profile() public void should_load_quality_profile()
{ {
var profile = new Profile var profile = new Profile
{ {
@ -29,16 +35,14 @@ public void should_lazyload_quality_profile()
Name = "TestProfile" Name = "TestProfile"
}; };
_profileRepository.Insert(profile);
Mocker.Resolve<ProfileRepository>().Insert(profile);
var movie = Builder<Movie>.CreateNew().BuildNew(); var movie = Builder<Movie>.CreateNew().BuildNew();
movie.ProfileId = profile.Id; movie.ProfileId = profile.Id;
Subject.Insert(movie); Subject.Insert(movie);
Subject.All().Single().Profile.Should().NotBeNull();
StoredModel.Profile.Should().NotBeNull();
} }
} }
} }

View File

@ -1,11 +1,9 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Movies.AlternativeTitles; using NzbDrone.Core.Movies.AlternativeTitles;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
@ -36,7 +34,7 @@ public void Setup()
.With(m => m.Title = "Fack Ju Göthe 2") .With(m => m.Title = "Fack Ju Göthe 2")
.With(m => m.CleanTitle = "fackjugoethe2") .With(m => m.CleanTitle = "fackjugoethe2")
.With(m => m.Year = 2015) .With(m => m.Year = 2015)
.With(m => m.AlternativeTitles = new LazyList<AlternativeTitle>( new List<AlternativeTitle> {new AlternativeTitle("Fack Ju Göthe 2: Same same")})) .With(m => m.AlternativeTitles = new List<AlternativeTitle> {new AlternativeTitle("Fack Ju Göthe 2: Same same")})
.Build(); .Build();
_parsedMovieInfo = new ParsedMovieInfo _parsedMovieInfo = new ParsedMovieInfo

View File

@ -7,6 +7,7 @@
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" /> <PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Include="NunitXml.TestLogger" Version="2.1.41" /> <PackageReference Include="NunitXml.TestLogger" Version="2.1.41" />
<PackageReference Include="Dapper" Version="2.0.30" />
<PackageReference Include="NBuilder" Version="6.0.1" /> <PackageReference Include="NBuilder" Version="6.0.1" />
<PackageReference Include="System.Data.SQLite.Core.Lidarr" Version="1.0.111.0-5" /> <PackageReference Include="System.Data.SQLite.Core.Lidarr" Version="1.0.111.0-5" />
</ItemGroup> </ItemGroup>

View File

@ -20,12 +20,12 @@ public UserRepository(IMainDatabase database, IEventAggregator eventAggregator)
public User FindUser(string username) public User FindUser(string username)
{ {
return Query.Where(u => u.Username == username).SingleOrDefault(); return Query(x => x.Username == username).SingleOrDefault();
} }
public User FindUser(Guid identifier) public User FindUser(Guid identifier)
{ {
return Query.Where(u => u.Identifier == identifier).SingleOrDefault(); return Query(x => x.Identifier == identifier).SingleOrDefault();
} }
} }
} }

View File

@ -27,7 +27,7 @@ public MakeDatabaseBackup(Logger logger)
public void BackupDatabase(IDatabase database, string targetDirectory) public void BackupDatabase(IDatabase database, string targetDirectory)
{ {
var sourceConnectionString = ""; var sourceConnectionString = "";
using (var db = database.GetDataMapper()) using (var db = database.OpenConnection())
{ {
sourceConnectionString = db.ConnectionString; sourceConnectionString = db.ConnectionString;
} }

View File

@ -1,7 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Dapper;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using Marr.Data.QGen;
using NzbDrone.Core.Movies; using NzbDrone.Core.Movies;
namespace NzbDrone.Core.Blacklisting namespace NzbDrone.Core.Blacklisting
@ -22,26 +23,35 @@ public BlacklistRepository(IMainDatabase database, IEventAggregator eventAggrega
public List<Blacklist> BlacklistedByTitle(int movieId, string sourceTitle) public List<Blacklist> BlacklistedByTitle(int movieId, string sourceTitle)
{ {
return Query.Where(e => e.MovieId == movieId) return Query(x => x.MovieId == movieId && x.SourceTitle.Contains(sourceTitle));
.AndWhere(e => e.SourceTitle.Contains(sourceTitle)).ToList();
} }
public List<Blacklist> BlacklistedByTorrentInfoHash(int movieId, string torrentInfoHash) public List<Blacklist> BlacklistedByTorrentInfoHash(int movieId, string torrentInfoHash)
{ {
return Query.Where(e => e.MovieId == movieId) return Query(x => x.MovieId == movieId && x.TorrentInfoHash.Contains(torrentInfoHash));
.AndWhere(e => e.TorrentInfoHash.Contains(torrentInfoHash)).ToList();
} }
public List<Blacklist> BlacklistedByMovie(int movieId) public List<Blacklist> BlacklistedByMovie(int movieId)
{ {
return Query.Where(b => b.MovieId == movieId).ToList(); return Query(x => x.MovieId == movieId);
} }
protected override SortBuilder<Blacklist> GetPagedQuery(QueryBuilder<Blacklist> query, PagingSpec<Blacklist> pagingSpec) private IEnumerable<Blacklist> SelectJoined(SqlBuilder.Template sql)
{ {
var baseQuery = query.Join<Blacklist, Movie>(JoinType.Inner, h => h.Movie, (h, s) => h.MovieId == s.Id); using (var conn = _database.OpenConnection())
{
return conn.Query<Blacklist, Movie, Blacklist>(
sql.RawSql,
(bl, movie) => {
bl.Movie = movie;
return bl;
},
sql.Parameters)
.ToList();
}
}
return base.GetPagedQuery(baseQuery, pagingSpec); protected override SqlBuilder PagedBuilder() => new SqlBuilder().Join("Movies ON Movies.Id = Blacklist.MovieId");
} protected override IEnumerable<Blacklist> PagedSelector(SqlBuilder.Template sql) => SelectJoined(sql);
} }
} }

View File

@ -18,10 +18,9 @@ public ConfigRepository(IMainDatabase database, IEventAggregator eventAggregator
{ {
} }
public Config Get(string key) public Config Get(string key)
{ {
return Query.Where(c => c.Key == key).SingleOrDefault(); return Query(c => c.Key == key).SingleOrDefault();
} }
public Config Upsert(string key, string value) public Config Upsert(string key, string value)

View File

@ -7,6 +7,10 @@ namespace NzbDrone.Core.CustomFormats
{ {
public class CustomFormat : ModelBase, IEquatable<CustomFormat> public class CustomFormat : ModelBase, IEquatable<CustomFormat>
{ {
public string Name { get; set; }
public List<FormatTag> FormatTags { get; set; }
public CustomFormat() public CustomFormat()
{ {
@ -18,9 +22,7 @@ public CustomFormat(string name, params string[] tags)
FormatTags = tags.Select(t => new FormatTag(t)).ToList(); FormatTags = tags.Select(t => new FormatTag(t)).ToList();
} }
public string Name { get; set; } public static implicit operator CustomFormatDefinition(CustomFormat format) => new CustomFormatDefinition { Id = format.Id, Name = format.Name, FormatTags = format.FormatTags };
public List<FormatTag> FormatTags { get; set; }
public override string ToString() public override string ToString()
{ {

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.CustomFormats
{
public class CustomFormatDefinition : ModelBase
{
public string Name { get; set; }
public List<FormatTag> FormatTags { get; set; }
public static implicit operator CustomFormat(CustomFormatDefinition def) => new CustomFormat { Id = def.Id, Name = def.Name, FormatTags = def.FormatTags };
}
}

View File

@ -3,12 +3,12 @@
namespace NzbDrone.Core.CustomFormats namespace NzbDrone.Core.CustomFormats
{ {
public interface ICustomFormatRepository : IBasicRepository<CustomFormat> public interface ICustomFormatRepository : IBasicRepository<CustomFormatDefinition>
{ {
} }
public class CustomFormatRepository : BasicRepository<CustomFormat>, ICustomFormatRepository public class CustomFormatRepository : BasicRepository<CustomFormatDefinition>, ICustomFormatRepository
{ {
public CustomFormatRepository(IMainDatabase database, IEventAggregator eventAggregator) public CustomFormatRepository(IMainDatabase database, IEventAggregator eventAggregator)
: base(database, eventAggregator) : base(database, eventAggregator)

View File

@ -7,9 +7,7 @@
using NzbDrone.Core.Blacklisting; using NzbDrone.Core.Blacklisting;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.History; using NzbDrone.Core.History;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles;
namespace NzbDrone.Core.CustomFormats namespace NzbDrone.Core.CustomFormats
@ -23,8 +21,7 @@ public interface ICustomFormatService
void Delete(int id); void Delete(int id);
} }
public class CustomFormatService : ICustomFormatService
public class CustomFormatService : ICustomFormatService, IHandle<ApplicationStartedEvent>
{ {
private readonly ICustomFormatRepository _formatRepository; private readonly ICustomFormatRepository _formatRepository;
private IProfileService _profileService; private IProfileService _profileService;
@ -58,6 +55,9 @@ public CustomFormatService(ICustomFormatRepository formatRepository, ICacheManag
_cache = cacheManager.GetCache<Dictionary<int, CustomFormat>>(typeof(CustomFormat), "formats"); _cache = cacheManager.GetCache<Dictionary<int, CustomFormat>>(typeof(CustomFormat), "formats");
_historyService = historyService; _historyService = historyService;
_logger = logger; _logger = logger;
// Fill up the cache for subsequent DB lookups
All();
} }
public void Update(CustomFormat customFormat) public void Update(CustomFormat customFormat)
@ -144,7 +144,7 @@ private Dictionary<int, CustomFormat> AllDictionary()
{ {
return _cache.Get("all", () => return _cache.Get("all", () =>
{ {
var all = _formatRepository.All().ToDictionary(m => m.Id); var all = _formatRepository.All().Select(x => (CustomFormat)x).ToDictionary(m => m.Id);
AllCustomFormats = all; AllCustomFormats = all;
return all; return all;
}); });
@ -194,11 +194,5 @@ public static Dictionary<string, List<CustomFormat>> Templates
}; };
} }
} }
public void Handle(ApplicationStartedEvent message)
{
// Fillup cache for DataMapper.
All();
}
} }
} }

View File

@ -3,11 +3,10 @@
using System.Data; using System.Data;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using Marr.Data; using System.Reflection;
using Marr.Data.QGen; using System.Text;
using NzbDrone.Common.Extensions; using Dapper;
using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.Datastore.Extensions;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Datastore namespace NzbDrone.Core.Datastore
@ -31,43 +30,89 @@ namespace NzbDrone.Core.Datastore
bool HasItems(); bool HasItems();
void DeleteMany(IEnumerable<int> ids); void DeleteMany(IEnumerable<int> ids);
void SetFields(TModel model, params Expression<Func<TModel, object>>[] properties); void SetFields(TModel model, params Expression<Func<TModel, object>>[] properties);
void SetFields(IList<TModel> models, params Expression<Func<TModel, object>>[] properties);
TModel Single(); TModel Single();
PagingSpec<TModel> GetPaged(PagingSpec<TModel> pagingSpec); PagingSpec<TModel> GetPaged(PagingSpec<TModel> pagingSpec);
} }
public class BasicRepository<TModel> : IBasicRepository<TModel> where TModel : ModelBase, new() public class BasicRepository<TModel> : IBasicRepository<TModel> where TModel : ModelBase, new()
{ {
private readonly IDatabase _database;
private readonly IEventAggregator _eventAggregator; private readonly IEventAggregator _eventAggregator;
private readonly PropertyInfo _keyProperty;
private readonly List<PropertyInfo> _properties;
private readonly string _updateSql;
private readonly string _insertSql;
protected IDataMapper DataMapper => _database.GetDataMapper(); protected readonly IDatabase _database;
protected readonly string _table;
protected string _selectTemplate;
protected string _deleteTemplate;
public BasicRepository(IDatabase database, IEventAggregator eventAggregator) public BasicRepository(IDatabase database, IEventAggregator eventAggregator)
{ {
_database = database; _database = database;
_eventAggregator = eventAggregator; _eventAggregator = eventAggregator;
var type = typeof(TModel);
_table = TableMapping.Mapper.TableNameMapping(type);
_keyProperty = type.GetProperty(nameof(ModelBase.Id));
var excluded = TableMapping.Mapper.ExcludeProperties(type).Select(x => x.Name).ToList();
excluded.Add(_keyProperty.Name);
_properties = type.GetProperties().Where(x => !excluded.Contains(x.Name)).ToList();
_insertSql = GetInsertSql();
_updateSql = GetUpdateSql(_properties);
_selectTemplate = $"SELECT /**select**/ FROM {_table} /**join**/ /**innerjoin**/ /**leftjoin**/ /**where**/ /**orderby**/";
_deleteTemplate = $"DELETE FROM {_table} /**where**/";
} }
protected QueryBuilder<TModel> Query => AddJoinQueries(DataMapper.Query<TModel>()); protected virtual SqlBuilder BuilderBase() => new SqlBuilder();
protected virtual SqlBuilder Builder() => BuilderBase().SelectAll();
protected void Delete(Expression<Func<TModel, bool>> filter) protected virtual IEnumerable<TModel> GetResults(SqlBuilder.Template sql)
{ {
DataMapper.Delete(filter); using (var conn = _database.OpenConnection())
{
return conn.Query<TModel>(sql.RawSql, sql.Parameters);
}
} }
public IEnumerable<TModel> All() protected List<TModel> Query(Expression<Func<TModel, bool>> where)
{ {
return AddJoinQueries(DataMapper.Query<TModel>()).ToList(); return Query(Builder().Where<TModel>(where));
}
protected List<TModel> Query(SqlBuilder builder)
{
return Query(builder, GetResults);
}
protected List<TModel> Query(SqlBuilder builder, Func<SqlBuilder.Template, IEnumerable<TModel>> queryFunc)
{
var sql = builder.AddTemplate(_selectTemplate).LogQuery();
return queryFunc(sql).ToList();
} }
public int Count() public int Count()
{ {
return DataMapper.Query<TModel>().GetRowCount(); using (var conn = _database.OpenConnection())
{
return conn.ExecuteScalar<int>($"SELECT COUNT(*) FROM {_table}");
}
}
public virtual IEnumerable<TModel> All()
{
return Query(Builder());
} }
public TModel Get(int id) public TModel Get(int id)
{ {
var model = Query.Where(c => c.Id == id).SingleOrDefault(); var model = Query(x => x.Id == id).FirstOrDefault();
if (model == null) if (model == null)
{ {
@ -79,13 +124,16 @@ public TModel Get(int id)
public IEnumerable<TModel> Get(IEnumerable<int> ids) public IEnumerable<TModel> Get(IEnumerable<int> ids)
{ {
var idList = ids.ToList(); if (!ids.Any())
var query = string.Format("Id IN ({0})", string.Join(",", idList));
var result = Query.Where(m => m.Id.In(idList)).ToList();
if (result.Count != idList.Count())
{ {
throw new ApplicationException($"Expected query to return {idList.Count} rows but returned {result.Count}"); return new List<TModel>();
}
var result = Query(x => ids.Contains(x.Id));
if (result.Count != ids.Count())
{
throw new ApplicationException($"Expected query to return {ids.Count()} rows but returned {result.Count}");
} }
return result; return result;
@ -108,13 +156,70 @@ public TModel Insert(TModel model)
throw new InvalidOperationException("Can't insert model with existing ID " + model.Id); throw new InvalidOperationException("Can't insert model with existing ID " + model.Id);
} }
DataMapper.Insert(model); using (var conn = _database.OpenConnection())
{
model = Insert(conn, null, model);
}
ModelCreated(model); ModelCreated(model);
return 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<TModel> 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) public TModel Update(TModel model)
{ {
if (model.Id == 0) if (model.Id == 0)
@ -122,52 +227,59 @@ public TModel Update(TModel model)
throw new InvalidOperationException("Can't update model with ID 0"); throw new InvalidOperationException("Can't update model with ID 0");
} }
DataMapper.Update(model, c => c.Id == model.Id); using (var conn = _database.OpenConnection())
{
UpdateFields(conn, null, model, _properties);
}
ModelUpdated(model); ModelUpdated(model);
return model; return model;
} }
public void Delete(TModel model)
{
Delete(model.Id);
}
public void InsertMany(IList<TModel> models)
{
using (var unitOfWork = new UnitOfWork(() => DataMapper))
{
unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted);
foreach (var model in models)
{
unitOfWork.DB.Insert(model);
}
unitOfWork.Commit();
}
}
public void UpdateMany(IList<TModel> models) public void UpdateMany(IList<TModel> models)
{ {
using (var unitOfWork = new UnitOfWork(() => DataMapper)) if (models.Any(x => x.Id == 0))
{
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"); throw new InvalidOperationException("Can't update model with ID 0");
} }
unitOfWork.DB.Update(model, c => c.Id == localModel.Id); using (var conn = _database.OpenConnection())
{
UpdateFields(conn, null, models, _properties);
}
} }
unitOfWork.Commit(); protected void Delete(Expression<Func<TModel, bool>> where)
{
Delete(Builder().Where<TModel>(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);
}
public void Delete(int id)
{
Delete(x => x.Id == id);
}
public void DeleteMany(IEnumerable<int> ids)
{
if (ids.Any())
{
Delete(x => ids.Contains(x.Id));
} }
} }
@ -187,31 +299,13 @@ public TModel Upsert(TModel model)
return model; return model;
} }
public void Delete(int id)
{
DataMapper.Delete<TModel>(c => c.Id == id);
}
public void DeleteMany(IEnumerable<int> ids)
{
using (var unitOfWork = new UnitOfWork(() => DataMapper))
{
unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted);
foreach (var id in ids)
{
var localId = id;
unitOfWork.DB.Delete<TModel>(c => c.Id == localId);
}
unitOfWork.Commit();
}
}
public void Purge(bool vacuum = false) public void Purge(bool vacuum = false)
{ {
DataMapper.Delete<TModel>(c => c.Id > -1); using (var conn = _database.OpenConnection())
{
conn.Execute($"DELETE FROM [{_table}]");
}
if (vacuum) if (vacuum)
{ {
Vacuum(); Vacuum();
@ -232,43 +326,115 @@ public void SetFields(TModel model, params Expression<Func<TModel, object>>[] pr
{ {
if (model.Id == 0) if (model.Id == 0)
{ {
throw new InvalidOperationException("Attempted to updated model without ID"); throw new InvalidOperationException("Attempted to update model without ID");
} }
DataMapper.Update<TModel>() var propertiesToUpdate = properties.Select(x => x.GetMemberName()).ToList();
.Where(c => c.Id == model.Id)
.ColumnsIncluding(properties) using (var conn = _database.OpenConnection())
.Entity(model) {
.Execute(); UpdateFields(conn, null, model, propertiesToUpdate);
}
ModelUpdated(model); ModelUpdated(model);
} }
public void SetFields(IList<TModel> models, params Expression<Func<TModel, object>>[] 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<PropertyInfo> 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<PropertyInfo> propertiesToUpdate)
{
var sql = propertiesToUpdate == _properties ? _updateSql : GetUpdateSql(propertiesToUpdate);
connection.Execute(sql, model, transaction: transaction);
}
private void UpdateFields(IDbConnection connection, IDbTransaction transaction, IList<TModel> models, List<PropertyInfo> propertiesToUpdate)
{
var sql = propertiesToUpdate == _properties ? _updateSql : GetUpdateSql(propertiesToUpdate);
connection.Execute(sql, models, transaction: transaction);
}
protected virtual SqlBuilder PagedBuilder() => BuilderBase();
protected virtual IEnumerable<TModel> PagedSelector(SqlBuilder.Template sql) => GetResults(sql);
public virtual PagingSpec<TModel> GetPaged(PagingSpec<TModel> pagingSpec) public virtual PagingSpec<TModel> GetPaged(PagingSpec<TModel> pagingSpec)
{ {
pagingSpec.Records = GetPagedQuery(Query, pagingSpec).ToList(); pagingSpec.Records = GetPagedRecords(PagedBuilder().SelectAll(), pagingSpec, PagedSelector);
pagingSpec.TotalRecords = GetPagedQuery(Query, pagingSpec).GetRowCount(); pagingSpec.TotalRecords = GetPagedRecordCount(PagedBuilder().SelectCount(), pagingSpec);
return pagingSpec; return pagingSpec;
} }
protected virtual SortBuilder<TModel> GetPagedQuery(QueryBuilder<TModel> query, PagingSpec<TModel> pagingSpec) private void AddFilters(SqlBuilder builder, PagingSpec<TModel> pagingSpec)
{ {
var filterExpressions = pagingSpec.FilterExpressions; var filters = pagingSpec.FilterExpressions;
var sortQuery = query.Where(filterExpressions.FirstOrDefault());
if (filterExpressions.Count > 1) foreach (var filter in filters)
{ {
// Start at the second item for the AndWhere clauses builder.Where<TModel>(filter);
for (var i = 1; i < filterExpressions.Count; i++)
{
sortQuery.AndWhere(filterExpressions[i]);
} }
} }
return sortQuery.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection()) protected List<TModel> GetPagedRecords(SqlBuilder builder, PagingSpec<TModel> pagingSpec, Func<SqlBuilder.Template, IEnumerable<TModel>> queryFunc)
.Skip(pagingSpec.PagingOffset()) {
.Take(pagingSpec.PageSize); 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<TModel> pagingSpec)
{
AddFilters(builder, pagingSpec);
var sql = builder.AddTemplate(_selectTemplate).LogQuery();
using (var conn = _database.OpenConnection())
{
return conn.ExecuteScalar<int>(sql.RawSql, sql.Parameters);
}
} }
protected void ModelCreated(TModel model) protected void ModelCreated(TModel model)
@ -294,11 +460,6 @@ private void PublishModelEvent(TModel model, ModelAction action)
} }
} }
protected virtual QueryBuilder<TActual> AddJoinQueries<TActual>(QueryBuilder<TActual> baseQuery)
{
return baseQuery;
}
protected virtual bool PublishModelEvents => false; protected virtual bool PublishModelEvents => false;
} }
} }

View File

@ -1,51 +0,0 @@
using System;
using Marr.Data.Converters;
using Marr.Data.Mapping;
namespace NzbDrone.Core.Datastore.Converters
{
public class BooleanIntConverter : IConverter
{
public object FromDB(ConverterContext context)
{
if (context.DbValue == DBNull.Value)
{
return DBNull.Value;
}
var val = (long)context.DbValue;
switch (val)
{
case 1:
return true;
case 0:
return false;
default:
throw new ConversionException(string.Format("The BooleanCharConverter could not convert the value '{0}' to a Boolean.", context.DbValue));
}
}
public object FromDB(ColumnMap map, object dbValue)
{
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
}
public object ToDB(object clrValue)
{
var val = (Nullable<bool>)clrValue;
switch (val)
{
case true:
return 1;
case false:
return 0;
default:
return DBNull.Value;
}
}
public Type DbType => typeof(int);
}
}

View File

@ -1,30 +1,28 @@
using System; using System.Data;
using Marr.Data.Converters; using System.Text.Json;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Common.Reflection; using NzbDrone.Common.Reflection;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Datastore.Converters namespace NzbDrone.Core.Datastore.Converters
{ {
public class CommandConverter : EmbeddedDocumentConverter public class CommandConverter : EmbeddedDocumentConverter<Command>
{ {
public override object FromDB(ConverterContext context) public override Command Parse(object value)
{ {
if (context.DbValue == DBNull.Value) var stringValue = (string) value;
{
return null;
}
var stringValue = (string)context.DbValue;
if (stringValue.IsNullOrWhiteSpace()) if (stringValue.IsNullOrWhiteSpace())
{ {
return null; return null;
} }
var ordinal = context.DataRecord.GetOrdinal("Name"); string contract;
var contract = context.DataRecord.GetString(ordinal); using (JsonDocument body = JsonDocument.Parse(stringValue))
{
contract = body.RootElement.GetProperty("name").GetString();
}
var impType = typeof (Command).Assembly.FindTypeByName(contract + "Command"); var impType = typeof (Command).Assembly.FindTypeByName(contract + "Command");
if (impType == null) if (impType == null)
@ -32,7 +30,12 @@ public override object FromDB(ConverterContext context)
throw new CommandNotFoundException(contract); throw new CommandNotFoundException(contract);
} }
return Json.Deserialize(stringValue, impType); return (Command) JsonSerializer.Deserialize(stringValue, impType, SerializerSettings);
}
public override void SetValue(IDbDataParameter parameter, Command value)
{
parameter.Value = value == null ? null : JsonSerializer.Serialize(value, SerializerSettings);
} }
} }
} }

View File

@ -1,91 +1,57 @@
using System; using System;
using System.ServiceModel; using System.Data;
using Marr.Data.Converters; using System.Text.Json;
using Marr.Data.Mapping; using System.Text.Json.Serialization;
using NzbDrone.Core.Qualities; using Dapper;
using Newtonsoft.Json; using NzbDrone.Common.Serializer;
using NzbDrone.Core.CustomFormats; using NzbDrone.Core.CustomFormats;
namespace NzbDrone.Core.Datastore.Converters namespace NzbDrone.Core.Datastore.Converters
{ {
public class CustomFormatIntConverter : JsonConverter, IConverter public class DapperCustomFormatIntConverter : SqlMapper.TypeHandler<CustomFormat>
{ {
//TODO think of something better. public override void SetValue(IDbDataParameter parameter, CustomFormat value)
public object FromDB(ConverterContext context)
{ {
if (context.DbValue == DBNull.Value) parameter.Value = value.Id;
}
public override CustomFormat Parse(object value)
{
Console.WriteLine(value.ToJson());
if (value is DBNull)
{ {
return null; return null;
} }
var val = Convert.ToInt32(context.DbValue); var val = Convert.ToInt32(value);
if (val == 0) if (val == 0)
{ {
return CustomFormat.None; 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]; return CustomFormatService.AllCustomFormats[val];
} }
public object FromDB(ColumnMap map, object dbValue)
{
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
} }
public object ToDB(object clrValue) public class CustomFormatIntConverter : JsonConverter<CustomFormat>
{ {
if(clrValue == DBNull.Value) return null; public override CustomFormat Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
if(!(clrValue is CustomFormat))
{ {
throw new InvalidOperationException("Attempted to save a quality definition that isn't really a quality definition"); var val = reader.GetInt32();
}
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) if (val == 0)
{ {
return CustomFormat.None; 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]; return CustomFormatService.AllCustomFormats[val];
} }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) public override void Write(Utf8JsonWriter writer, CustomFormat value, JsonSerializerOptions options)
{ {
writer.WriteValue(ToDB(value)); writer.WriteNumberValue(value.Id);
} }
} }
} }

View File

@ -1,46 +0,0 @@
using System;
using Marr.Data.Converters;
using Marr.Data.Mapping;
namespace NzbDrone.Core.Datastore.Converters
{
public class DoubleConverter : IConverter
{
public object FromDB(ConverterContext context)
{
if (context.DbValue == DBNull.Value)
{
return DBNull.Value;
}
if (context.DbValue is double)
{
return context.DbValue;
}
return Convert.ToDouble(context.DbValue);
}
public object FromDB(ColumnMap map, object dbValue)
{
if (dbValue == DBNull.Value)
{
return DBNull.Value;
}
if (dbValue is double)
{
return dbValue;
}
return Convert.ToDouble(dbValue);
}
public object ToDB(object clrValue)
{
return clrValue;
}
public Type DbType { get; private set; }
}
}

View File

@ -1,65 +1,49 @@
using System; using System.Data;
using Marr.Data.Converters; using System.Text.Json;
using Marr.Data.Mapping; using System.Text.Json.Serialization;
using Newtonsoft.Json; using Dapper;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Converters;
namespace NzbDrone.Core.Datastore.Converters namespace NzbDrone.Core.Datastore.Converters
{ {
public class EmbeddedDocumentConverter : IConverter public class EmbeddedDocumentConverter<T> : SqlMapper.TypeHandler<T>
{ {
private readonly JsonSerializerSettings SerializerSetting; protected readonly JsonSerializerOptions SerializerSettings;
public EmbeddedDocumentConverter(params JsonConverter[] converters) public EmbeddedDocumentConverter()
{ {
SerializerSetting = new JsonSerializerSettings var serializerSettings = new JsonSerializerOptions
{ {
DateTimeZoneHandling = DateTimeZoneHandling.Utc, AllowTrailingCommas = true,
NullValueHandling = NullValueHandling.Ignore, IgnoreNullValues = false,
Formatting = Formatting.Indented, PropertyNameCaseInsensitive = true,
DefaultValueHandling = DefaultValueHandling.Include, DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
ContractResolver = new CamelCasePropertyNamesContractResolver() PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
}; };
SerializerSetting.Converters.Add(new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy() }); serializerSettings.Converters.Add(new NoFlagsStringEnumConverter());
SerializerSetting.Converters.Add(new VersionConverter()); serializerSettings.Converters.Add(new TimeSpanConverter());
serializerSettings.Converters.Add(new UtcConverter());
SerializerSettings = serializerSettings;
}
public EmbeddedDocumentConverter(params JsonConverter[] converters) : this()
{
foreach (var converter in converters) foreach (var converter in converters)
{ {
SerializerSetting.Converters.Add(converter); SerializerSettings.Converters.Add(converter);
} }
} }
public virtual object FromDB(ConverterContext context) public override void SetValue(IDbDataParameter parameter, T value)
{ {
if (context.DbValue == DBNull.Value) parameter.Value = JsonSerializer.Serialize(value, SerializerSettings);
}
public override T Parse(object value)
{ {
return DBNull.Value; return JsonSerializer.Deserialize<T>((string) value, SerializerSettings);
} }
var stringValue = (string)context.DbValue;
if (string.IsNullOrWhiteSpace(stringValue))
{
return null;
}
return JsonConvert.DeserializeObject(stringValue, context.ColumnMap.FieldType, SerializerSetting);
}
public object FromDB(ColumnMap map, object dbValue)
{
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);
} }
} }

View File

@ -1,36 +0,0 @@
using System;
using Marr.Data.Converters;
using Marr.Data.Mapping;
namespace NzbDrone.Core.Datastore.Converters
{
public class EnumIntConverter : IConverter
{
public Type DbType => typeof(int);
public object FromDB(ConverterContext context)
{
if (context.DbValue != null && context.DbValue != DBNull.Value)
{
return Enum.ToObject(context.ColumnMap.FieldType, (long)context.DbValue);
}
return null;
}
public object FromDB(ColumnMap map, object dbValue)
{
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
}
public object ToDB(object clrValue)
{
if (clrValue != null)
{
return (int)clrValue;
}
return DBNull.Value;
}
}
}

View File

@ -1,40 +1,24 @@
using System; using System;
using Marr.Data.Converters; using System.Data;
using Marr.Data.Mapping; using Dapper;
namespace NzbDrone.Core.Datastore.Converters namespace NzbDrone.Core.Datastore.Converters
{ {
public class GuidConverter : IConverter public class GuidConverter : SqlMapper.TypeHandler<Guid>
{ {
public object FromDB(ConverterContext context) public override Guid Parse(object value)
{ {
if (context.DbValue == DBNull.Value) if (value == null)
{ {
return Guid.Empty; return Guid.Empty;
} }
var value = (string)context.DbValue; return new Guid((string)value);
return new Guid(value);
} }
public object FromDB(ColumnMap map, object dbValue) public override void SetValue(IDbDataParameter parameter, Guid value)
{ {
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); parameter.Value = value.ToString();
} }
public object ToDB(object clrValue)
{
if (clrValue == null)
{
return DBNull.Value;
}
var value = clrValue;
return value.ToString();
}
public Type DbType => typeof(string);
} }
} }

View File

@ -1,36 +0,0 @@
using System;
using Marr.Data.Converters;
using Marr.Data.Mapping;
namespace NzbDrone.Core.Datastore.Converters
{
public class Int32Converter : IConverter
{
public object FromDB(ConverterContext context)
{
if (context.DbValue == DBNull.Value)
{
return DBNull.Value;
}
if (context.DbValue is int)
{
return context.DbValue;
}
return Convert.ToInt32(context.DbValue);
}
public object FromDB(ColumnMap map, object dbValue)
{
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
}
public object ToDB(object clrValue)
{
return clrValue;
}
public Type DbType { get; private set; }
}
}

View File

@ -1,65 +1,48 @@
using System; using System;
using Marr.Data.Converters; using System.Data;
using Marr.Data.Mapping; using System.Text.Json;
using Newtonsoft.Json; using System.Text.Json.Serialization;
using Dapper;
using NzbDrone.Core.Languages; using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Datastore.Converters namespace NzbDrone.Core.Datastore.Converters
{ {
public class LanguageIntConverter : JsonConverter, IConverter public class DapperLanguageIntConverter : SqlMapper.TypeHandler<Language>
{ {
public object FromDB(ConverterContext context) public override void SetValue(IDbDataParameter parameter, Language value)
{ {
if (context.DbValue == DBNull.Value) if (value == null)
{
throw new InvalidOperationException("Attempted to save a language that isn't really a language");
}
else
{
parameter.Value = (int) value;
}
}
public override Language Parse(object value)
{
if (value == null || value is DBNull)
{ {
return Language.Unknown; return Language.Unknown;
} }
var val = Convert.ToInt32(context.DbValue); return (Language) Convert.ToInt32(value);
}
return (Language)val;
} }
public object FromDB(ColumnMap map, object dbValue) public class LanguageIntConverter : JsonConverter<Language>
{ {
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); public override Language Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var item = reader.GetInt32();
return (Language)item;
} }
public object ToDB(object clrValue) public override void Write(Utf8JsonWriter writer, Language value, JsonSerializerOptions options)
{ {
if (clrValue == DBNull.Value) return 0; writer.WriteNumberValue((int) value);
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));
} }
} }
} }

View File

@ -0,0 +1,17 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace NzbDrone.Core.Datastore.Converters
{
public class NoFlagsStringEnumConverter : JsonConverterFactory
{
private static JsonStringEnumConverter s_stringEnumConverter = new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, false);
public override bool CanConvert(Type typeToConvert)
=> typeToConvert.IsEnum && !typeToConvert.IsDefined(typeof(FlagsAttribute), inherit: false);
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
=> s_stringEnumConverter.CreateConverter(typeToConvert, options);
}
}

View File

@ -1,36 +1,25 @@
using System; using System;
using Marr.Data.Converters; using System.Data;
using Marr.Data.Mapping; using Dapper;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
namespace NzbDrone.Core.Datastore.Converters namespace NzbDrone.Core.Datastore.Converters
{ {
public class OsPathConverter : IConverter public class OsPathConverter : SqlMapper.TypeHandler<OsPath>
{ {
public object FromDB(ConverterContext context) public override void SetValue(IDbDataParameter parameter, OsPath value)
{ {
if (context.DbValue == DBNull.Value) parameter.Value = value.FullPath;
}
public override OsPath Parse(object value)
{ {
return DBNull.Value; if (value == null || value is DBNull)
}
var value = (string)context.DbValue;
return new OsPath(value);
}
public object FromDB(ColumnMap map, object dbValue)
{ {
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); return new OsPath(null);
} }
public object ToDB(object clrValue) return new OsPath((string) value);
{ }
var value = (OsPath)clrValue;
return value.FullPath;
}
public Type DbType => typeof(string);
} }
} }

View File

@ -1,39 +1,22 @@
using System; using System.Data;
using Marr.Data.Converters; using System.Text.Json;
using NzbDrone.Common.Reflection;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Datastore.Converters namespace NzbDrone.Core.Datastore.Converters
{ {
public class ProviderSettingConverter : EmbeddedDocumentConverter public class ProviderSettingConverter : EmbeddedDocumentConverter<IProviderConfig>
{ {
public override object FromDB(ConverterContext context) public override IProviderConfig Parse(object value)
{ {
if (context.DbValue == DBNull.Value) // We can't deserialize based on another column, happens in ProviderRepository instead
return null;
}
public override void SetValue(IDbDataParameter parameter, IProviderConfig value)
{ {
return NullConfig.Instance; // 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);
} }
} }

View File

@ -1,59 +1,37 @@
using System; using System;
using Marr.Data.Converters;
using Marr.Data.Mapping;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using Newtonsoft.Json; using System.Text.Json;
using System.Text.Json.Serialization;
using Dapper;
using System.Data;
namespace NzbDrone.Core.Datastore.Converters namespace NzbDrone.Core.Datastore.Converters
{ {
public class QualityIntConverter : JsonConverter, IConverter public class QualityIntConverter : JsonConverter<Quality>
{ {
public object FromDB(ConverterContext context) public override Quality Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
if (context.DbValue == DBNull.Value) var item = reader.GetInt32();
return (Quality)item;
}
public override void Write(Utf8JsonWriter writer, Quality value, JsonSerializerOptions options)
{ {
return Quality.Unknown; writer.WriteNumberValue((int) value);
}
} }
var val = Convert.ToInt32(context.DbValue); public class DapperQualityIntConverter : SqlMapper.TypeHandler<Quality>
return (Quality)val;
}
public object FromDB(ColumnMap map, object dbValue)
{ {
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); public override void SetValue(IDbDataParameter parameter, Quality value)
}
public object ToDB(object clrValue)
{ {
if (clrValue == DBNull.Value) return 0; parameter.Value = value == null ? 0 : (int) value;
}
if (clrValue as Quality == null) public override Quality Parse(object value)
{ {
throw new InvalidOperationException("Attempted to save a quality that isn't really a quality"); return (Quality) Convert.ToInt32(value);
}
} }
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));
}
}
} }

View File

@ -1,60 +1,41 @@
using System; using System;
using Marr.Data.Converters;
using Marr.Data.Mapping;
using NzbDrone.Core.Qualities;
using Newtonsoft.Json;
using NzbDrone.Core.CustomFormats; using NzbDrone.Core.CustomFormats;
using System.Text.Json.Serialization;
using System.Text.Json;
using Dapper;
using System.Data;
namespace NzbDrone.Core.Datastore.Converters namespace NzbDrone.Core.Datastore.Converters
{ {
public class QualityTagStringConverter : JsonConverter, IConverter public class DapperQualityTagStringConverter : SqlMapper.TypeHandler<FormatTag>
{ {
public object FromDB(ConverterContext context) public override void SetValue(IDbDataParameter parameter, FormatTag value)
{ {
if (context.DbValue == DBNull.Value) parameter.Value = value.Raw;
}
public override FormatTag Parse(object value)
{
if (value == null || value is DBNull)
{ {
return new FormatTag(""); //Will throw argument exception! return new FormatTag(""); //Will throw argument exception!
} }
var val = Convert.ToString(context.DbValue); return new FormatTag(Convert.ToString(value));
}
return new FormatTag(val);
} }
public object FromDB(ColumnMap map, object dbValue) public class QualityTagStringConverter : JsonConverter<FormatTag>
{ {
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); public override FormatTag Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
}
public object ToDB(object clrValue)
{ {
if(clrValue == DBNull.Value) return 0; var item = reader.GetString();
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)); return new FormatTag(Convert.ToString(item));
} }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) public override void Write(Utf8JsonWriter writer, FormatTag value, JsonSerializerOptions options)
{ {
writer.WriteValue(ToDB(value)); writer.WriteStringValue(value.Raw);
} }
} }
} }

View File

@ -1,43 +1,19 @@
using System; using System;
using System.Globalization; using System.Text.Json;
using Marr.Data.Converters; using System.Text.Json.Serialization;
using Marr.Data.Mapping;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Core.Datastore.Converters namespace NzbDrone.Core.Datastore.Converters
{ {
public class TimeSpanConverter : IConverter public class TimeSpanConverter : JsonConverter<TimeSpan>
{ {
public object FromDB(ConverterContext context) public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
if (context.DbValue == DBNull.Value) return TimeSpan.Parse(reader.GetString());
{
return TimeSpan.Zero;
} }
if (context.DbValue is TimeSpan) public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options)
{ {
return context.DbValue; writer.WriteStringValue(value.ToString());
} }
return TimeSpan.Parse(context.DbValue.ToString(), CultureInfo.InvariantCulture);
}
public object FromDB(ColumnMap map, object dbValue)
{
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; }
} }
} }

View File

@ -1,32 +1,34 @@
using System; using System;
using Marr.Data.Converters; using System.Data;
using Marr.Data.Mapping; using System.Text.Json;
using System.Text.Json.Serialization;
using Dapper;
namespace NzbDrone.Core.Datastore.Converters namespace NzbDrone.Core.Datastore.Converters
{ {
public class UtcConverter : IConverter public class DapperUtcConverter : SqlMapper.TypeHandler<DateTime>
{ {
public object FromDB(ConverterContext context) public override void SetValue(IDbDataParameter parameter, DateTime value)
{ {
return context.DbValue; parameter.Value = value.ToUniversalTime();
} }
public object FromDB(ColumnMap map, object dbValue) public override DateTime Parse(object value)
{ {
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue }); return DateTime.SpecifyKind((DateTime)value, DateTimeKind.Utc);
}
} }
public object ToDB(object clrValue) public class UtcConverter : JsonConverter<DateTime>
{ {
if (clrValue == DBNull.Value) public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
return clrValue; return DateTime.Parse(reader.GetString());
} }
var dateTime = (DateTime)clrValue; public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
return dateTime.ToUniversalTime(); {
} writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"));
}
public Type DbType => typeof(DateTime);
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using Marr.Data; using System.Data;
using Dapper;
using NLog; using NLog;
using NzbDrone.Common.Instrumentation; using NzbDrone.Common.Instrumentation;
@ -7,7 +8,7 @@ namespace NzbDrone.Core.Datastore
{ {
public interface IDatabase public interface IDatabase
{ {
IDataMapper GetDataMapper(); IDbConnection OpenConnection();
Version Version { get; } Version Version { get; }
int Migration { get; } int Migration { get; }
void Vacuum(); void Vacuum();
@ -16,17 +17,17 @@ public interface IDatabase
public class Database : IDatabase public class Database : IDatabase
{ {
private readonly string _databaseName; private readonly string _databaseName;
private readonly Func<IDataMapper> _datamapperFactory; private readonly Func<IDbConnection> _datamapperFactory;
private readonly Logger _logger = NzbDroneLogger.GetLogger(typeof(Database)); private readonly Logger _logger = NzbDroneLogger.GetLogger(typeof(Database));
public Database(string databaseName, Func<IDataMapper> datamapperFactory) public Database(string databaseName, Func<IDbConnection> datamapperFactory)
{ {
_databaseName = databaseName; _databaseName = databaseName;
_datamapperFactory = datamapperFactory; _datamapperFactory = datamapperFactory;
} }
public IDataMapper GetDataMapper() public IDbConnection OpenConnection()
{ {
return _datamapperFactory(); return _datamapperFactory();
} }
@ -37,7 +38,7 @@ public Version Version
{ {
using (var db = _datamapperFactory()) using (var db = _datamapperFactory())
{ {
var version = db.ExecuteScalar("SELECT sqlite_version()").ToString(); var version = db.QueryFirstOrDefault<string>("SELECT sqlite_version()");
return new Version(version); return new Version(version);
} }
} }
@ -47,9 +48,10 @@ public int Migration
{ {
get get
{ {
var migration = _datamapperFactory() using (var db = _datamapperFactory())
.ExecuteScalar("SELECT version from VersionInfo ORDER BY version DESC LIMIT 1").ToString(); {
return Convert.ToInt32(migration); return db.QueryFirstOrDefault<int>("SELECT version from VersionInfo ORDER BY version DESC LIMIT 1");
}
} }
} }
@ -60,7 +62,7 @@ public void Vacuum()
_logger.Info("Vacuuming {0} database", _databaseName); _logger.Info("Vacuuming {0} database", _databaseName);
using (var db = _datamapperFactory()) using (var db = _datamapperFactory())
{ {
db.ExecuteNonQuery("Vacuum;"); db.Execute("Vacuum;");
} }
_logger.Info("{0} database compressed", _databaseName); _logger.Info("{0} database compressed", _databaseName);
} }

View File

@ -1,7 +1,5 @@
using System; using System;
using System.Data.SQLite; using System.Data.SQLite;
using Marr.Data;
using Marr.Data.Reflection;
using NLog; using NLog;
using NzbDrone.Common.Composition; using NzbDrone.Common.Composition;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
@ -31,7 +29,6 @@ static DbFactory()
{ {
InitializeEnvironment(); InitializeEnvironment();
MapRepository.Instance.ReflectionStrategy = new SimpleReflectionStrategy();
TableMapping.Map(); TableMapping.Map();
} }
@ -98,12 +95,11 @@ public IDatabase Create(MigrationContext migrationContext)
var db = new Database(migrationContext.MigrationType.ToString(), () => var db = new Database(migrationContext.MigrationType.ToString(), () =>
{ {
var dataMapper = new DataMapper(SQLiteFactory.Instance, connectionString) var conn = SQLiteFactory.Instance.CreateConnection();
{ conn.ConnectionString = connectionString;
SqlMode = SqlModes.Text, conn.Open();
};
return dataMapper; return conn;
}); });
return db; return db;

View File

@ -0,0 +1,146 @@
/* This class was copied from Mehfuz's LinqExtender project, which is available from github.
* http://mehfuzh.github.com/LinqExtender/
*/
using System;
using System.Linq.Expressions;
namespace NzbDrone.Core.Datastore
{
///<summary>
/// Expression visitor
///</summary>
public class ExpressionVisitor
{
/// <summary>
/// Visits expression and delegates call to different to branch.
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
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());
}
/// <summary>
/// Visits the constance expression. To be implemented by user.
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
protected virtual Expression VisitConstant(ConstantExpression expression)
{
return expression;
}
/// <summary>
/// Visits the memeber access expression. To be implemented by user.
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
protected virtual Expression VisitMemberAccess(MemberExpression expression)
{
return expression;
}
/// <summary>
/// Visits the method call expression. To be implemented by user.
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
protected virtual Expression VisitMethodCall(MethodCallExpression expression)
{
throw new NotImplementedException();
}
/// <summary>
/// Visits the binary expression.
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
protected virtual Expression VisitBinary(BinaryExpression expression)
{
Visit(expression.Left);
Visit(expression.Right);
return expression;
}
/// <summary>
/// Visits the unary expression.
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
protected virtual Expression VisitUnary(UnaryExpression expression)
{
Visit(expression.Operand);
return expression;
}
/// <summary>
/// Visits the lamda expression.
/// </summary>
/// <param name="lambdaExpression"></param>
/// <returns></returns>
protected virtual Expression VisitLamda(LambdaExpression lambdaExpression)
{
Visit(lambdaExpression.Body);
return lambdaExpression;
}
private Expression VisitParameter(ParameterExpression expression)
{
return expression;
}
}
}

View File

@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using Dapper;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Serializer;
namespace NzbDrone.Core.Datastore
{
public static class SqlBuilderExtensions
{
public static bool LogSql { get; set; }
public static SqlBuilder SelectAll(this SqlBuilder builder)
{
return builder.Select("*");
}
public static SqlBuilder SelectCount(this SqlBuilder builder)
{
return builder.Select("COUNT(*)");
}
public static SqlBuilder Where<TModel>(this SqlBuilder builder, Expression<Func<TModel, bool>> filter)
{
var wb = new WhereBuilder(filter);
return builder.Where(wb.ToString(), wb.Parameters);
}
public static SqlBuilder OrWhere<TModel>(this SqlBuilder builder, Expression<Func<TModel, bool>> 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<string, object> ToDictionary(this DynamicParameters dynamicParams)
{
var argsDictionary = new Dictionary<string, object>();
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<Object>;
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<string, object> 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;
}
}
}

View File

@ -1,63 +0,0 @@
using System.Reflection;
using Marr.Data;
using Marr.Data.Mapping;
using NzbDrone.Common.Reflection;
using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Datastore.Extensions
{
public static class MappingExtensions
{
public static ColumnMapBuilder<T> MapResultSet<T>(this FluentMappings.MappingsFluentEntity<T> mapBuilder) where T : ResultSet, new()
{
return mapBuilder
.Columns
.AutoMapPropertiesWhere(IsMappableProperty);
}
public static ColumnMapBuilder<T> RegisterDefinition<T>(this FluentMappings.MappingsFluentEntity<T> mapBuilder, string tableName = null) where T : ProviderDefinition, new()
{
return RegisterModel(mapBuilder, tableName).Ignore(c => c.ImplementationName);
}
public static ColumnMapBuilder<T> RegisterModel<T>(this FluentMappings.MappingsFluentEntity<T> 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<T> AutoMapChildModels<T>(this ColumnMapBuilder<T> 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;
}
}
}

View File

@ -1,44 +0,0 @@
using System;
using System.Linq;
using System.Linq.Expressions;
namespace NzbDrone.Core.Datastore.Extensions
{
public static class PagingSpecExtensions
{
public static Expression<Func<TModel, object>> OrderByClause<TModel>(this PagingSpec<TModel> pagingSpec)
{
return CreateExpression<TModel>(pagingSpec.SortKey);
}
public static int PagingOffset<TModel>(this PagingSpec<TModel> pagingSpec)
{
return (pagingSpec.Page - 1)*pagingSpec.PageSize;
}
public static Marr.Data.QGen.SortDirection ToSortDirection<TModel>(this PagingSpec<TModel> pagingSpec)
{
if (pagingSpec.SortDirection == SortDirection.Descending) return Marr.Data.QGen.SortDirection.Desc;
return Marr.Data.QGen.SortDirection.Asc;
}
private static Expression<Func<TModel, object>> CreateExpression<TModel>(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<Func<TModel, object>>(expressionBody, parameterExpression);
}
}
}

View File

@ -1,51 +0,0 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using Marr.Data;
using Marr.Data.Mapping;
namespace NzbDrone.Core.Datastore.Extensions
{
public static class RelationshipExtensions
{
public static RelationshipBuilder<TParent> HasOne<TParent, TChild>(this RelationshipBuilder<TParent> relationshipBuilder, Expression<Func<TParent, LazyLoaded<TChild>>> portalExpression, Func<TParent, int> 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<TChild>().Where(c => c.Id == id).SingleOrDefault();
}
);
}
public static RelationshipBuilder<TParent> Relationship<TParent>(this ColumnMapBuilder<TParent> mapBuilder)
{
return mapBuilder.Relationships.AutoMapComplexTypeProperties<ILazyLoaded>();
}
public static RelationshipBuilder<TParent> HasMany<TParent, TChild>(this RelationshipBuilder<TParent> relationshipBuilder, Expression<Func<TParent, LazyList<TChild>>> portalExpression, Func<TChild, int> parentIdSelector)
where TParent : ModelBase
where TChild : ModelBase
{
return relationshipBuilder.For(portalExpression.GetMemberName())
.LazyLoad((db, parent) => db.Query<TChild>().Where(c => parentIdSelector(c) == parent.Id).ToList());
}
private static string GetMemberName<T, TMember>(this Expression<Func<T, TMember>> member)
{
var expression = member.Body as MemberExpression;
if (expression == null)
{
expression = (MemberExpression)((UnaryExpression)member.Body).Operand;
}
return expression.Member.Name;
}
}
}

View File

@ -1,30 +0,0 @@
using System.Collections.Generic;
using Marr.Data;
namespace NzbDrone.Core.Datastore
{
public class LazyList<T> : LazyLoaded<List<T>>
{
public LazyList()
: this(new List<T>())
{
}
public LazyList(IEnumerable<T> items)
: base(new List<T>(items))
{
}
public static implicit operator LazyList<T>(List<T> val)
{
return new LazyList<T>(val);
}
public static implicit operator List<T>(LazyList<T> lazy)
{
return lazy.Value;
}
}
}

View File

@ -1,5 +1,5 @@
using System; using System;
using Marr.Data; using System.Data;
namespace NzbDrone.Core.Datastore namespace NzbDrone.Core.Datastore
{ {
@ -17,9 +17,9 @@ public LogDatabase(IDatabase database)
_database = database; _database = database;
} }
public IDataMapper GetDataMapper() public IDbConnection OpenConnection()
{ {
return _database.GetDataMapper(); return _database.OpenConnection();
} }
public Version Version => _database.Version; public Version Version => _database.Version;

View File

@ -1,5 +1,5 @@
using System; using System;
using Marr.Data; using System.Data;
namespace NzbDrone.Core.Datastore namespace NzbDrone.Core.Datastore
{ {
@ -17,9 +17,9 @@ public MainDatabase(IDatabase database)
_database = database; _database = database;
} }
public IDataMapper GetDataMapper() public IDbConnection OpenConnection()
{ {
return _database.GetDataMapper(); return _database.OpenConnection();
} }
public Version Version => _database.Version; public Version Version => _database.Version;

View File

@ -26,7 +26,7 @@ protected override void MainDbUpgrade()
private void ConvertQualityProfiles(IDbConnection conn, IDbTransaction tran) private void ConvertQualityProfiles(IDbConnection conn, IDbTransaction tran)
{ {
var qualityProfileItemConverter = new EmbeddedDocumentConverter(new QualityIntConverter()); var qualityProfileItemConverter = new EmbeddedDocumentConverter<List<ProfileQualityItem>>(new QualityIntConverter());
// Convert 'Allowed' column in QualityProfiles from Json List<object> to Json List<int> (int = Quality) // Convert 'Allowed' column in QualityProfiles from Json List<object> to Json List<int> (int = Quality)
using (IDbCommand qualityProfileCmd = conn.CreateCommand()) using (IDbCommand qualityProfileCmd = conn.CreateCommand())
@ -44,13 +44,12 @@ private void ConvertQualityProfiles(IDbConnection conn, IDbTransaction tran)
var items = Quality.DefaultQualityDefinitions.OrderBy(v => v.Weight).Select(v => new ProfileQualityItem { Quality = v.Quality, Allowed = allowed.Contains(v.Quality) }).ToList(); var 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()) using (IDbCommand updateCmd = conn.CreateCommand())
{ {
updateCmd.Transaction = tran; updateCmd.Transaction = tran;
updateCmd.CommandText = "UPDATE QualityProfiles SET Items = ? WHERE Id = ?"; updateCmd.CommandText = "UPDATE QualityProfiles SET Items = ? WHERE Id = ?";
updateCmd.AddParameter(allowedNewJson); var param = updateCmd.CreateParameter();
qualityProfileItemConverter.SetValue(param, items);
updateCmd.AddParameter(id); updateCmd.AddParameter(id);
updateCmd.ExecuteNonQuery(); updateCmd.ExecuteNonQuery();
@ -70,7 +69,7 @@ private void ConvertQualityModels(IDbConnection conn, IDbTransaction tran)
private void ConvertQualityModel(IDbConnection conn, IDbTransaction tran, string tableName) private void ConvertQualityModel(IDbConnection conn, IDbTransaction tran, string tableName)
{ {
var qualityModelConverter = new EmbeddedDocumentConverter(new QualityIntConverter()); var qualityModelConverter = new EmbeddedDocumentConverter<DestinationQualityModel036>(new QualityIntConverter());
using (IDbCommand qualityModelCmd = conn.CreateCommand()) using (IDbCommand qualityModelCmd = conn.CreateCommand())
{ {
@ -89,17 +88,18 @@ private void ConvertQualityModel(IDbConnection conn, IDbTransaction tran, string
continue; continue;
} }
var qualityNewJson = qualityModelConverter.ToDB(new DestinationQualityModel036 var qualityNew = new DestinationQualityModel036
{ {
Quality = sourceQuality.Quality.Id, Quality = sourceQuality.Quality.Id,
Proper = sourceQuality.Proper Proper = sourceQuality.Proper
}); };
using (IDbCommand updateCmd = conn.CreateCommand()) using (IDbCommand updateCmd = conn.CreateCommand())
{ {
updateCmd.Transaction = tran; updateCmd.Transaction = tran;
updateCmd.CommandText = "UPDATE " + tableName + " SET Quality = ? WHERE Quality = ?"; updateCmd.CommandText = "UPDATE " + tableName + " SET Quality = ? WHERE Quality = ?";
updateCmd.AddParameter(qualityNewJson); var param = updateCmd.CreateParameter();
qualityModelConverter.SetValue(param, qualityNew);
updateCmd.AddParameter(qualityJson); updateCmd.AddParameter(qualityJson);
updateCmd.ExecuteNonQuery(); updateCmd.ExecuteNonQuery();

View File

@ -1,9 +1,5 @@
using FluentMigrator; using FluentMigrator;
using Marr.Data.Mapping;
using NzbDrone.Core.Datastore.Migration.Framework; using NzbDrone.Core.Datastore.Migration.Framework;
using NzbDrone.Core.Movies;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Datastore.Extensions;
namespace NzbDrone.Core.Datastore.Migration namespace NzbDrone.Core.Datastore.Migration
{ {

View File

@ -1,13 +1,6 @@
using System.Collections.Generic; using System.Data;
using System.Data;
using System.Linq;
using FluentMigrator; 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.Datastore.Migration.Framework;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.Datastore.Migration namespace NzbDrone.Core.Datastore.Migration
{ {

View File

@ -31,7 +31,7 @@ protected override void MainDbUpgrade()
private void UpdateLanguage(IDbConnection conn, IDbTransaction tran) private void UpdateLanguage(IDbConnection conn, IDbTransaction tran)
{ {
var LanguageConverter = new EmbeddedDocumentConverter(new LanguageIntConverter()); var LanguageConverter = new EmbeddedDocumentConverter<List<Language>>(new LanguageIntConverter());
var profileLanguages = new Dictionary<int, int>(); var profileLanguages = new Dictionary<int, int>();
using (IDbCommand getProfileCmd = conn.CreateCommand()) using (IDbCommand getProfileCmd = conn.CreateCommand())
@ -77,7 +77,7 @@ private void UpdateLanguage(IDbConnection conn, IDbTransaction tran)
foreach (var group in movieLanguages.GroupBy(v => v.Value, v => v.Key)) foreach (var group in movieLanguages.GroupBy(v => v.Value, v => v.Key))
{ {
var languageJson = LanguageConverter.ToDB(new List<Language> { Language.FindById(group.Key) }); var language = new List<Language> { Language.FindById(group.Key) };
var movieIds = group.Select(v => v.ToString()).Join(","); var movieIds = group.Select(v => v.ToString()).Join(",");
@ -85,7 +85,8 @@ private void UpdateLanguage(IDbConnection conn, IDbTransaction tran)
{ {
updateMovieFilesCmd.Transaction = tran; updateMovieFilesCmd.Transaction = tran;
updateMovieFilesCmd.CommandText = $"UPDATE MovieFiles SET Languages = ? WHERE MovieId IN ({movieIds})"; updateMovieFilesCmd.CommandText = $"UPDATE MovieFiles SET Languages = ? WHERE MovieId IN ({movieIds})";
updateMovieFilesCmd.AddParameter(languageJson); var param = updateMovieFilesCmd.CreateParameter();
LanguageConverter.SetValue(param, language);
updateMovieFilesCmd.ExecuteNonQuery(); updateMovieFilesCmd.ExecuteNonQuery();
} }
@ -94,7 +95,8 @@ private void UpdateLanguage(IDbConnection conn, IDbTransaction tran)
{ {
updateHistoryCmd.Transaction = tran; updateHistoryCmd.Transaction = tran;
updateHistoryCmd.CommandText = $"UPDATE History SET Languages = ? WHERE MovieId IN ({movieIds})"; updateHistoryCmd.CommandText = $"UPDATE History SET Languages = ? WHERE MovieId IN ({movieIds})";
updateHistoryCmd.AddParameter(languageJson); var param = updateHistoryCmd.CreateParameter();
LanguageConverter.SetValue(param, language);
updateHistoryCmd.ExecuteNonQuery(); updateHistoryCmd.ExecuteNonQuery();
} }
@ -103,7 +105,8 @@ private void UpdateLanguage(IDbConnection conn, IDbTransaction tran)
{ {
updateBlacklistCmd.Transaction = tran; updateBlacklistCmd.Transaction = tran;
updateBlacklistCmd.CommandText = $"UPDATE Blacklist SET Languages = ? WHERE MovieId IN ({movieIds})"; updateBlacklistCmd.CommandText = $"UPDATE Blacklist SET Languages = ? WHERE MovieId IN ({movieIds})";
updateBlacklistCmd.AddParameter(languageJson); var param = updateBlacklistCmd.CreateParameter();
LanguageConverter.SetValue(param, language);
updateBlacklistCmd.ExecuteNonQuery(); updateBlacklistCmd.ExecuteNonQuery();
} }

View File

@ -0,0 +1,20 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(162)]
public class fix_profile_format_default : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
// badValue was set as default in 147 but it's invalid JSON
// so System.Text.Json refuses to parse it (even though Newtonsoft allows it)
var badValue = "[{format:0, allowed:true}]";
var defaultValue = "[{\"format\":0, \"allowed\":true}]";
Alter.Column("FormatItems").OnTable("Profiles").AsString().WithDefaultValue(defaultValue);
Update.Table("Profiles").Set(new { FormatItems = defaultValue }).Where( new { FormatItems = badValue });
}
}
}

View File

@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Dapper;
using NzbDrone.Common.Reflection;
namespace NzbDrone.Core.Datastore
{
public static class MappingExtensions
{
public static PropertyInfo GetMemberName<T>(this Expression<Func<T, object>> 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<Type, List<PropertyInfo>>();
TableMap = new Dictionary<Type, string>();
}
public Dictionary<Type, List<PropertyInfo>> IgnoreList { get; set; }
public Dictionary<Type, string> TableMap { get; set; }
public ColumnMapper<TEntity> Entity<TEntity>(string tableName)
{
TableMap.Add(typeof(TEntity), tableName);
if (IgnoreList.TryGetValue(typeof(TEntity), out var list))
{
return new ColumnMapper<TEntity>(list);
}
list = new List<PropertyInfo>();
IgnoreList[typeof(TEntity)] = list;
return new ColumnMapper<TEntity>(list);
}
public List<PropertyInfo> ExcludeProperties(Type x)
{
return IgnoreList.ContainsKey(x) ? IgnoreList[x] : new List<PropertyInfo>();
}
public string TableNameMapping(Type x)
{
return TableMap.ContainsKey(x) ? TableMap[x] : null;
}
}
public class ColumnMapper<T>
{
private readonly List<PropertyInfo> _ignoreList;
public ColumnMapper(List<PropertyInfo> ignoreList)
{
_ignoreList = ignoreList;
}
public ColumnMapper<T> AutoMapPropertiesWhere(Func<PropertyInfo, bool> predicate)
{
Type entityType = typeof(T);
var properties = entityType.GetProperties();
_ignoreList.AddRange(properties.Where(x => !predicate(x)));
return this;
}
public ColumnMapper<T> RegisterModel()
{
return AutoMapPropertiesWhere(IsMappableProperty);
}
public ColumnMapper<T> Ignore(Expression<Func<T, object>> 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;
}
}
}

View File

@ -1,148 +1,138 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Marr.Data; using Dapper;
using Marr.Data.Mapping;
using NzbDrone.Common.Reflection; using NzbDrone.Common.Reflection;
using NzbDrone.Core.Authentication;
using NzbDrone.Core.Blacklisting; using NzbDrone.Core.Blacklisting;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Datastore.Extensions;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Instrumentation;
using NzbDrone.Core.Jobs;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Profiles.Delay;
using NzbDrone.Core.RemotePathMappings;
using NzbDrone.Core.Notifications;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Restrictions;
using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Tags;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Movies;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Authentication;
using NzbDrone.Core.CustomFilters; using NzbDrone.Core.CustomFilters;
using NzbDrone.Core.CustomFormats; using NzbDrone.Core.CustomFormats;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.Extras.Metadata; using NzbDrone.Core.Extras.Metadata;
using NzbDrone.Core.Extras.Metadata.Files; using NzbDrone.Core.Extras.Metadata.Files;
using NzbDrone.Core.Extras.Others; using NzbDrone.Core.Extras.Others;
using NzbDrone.Core.Extras.Subtitles; 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.Messaging.Commands;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Movies.AlternativeTitles;
using NzbDrone.Core.NetImport; using NzbDrone.Core.NetImport;
using NzbDrone.Core.NetImport.ImportExclusions; using NzbDrone.Core.NetImport.ImportExclusions;
using NzbDrone.Core.Movies.AlternativeTitles; using NzbDrone.Core.Notifications;
using NzbDrone.Core.Languages; using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Profiles.Delay;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.RemotePathMappings;
using NzbDrone.Core.Restrictions;
using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Tags;
using NzbDrone.Core.ThingiProvider;
using static Dapper.SqlMapper;
namespace NzbDrone.Core.Datastore namespace NzbDrone.Core.Datastore
{ {
public static class TableMapping public static class TableMapping
{ {
private static readonly FluentMappings Mapper = new FluentMappings(true); static TableMapping()
{
Mapper = new TableMapper();
}
public static TableMapper Mapper { get; private set; }
public static void Map() public static void Map()
{ {
RegisterMappers(); RegisterMappers();
Mapper.Entity<Config>().RegisterModel("Config"); Mapper.Entity<Config>("Config").RegisterModel();
Mapper.Entity<RootFolder>().RegisterModel("RootFolders") Mapper.Entity<RootFolder>("RootFolders").RegisterModel()
.Ignore(r => r.Accessible) .Ignore(r => r.Accessible)
.Ignore(r => r.FreeSpace) .Ignore(r => r.FreeSpace)
.Ignore(r => r.TotalSpace); .Ignore(r => r.TotalSpace);
Mapper.Entity<ScheduledTask>().RegisterModel("ScheduledTasks"); Mapper.Entity<ScheduledTask>("ScheduledTasks").RegisterModel();
Mapper.Entity<IndexerDefinition>().RegisterDefinition("Indexers") Mapper.Entity<IndexerDefinition>("Indexers").RegisterModel()
.Ignore(x => x.ImplementationName)
.Ignore(i => i.Enable) .Ignore(i => i.Enable)
.Ignore(i => i.Protocol) .Ignore(i => i.Protocol)
.Ignore(i => i.SupportsRss) .Ignore(i => i.SupportsRss)
.Ignore(i => i.SupportsSearch) .Ignore(i => i.SupportsSearch)
.Ignore(d => d.Tags); .Ignore(d => d.Tags);
Mapper.Entity<NetImportDefinition>().RegisterDefinition("NetImport") Mapper.Entity<NetImportDefinition>("NetImport").RegisterModel()
.Ignore(i => i.Enable) .Ignore(x => x.ImplementationName)
.Relationship() .Ignore(i => i.Enable);
.HasOne(n => n.Profile, n => n.ProfileId);
Mapper.Entity<NotificationDefinition>().RegisterDefinition("Notifications") Mapper.Entity<NotificationDefinition>("Notifications").RegisterModel()
.Ignore(x => x.ImplementationName)
.Ignore(i => i.SupportsOnGrab) .Ignore(i => i.SupportsOnGrab)
.Ignore(i => i.SupportsOnDownload) .Ignore(i => i.SupportsOnDownload)
.Ignore(i => i.SupportsOnUpgrade) .Ignore(i => i.SupportsOnUpgrade)
.Ignore(i => i.SupportsOnRename) .Ignore(i => i.SupportsOnRename)
.Ignore(i => i.SupportsOnHealthIssue); .Ignore(i => i.SupportsOnHealthIssue);
Mapper.Entity<MetadataDefinition>().RegisterDefinition("Metadata") Mapper.Entity<MetadataDefinition>("Metadata").RegisterModel()
.Ignore(x => x.ImplementationName)
.Ignore(d => d.Tags); .Ignore(d => d.Tags);
Mapper.Entity<DownloadClientDefinition>().RegisterDefinition("DownloadClients") Mapper.Entity<DownloadClientDefinition>("DownloadClients").RegisterModel()
.Ignore(x => x.ImplementationName)
.Ignore(d => d.Protocol) .Ignore(d => d.Protocol)
.Ignore(d => d.Tags); .Ignore(d => d.Tags);
Mapper.Entity<History.History>().RegisterModel("History") Mapper.Entity<History.History>("History").RegisterModel();
.AutoMapChildModels();
Mapper.Entity<MovieFile>().RegisterModel("MovieFiles") Mapper.Entity<MovieFile>("MovieFiles").RegisterModel()
.Ignore(f => f.Path) .Ignore(f => f.Path);
.Relationships.AutoMapICollectionOrComplexProperties()
.For("Movie")
.LazyLoad(condition: parent => parent.Id > 0,
query: (db, parent) => db.Query<Movie>().Where(c => c.MovieFileId == parent.Id).ToList())
.HasOne(file => file.Movie, file => file.MovieId);
Mapper.Entity<Movie>().RegisterModel("Movies") Mapper.Entity<Movie>("Movies").RegisterModel()
.Ignore(s => s.RootFolderPath) .Ignore(s => s.RootFolderPath)
.Ignore(m => m.Actors) .Ignore(m => m.Actors);
.Relationship()
.HasOne(s => s.Profile, s => s.ProfileId);
//.HasOne(m => m.MovieFile, m => m.MovieFileId);
Mapper.Entity<AlternativeTitle>().RegisterModel("AlternativeTitles") Mapper.Entity<AlternativeTitle>("AlternativeTitles").RegisterModel();
.For(t => t.Id)
.SetAltName("AltTitle_Id")
.Relationship()
.HasOne(t => t.Movie, t => t.MovieId);
Mapper.Entity<ImportExclusion>("ImportExclusions").RegisterModel();
Mapper.Entity<ImportExclusion>().RegisterModel("ImportExclusions"); Mapper.Entity<QualityDefinition>("QualityDefinitions").RegisterModel()
Mapper.Entity<QualityDefinition>().RegisterModel("QualityDefinitions")
.Ignore(d => d.GroupName) .Ignore(d => d.GroupName)
.Ignore(d => d.Weight) .Ignore(d => d.Weight);
.Relationship();
Mapper.Entity<CustomFormat>().RegisterModel("CustomFormats") Mapper.Entity<CustomFormatDefinition>("CustomFormats").RegisterModel();
.Relationship();
Mapper.Entity<Profile>().RegisterModel("Profiles"); Mapper.Entity<Profile>("Profiles").RegisterModel();
Mapper.Entity<Log>().RegisterModel("Logs"); Mapper.Entity<Log>("Logs").RegisterModel();
Mapper.Entity<NamingConfig>().RegisterModel("NamingConfig"); Mapper.Entity<NamingConfig>("NamingConfig").RegisterModel();
Mapper.Entity<Blacklist>().RegisterModel("Blacklist"); Mapper.Entity<Blacklist>("Blacklist").RegisterModel();
Mapper.Entity<MetadataFile>().RegisterModel("MetadataFiles"); Mapper.Entity<MetadataFile>("MetadataFiles").RegisterModel();
Mapper.Entity<SubtitleFile>().RegisterModel("SubtitleFiles"); Mapper.Entity<SubtitleFile>("SubtitleFiles").RegisterModel();
Mapper.Entity<OtherExtraFile>().RegisterModel("ExtraFiles"); Mapper.Entity<OtherExtraFile>("ExtraFiles").RegisterModel();
Mapper.Entity<PendingRelease>().RegisterModel("PendingReleases") Mapper.Entity<PendingRelease>("PendingReleases").RegisterModel()
.Ignore(e => e.RemoteMovie); .Ignore(e => e.RemoteMovie);
Mapper.Entity<RemotePathMapping>().RegisterModel("RemotePathMappings"); Mapper.Entity<RemotePathMapping>("RemotePathMappings").RegisterModel();
Mapper.Entity<Tag>().RegisterModel("Tags"); Mapper.Entity<Tag>("Tags").RegisterModel();
Mapper.Entity<Restriction>().RegisterModel("Restrictions"); Mapper.Entity<Restriction>("Restrictions").RegisterModel();
Mapper.Entity<DelayProfile>().RegisterModel("DelayProfiles"); Mapper.Entity<DelayProfile>("DelayProfiles").RegisterModel();
Mapper.Entity<User>().RegisterModel("Users"); Mapper.Entity<User>("Users").RegisterModel();
Mapper.Entity<CommandModel>().RegisterModel("Commands") Mapper.Entity<CommandModel>("Commands").RegisterModel()
.Ignore(c => c.Message); .Ignore(c => c.Message);
Mapper.Entity<IndexerStatus>().RegisterModel("IndexerStatus"); Mapper.Entity<IndexerStatus>("IndexerStatus").RegisterModel();
Mapper.Entity<DownloadClientStatus>().RegisterModel("DownloadClientStatus"); Mapper.Entity<DownloadClientStatus>("DownloadClientStatus").RegisterModel();
Mapper.Entity<CustomFilter>().RegisterModel("CustomFilters"); Mapper.Entity<CustomFilter>("CustomFilters").RegisterModel();
} }
private static void RegisterMappers() private static void RegisterMappers()
@ -150,32 +140,30 @@ private static void RegisterMappers()
RegisterEmbeddedConverter(); RegisterEmbeddedConverter();
RegisterProviderSettingConverter(); RegisterProviderSettingConverter();
MapRepository.Instance.RegisterTypeConverter(typeof(int), new Int32Converter()); SqlMapper.RemoveTypeMap(typeof(DateTime));
MapRepository.Instance.RegisterTypeConverter(typeof(double), new DoubleConverter()); SqlMapper.AddTypeHandler(new DapperUtcConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(DateTime), new UtcConverter()); SqlMapper.AddTypeHandler(new DapperQualityIntConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(bool), new BooleanIntConverter()); SqlMapper.AddTypeHandler(new DapperCustomFormatIntConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(Enum), new EnumIntConverter()); SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<ProfileQualityItem>>(new QualityIntConverter()));
MapRepository.Instance.RegisterTypeConverter(typeof(Quality), new QualityIntConverter()); SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<ProfileFormatItem>>(new CustomFormatIntConverter()));
MapRepository.Instance.RegisterTypeConverter(typeof(CustomFormat), new CustomFormatIntConverter()); SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<FormatTag>>(new QualityTagStringConverter()));
MapRepository.Instance.RegisterTypeConverter(typeof(List<ProfileQualityItem>), new EmbeddedDocumentConverter(new QualityIntConverter())); SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<QualityModel>(new CustomFormatIntConverter(), new QualityIntConverter()));
MapRepository.Instance.RegisterTypeConverter(typeof(List<ProfileFormatItem>), new EmbeddedDocumentConverter(new CustomFormatIntConverter())); SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<Dictionary<string, string>>());
MapRepository.Instance.RegisterTypeConverter(typeof(List<FormatTag>), new EmbeddedDocumentConverter(new QualityTagStringConverter())); SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<IDictionary<string, string>>());
MapRepository.Instance.RegisterTypeConverter(typeof(QualityModel), new EmbeddedDocumentConverter(new CustomFormatIntConverter(), new QualityIntConverter())); SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<int>>());
MapRepository.Instance.RegisterTypeConverter(typeof(Dictionary<string, string>), new EmbeddedDocumentConverter()); SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<KeyValuePair<string, int>>>());
MapRepository.Instance.RegisterTypeConverter(typeof(IDictionary<string, string>), new EmbeddedDocumentConverter()); SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<KeyValuePair<string, int>>());
MapRepository.Instance.RegisterTypeConverter(typeof(List<int>), new EmbeddedDocumentConverter()); SqlMapper.AddTypeHandler(new DapperLanguageIntConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(List<KeyValuePair<string, int>>), new EmbeddedDocumentConverter()); SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<Language>>(new LanguageIntConverter()));
MapRepository.Instance.RegisterTypeConverter(typeof(Language), new LanguageIntConverter()); SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<string>>());
MapRepository.Instance.RegisterTypeConverter(typeof(List<Language>), new EmbeddedDocumentConverter(new LanguageIntConverter())); SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<ParsedMovieInfo>());
MapRepository.Instance.RegisterTypeConverter(typeof(List<string>), new EmbeddedDocumentConverter()); SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<ReleaseInfo>());
MapRepository.Instance.RegisterTypeConverter(typeof(ParsedMovieInfo), new EmbeddedDocumentConverter()); SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<HashSet<int>>());
MapRepository.Instance.RegisterTypeConverter(typeof(ReleaseInfo), new EmbeddedDocumentConverter()); SqlMapper.AddTypeHandler(new OsPathConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(HashSet<int>), new EmbeddedDocumentConverter()); SqlMapper.RemoveTypeMap(typeof(Guid));
MapRepository.Instance.RegisterTypeConverter(typeof(OsPath), new OsPathConverter()); SqlMapper.RemoveTypeMap(typeof(Guid?));
MapRepository.Instance.RegisterTypeConverter(typeof(Guid), new GuidConverter()); SqlMapper.AddTypeHandler(new GuidConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(Command), new CommandConverter()); SqlMapper.AddTypeHandler(new CommandConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(TimeSpan), new TimeSpanConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(TimeSpan?), new TimeSpanConverter());
} }
private static void RegisterProviderSettingConverter() private static void RegisterProviderSettingConverter()
@ -185,7 +173,7 @@ private static void RegisterProviderSettingConverter()
var providerSettingConverter = new ProviderSettingConverter(); var providerSettingConverter = new ProviderSettingConverter();
foreach (var embeddedType in settingTypes) foreach (var embeddedType in settingTypes)
{ {
MapRepository.Instance.RegisterTypeConverter(embeddedType, providerSettingConverter); SqlMapper.AddTypeHandler(embeddedType, providerSettingConverter);
} }
} }
@ -193,16 +181,24 @@ private static void RegisterEmbeddedConverter()
{ {
var embeddedTypes = typeof(IEmbeddedDocument).Assembly.ImplementationsOf<IEmbeddedDocument>(); var embeddedTypes = typeof(IEmbeddedDocument).Assembly.ImplementationsOf<IEmbeddedDocument>();
var embeddedConvertor = new EmbeddedDocumentConverter(); var embeddedConverterDefinition = typeof(EmbeddedDocumentConverter<>).GetGenericTypeDefinition();
var genericListDefinition = typeof(List<>).GetGenericTypeDefinition(); var genericListDefinition = typeof(List<>).GetGenericTypeDefinition();
foreach (var embeddedType in embeddedTypes) foreach (var embeddedType in embeddedTypes)
{ {
var embeddedListType = genericListDefinition.MakeGenericType(embeddedType); var embeddedListType = genericListDefinition.MakeGenericType(embeddedType);
MapRepository.Instance.RegisterTypeConverter(embeddedType, embeddedConvertor); RegisterEmbeddedConverter(embeddedType, embeddedConverterDefinition);
MapRepository.Instance.RegisterTypeConverter(embeddedListType, embeddedConvertor); RegisterEmbeddedConverter(embeddedListType, embeddedConverterDefinition);
} }
}
private static void RegisterEmbeddedConverter(Type embeddedType, Type embeddedConverterDefinition)
{
var embeddedConverterType = embeddedConverterDefinition.MakeGenericType(embeddedType);
var converter = (ITypeHandler) Activator.CreateInstance(embeddedConverterType);
SqlMapper.AddTypeHandler(embeddedType, converter);
} }
} }
} }

View File

@ -0,0 +1,303 @@
using System;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;
using Dapper;
using System.Data;
using System.Linq;
using System.Collections.Generic;
namespace NzbDrone.Core.Datastore
{
public class WhereBuilder : ExpressionVisitor
{
private const DbType EnumerableMultiParameter = (DbType)(-1);
private readonly string _paramNamePrefix;
private int _paramCount = 0;
protected StringBuilder _sb;
public DynamicParameters Parameters { get; private set; }
public WhereBuilder(Expression filter)
{
_paramNamePrefix = Guid.NewGuid().ToString().Replace("-", "_");
Parameters = new DynamicParameters();
_sb = new StringBuilder();
if (filter != null)
{
base.Visit(filter);
}
}
private string AddParameter(object value, DbType? dbType = null)
{
_paramCount++;
var name = _paramNamePrefix + "_P" + _paramCount;
Parameters.Add(name, value, dbType);
return '@' + name;
}
protected override Expression VisitBinary(BinaryExpression expression)
{
_sb.Append("(");
Visit(expression.Left);
_sb.AppendFormat(" {0} ", Decode(expression));
Visit(expression.Right);
_sb.Append(")");
return expression;
}
protected override Expression VisitMethodCall(MethodCallExpression expression)
{
string method = expression.Method.Name;
switch (expression.Method.Name)
{
case "Contains":
ParseContainsExpression(expression);
break;
case "StartsWith":
ParseStartsWith(expression);
break;
case "EndsWith":
ParseEndsWith(expression);
break;
default:
string msg = string.Format("'{0}' expressions are not yet implemented in the where clause expression tree parser.", method);
throw new NotImplementedException(msg);
}
return expression;
}
protected override Expression VisitMemberAccess(MemberExpression expression)
{
string tableName = TableMapping.Mapper.TableNameMapping(expression.Expression.Type);
if (tableName != null)
{
_sb.Append($"\"{tableName}\".\"{expression.Member.Name}\"");
}
else
{
object value = GetRightValue(expression);
// string is IEnumerable<Char> 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();
}
}
}

View File

@ -62,7 +62,7 @@ private int CompareAll(params int[] comparers)
private int CompareQuality(DownloadDecision x, DownloadDecision y) private int CompareQuality(DownloadDecision x, DownloadDecision y)
{ {
return CompareAll(CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.Movie.Profile.Value.GetIndex(remoteMovie.ParsedMovieInfo.Quality.Quality)), return CompareAll(CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.Movie.Profile.GetIndex(remoteMovie.ParsedMovieInfo.Quality.Quality)),
CompareCustomFormats(x, y), CompareCustomFormats(x, y),
CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.ParsedMovieInfo.Quality.Revision.Real), CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.ParsedMovieInfo.Quality.Revision.Real),
CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.ParsedMovieInfo.Quality.Revision.Version)); CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.ParsedMovieInfo.Quality.Revision.Version));
@ -73,8 +73,8 @@ private int CompareCustomFormats(DownloadDecision x, DownloadDecision y)
var left = x.RemoteMovie.ParsedMovieInfo.Quality.CustomFormats.WithNone(); var left = x.RemoteMovie.ParsedMovieInfo.Quality.CustomFormats.WithNone();
var right = y.RemoteMovie.ParsedMovieInfo.Quality.CustomFormats; var right = y.RemoteMovie.ParsedMovieInfo.Quality.CustomFormats;
var leftIndicies = QualityModelComparer.GetIndicies(left, x.RemoteMovie.Movie.Profile.Value); var leftIndicies = QualityModelComparer.GetIndicies(left, x.RemoteMovie.Movie.Profile);
var rightIndicies = QualityModelComparer.GetIndicies(right, y.RemoteMovie.Movie.Profile.Value); var rightIndicies = QualityModelComparer.GetIndicies(right, y.RemoteMovie.Movie.Profile);
var leftTotal = leftIndicies.Sum(); var leftTotal = leftIndicies.Sum();
var rightTotal = rightIndicies.Sum(); var rightTotal = rightIndicies.Sum();
@ -87,8 +87,7 @@ private int ComparePreferredWords(DownloadDecision x, DownloadDecision y)
return CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => return CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie =>
{ {
var title = remoteMovie.Release.Title; var title = remoteMovie.Release.Title;
remoteMovie.Movie.Profile.LazyLoad(); var preferredWords = remoteMovie.Movie.Profile.PreferredTags;
var preferredWords = remoteMovie.Movie.Profile.Value.PreferredTags;
if (preferredWords == null) if (preferredWords == null)
{ {

View File

@ -22,7 +22,7 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se
{ {
var formats = subject.ParsedMovieInfo.Quality.CustomFormats.WithNone(); var formats = subject.ParsedMovieInfo.Quality.CustomFormats.WithNone();
_logger.Debug("Checking if report meets custom format requirements. {0}", formats.ToExtendedString()); _logger.Debug("Checking if report meets custom format requirements. {0}", formats.ToExtendedString());
var notAllowedFormats = subject.Movie.Profile.Value.FormatItems.Where(v => v.Allowed == false).Select(f => f.Format).ToList(); var notAllowedFormats = subject.Movie.Profile.FormatItems.Where(v => v.Allowed == false).Select(f => f.Format).ToList();
var notWantedFormats = notAllowedFormats.Intersect(formats); var notWantedFormats = notAllowedFormats.Intersect(formats);
if (notWantedFormats.Any()) if (notWantedFormats.Any())
{ {

View File

@ -22,7 +22,7 @@ public CutoffSpecification(UpgradableSpecification qualityUpgradableSpecificatio
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{ {
var profile = subject.Movie.Profile.Value; var profile = subject.Movie.Profile;
if (subject.Movie.MovieFile != null) if (subject.Movie.MovieFile != null)
{ {
@ -33,7 +33,7 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se
var qualityCutoffIndex = profile.GetIndex(profile.Cutoff); var qualityCutoffIndex = profile.GetIndex(profile.Cutoff);
var qualityCutoff = profile.Items[qualityCutoffIndex.Index]; var qualityCutoff = profile.Items[qualityCutoffIndex.Index];
return Decision.Reject("Existing file meets cutoff: {0} - {1}", qualityCutoff, subject.Movie.Profile.Value.Cutoff); return Decision.Reject("Existing file meets cutoff: {0} - {1}", qualityCutoff, subject.Movie.Profile.Cutoff);
} }
} }

View File

@ -19,7 +19,7 @@ public LanguageSpecification(Logger logger)
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
{ {
var wantedLanguage = subject.Movie.Profile.Value.Language; var wantedLanguage = subject.Movie.Profile.Language;
if (wantedLanguage == Language.Any) if (wantedLanguage == Language.Any)
{ {

View File

@ -20,7 +20,7 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se
{ {
_logger.Debug("Checking if report meets quality requirements. {0}", subject.ParsedMovieInfo.Quality); _logger.Debug("Checking if report meets quality requirements. {0}", subject.ParsedMovieInfo.Quality);
var profile = subject.Movie.Profile.Value; var profile = subject.Movie.Profile;
var qualityIndex = profile.GetIndex(subject.ParsedMovieInfo.Quality.Quality); var qualityIndex = profile.GetIndex(subject.ParsedMovieInfo.Quality.Quality);
var qualityOrGroup = profile.Items[qualityIndex.Index]; var qualityOrGroup = profile.Items[qualityIndex.Index];

View File

@ -35,7 +35,7 @@ public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCrit
foreach (var queueItem in matchingMovies) foreach (var queueItem in matchingMovies)
{ {
var remoteMovie = queueItem.RemoteMovie; var remoteMovie = queueItem.RemoteMovie;
var qualityProfile = subject.Movie.Profile.Value; var qualityProfile = subject.Movie.Profile;
_logger.Debug("Checking if existing release in queue meets cutoff. Queued quality is: {0}", remoteMovie.ParsedMovieInfo.Quality); _logger.Debug("Checking if existing release in queue meets cutoff. Queued quality is: {0}", remoteMovie.ParsedMovieInfo.Quality);

View File

@ -37,14 +37,14 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se
return Decision.Accept(); return Decision.Accept();
} }
var profile = subject.Movie.Profile.Value; var profile = subject.Movie.Profile;
var delayProfile = _delayProfileService.BestForTags(subject.Movie.Tags); var delayProfile = _delayProfileService.BestForTags(subject.Movie.Tags);
var delay = delayProfile.GetProtocolDelay(subject.Release.DownloadProtocol); var delay = delayProfile.GetProtocolDelay(subject.Release.DownloadProtocol);
var isPreferredProtocol = subject.Release.DownloadProtocol == delayProfile.PreferredProtocol; var isPreferredProtocol = subject.Release.DownloadProtocol == delayProfile.PreferredProtocol;
// Preferred word count // Preferred word count
var title = subject.Release.Title; var title = subject.Release.Title;
var preferredWords = subject.Movie.Profile?.Value?.PreferredTags; var preferredWords = subject.Movie.Profile?.PreferredTags;
var preferredCount = 0; var preferredCount = 0;
if (preferredWords == null) if (preferredWords == null)

Some files were not shown because too many files have changed in this diff Show More