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.Linq;
using Nancy;
using Radarr.Http.Extensions;
using NzbDrone.Api.Movies;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Download;
using NzbDrone.Core.History;
using Radarr.Http;
using Radarr.Http.REST;
using NzbDrone.Core.DecisionEngine.Specifications;
namespace NzbDrone.Api.History
@ -37,7 +35,7 @@ namespace NzbDrone.Api.History
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;

View File

@ -29,7 +29,7 @@ namespace NzbDrone.Api.Indexers
if (decision.RemoteMovie.Movie != null)
{
release.QualityWeight = decision.RemoteMovie.Movie
.Profile.Value
.Profile
.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.MetadataSource;
using NzbDrone.Core.MetadataSource.RadarrAPI;
using NzbDrone.Core.Movies.AlternativeTitles;
using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Movies.Events;
using Radarr.Http;

View File

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

View File

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

View File

@ -7,6 +7,8 @@ using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Messaging.Events;
using Radarr.Host;
using NzbDrone.Test.Common;
using NzbDrone.Core.CustomFormats;
using System.Collections.Generic;
namespace NzbDrone.Common.Test
{
@ -20,6 +22,11 @@ namespace NzbDrone.Common.Test
container.Register<IMainDatabase>(new MainDatabase(null));
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);
var handlers = Subject.BuildAll<IHandle<ApplicationStartedEvent>>()
@ -28,4 +35,4 @@ namespace NzbDrone.Common.Test
handlers.Should().OnlyHaveUniqueItems();
}
}
}
}

View File

@ -133,10 +133,6 @@ namespace NzbDrone.Common.Extensions
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 = ", ")
{

View File

@ -5,71 +5,76 @@ using Moq;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Movies.Events;
using System.Collections.Generic;
namespace NzbDrone.Core.Test.BulkImport
{
[TestFixture]
public class AddMultiMoviesFixture : CoreTest<MovieService>
{
private List<Movie> fakeMovies;
[TestFixture]
public class AddMultiMoviesFixture : CoreTest<MovieService>
{
private List<Movie> fakeMovies;
[SetUp]
public void Setup()
{
fakeMovies = Builder<Movie>.CreateListOfSize(3).BuildList();
fakeMovies.ForEach(m =>
{
m.Path = null;
m.RootFolderPath = @"C:\Test\TV";
});
}
[SetUp]
public void Setup()
{
fakeMovies = Builder<Movie>.CreateListOfSize(3).BuildList();
fakeMovies.ForEach(m =>
{
m.Path = null;
m.RootFolderPath = @"C:\Test\TV";
});
}
[Test]
public void movies_added_event_should_have_proper_path()
{
Mocker.GetMock<IBuildFileNames>()
.Setup(s => s.GetMovieFolder(It.IsAny<Movie>(), null))
.Returns((Movie m, NamingConfig n) => m.Title);
[Test]
public void movies_added_event_should_have_proper_path()
{
Mocker.GetMock<IBuildFileNames>()
.Setup(s => s.GetMovieFolder(It.IsAny<Movie>(), null))
.Returns((Movie m, NamingConfig n) => m.Title);
var movies = Subject.AddMovies(fakeMovies);
Mocker.GetMock<IMovieRepository>().Setup(s => s.FindByTmdbId(It.IsAny<List<int>>()))
.Returns(new List<Movie>());
foreach (Movie movie in movies)
{
movie.Path.Should().NotBeNullOrEmpty();
}
var movies = Subject.AddMovies(fakeMovies);
//Subject.GetAllMovies().Should().HaveCount(3);
}
foreach (Movie movie in movies)
{
movie.Path.Should().NotBeNullOrEmpty();
}
[Test]
public void movies_added_should_ignore_already_added()
{
Mocker.GetMock<IBuildFileNames>()
.Setup(s => s.GetMovieFolder(It.IsAny<Movie>(), null))
.Returns((Movie m, NamingConfig n) => m.Title);
// Subject.GetAllMovies().Should().HaveCount(3);
}
Mocker.GetMock<IMovieRepository>().Setup(s => s.All()).Returns(new List<Movie> { fakeMovies[0] });
[Test]
public void movies_added_should_ignore_already_added()
{
Mocker.GetMock<IBuildFileNames>()
.Setup(s => s.GetMovieFolder(It.IsAny<Movie>(), null))
.Returns((Movie m, NamingConfig n) => m.Title);
var movies = Subject.AddMovies(fakeMovies);
Mocker.GetMock<IMovieRepository>().Setup(s => s.FindByTmdbId(It.IsAny<List<int>>()))
.Returns(new List<Movie> { fakeMovies[0] });
Mocker.GetMock<IMovieRepository>().Verify(v => v.InsertMany(It.Is<List<Movie>>(l => l.Count == 2)));
}
var movies = Subject.AddMovies(fakeMovies);
[Test]
public void movies_added_should_ignore_duplicates()
{
Mocker.GetMock<IBuildFileNames>()
.Setup(s => s.GetMovieFolder(It.IsAny<Movie>(), null))
.Returns((Movie m, NamingConfig n) => m.Title);
Mocker.GetMock<IMovieRepository>().Verify(v => v.InsertMany(It.Is<List<Movie>>(l => l.Count == 2)));
}
fakeMovies[2].TmdbId = fakeMovies[0].TmdbId;
[Test]
public void movies_added_should_ignore_duplicates()
{
Mocker.GetMock<IBuildFileNames>()
.Setup(s => s.GetMovieFolder(It.IsAny<Movie>(), null))
.Returns((Movie m, NamingConfig n) => m.Title);
var movies = Subject.AddMovies(fakeMovies);
Mocker.GetMock<IMovieRepository>().Setup(s => s.FindByTmdbId(It.IsAny<List<int>>()))
.Returns(new List<Movie>());
Mocker.GetMock<IMovieRepository>().Verify(v => v.InsertMany(It.Is<List<Movie>>(l => l.Count == 2)));
}
fakeMovies[2].TmdbId = fakeMovies[0].TmdbId;
}
}
var movies = Subject.AddMovies(fakeMovies);
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.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
@ -15,7 +16,7 @@ namespace NzbDrone.Core.Test.Datastore
BasicRepositoryFixture : DbTest<BasicRepository<ScheduledTask>, ScheduledTask>
{
private ScheduledTask _basicType;
private List<ScheduledTask> _basicList;
[SetUp]
public void Setup()
@ -25,15 +26,28 @@ namespace NzbDrone.Core.Test.Datastore
.With(c => c.Id = 0)
.With(c => c.LastExecution = DateTime.UtcNow)
.Build();
_basicList = Builder<ScheduledTask>
.CreateListOfSize(5)
.All()
.With(x => x.Id = 0)
.BuildList();
}
[Test]
public void should_be_able_to_add()
public void should_be_able_to_insert()
{
Subject.Insert(_basicType);
Subject.All().Should().HaveCount(1);
}
[Test]
public void should_be_able_to_insert_many()
{
Subject.InsertMany(_basicList);
Subject.All().Should().HaveCount(5);
}
[Test]
public void purge_should_delete_all()
{
@ -47,7 +61,6 @@ namespace NzbDrone.Core.Test.Datastore
}
[Test]
public void should_be_able_to_delete_model()
{
@ -58,6 +71,16 @@ namespace NzbDrone.Core.Test.Datastore
Subject.All().Should().BeEmpty();
}
[Test]
public void should_be_able_to_delete_many()
{
Subject.InsertMany(_basicList);
Subject.All().Should().HaveCount(5);
Subject.DeleteMany(_basicList.Take(2).ToList());
Subject.All().Should().HaveCount(3);
}
[Test]
public void should_be_able_to_find_by_id()
{
@ -67,6 +90,64 @@ namespace NzbDrone.Core.Test.Datastore
storeObject.Should().BeEquivalentTo(_basicType, o=>o.IncludingAllRuntimeProperties());
}
[Test]
public void should_be_able_to_find_by_multiple_id()
{
Subject.InsertMany(_basicList);
var storeObject = Subject.Get(_basicList.Take(2).Select(x => x.Id));
storeObject.Should().HaveCount(2);
}
[Test]
public void should_be_able_to_update()
{
Subject.Insert(_basicType);
_basicType.Interval = 999;
Subject.Update(_basicType);
Subject.All().First().Interval.Should().Be(999);
}
[Test]
public void should_be_able_to_update_many()
{
Subject.InsertMany(_basicList);
_basicList.ForEach(x => x.Interval = 999);
Subject.UpdateMany(_basicList);
Subject.All().All(x => x.Interval == 999);
}
[Test]
public void should_be_able_to_update_single_field()
{
Subject.Insert(_basicType);
_basicType.Interval = 999;
_basicType.LastExecution = DateTime.UtcNow;
Subject.SetFields(_basicType, x => x.Interval);
var dbValue = Subject.Single();
dbValue.Interval.Should().Be(999);
dbValue.LastExecution.Should().NotBe(_basicType.LastExecution);
}
[Test]
public void should_be_able_to_update_many_single_field()
{
Subject.InsertMany(_basicList);
_basicList.ForEach(x => x.Interval = 999);
_basicList.ForEach(x => x.LastExecution = DateTime.UtcNow);
Subject.SetFields(_basicList, x => x.Interval);
var dbValue = Subject.All().First();
dbValue.Interval.Should().Be(999);
dbValue.LastExecution.Should().NotBe(_basicType.LastExecution);
}
[Test]
public void should_be_able_to_get_single()
{
@ -86,7 +167,6 @@ namespace NzbDrone.Core.Test.Datastore
Assert.Throws<ModelNotFoundException>(() => Subject.Get(12));
}
[Test]
public void get_all_with_empty_db_should_return_empty_list()
{
@ -98,7 +178,6 @@ namespace NzbDrone.Core.Test.Datastore
public void should_be_able_to_call_ToList_on_empty_quariable()
{
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.Test.Framework;
using NzbDrone.Core.Movies.Commands;
using NUnit.Framework;
using FluentAssertions;
using System.Data.SQLite;
namespace NzbDrone.Core.Test.Datastore.Converters
{
[TestFixture]
public class CommandConverterFixture : CoreTest<CommandConverter>
{
SQLiteParameter param;
[SetUp]
public void Setup()
{
param = new SQLiteParameter();
}
[Test]
public void should_return_json_string_when_saving_boolean_to_db()
{
var command = new RefreshMovieCommand();
Subject.ToDB(command).Should().BeOfType<string>();
Subject.SetValue(param, command);
param.Value.Should().BeOfType<string>();
}
[Test]
public void should_return_null_for_null_value_when_saving_to_db()
{
Subject.ToDB(null).Should().Be(null);
}
[Test]
public void should_return_db_null_for_db_null_value_when_saving_to_db()
{
Subject.ToDB(DBNull.Value).Should().Be(DBNull.Value);
Subject.SetValue(param, null);
param.Value.Should().BeNull();
}
[Test]
public void should_return_command_when_getting_json_from_db()
{
var dataRecordMock = new Mock<IDataRecord>();
dataRecordMock.Setup(s => s.GetOrdinal("Name")).Returns(0);
dataRecordMock.Setup(s => s.GetString(0)).Returns("RefreshMovie");
var data = "{\"name\": \"RefreshMovie\"}";
var context = new ConverterContext
{
DataRecord = dataRecordMock.Object,
DbValue = new RefreshMovieCommand().ToJson()
};
Subject.FromDB(context).Should().BeOfType<RefreshMovieCommand>();
Subject.Parse(data).Should().BeOfType<RefreshMovieCommand>();
}
[Test]
public void should_return_null_for_null_value_when_getting_from_db()
{
var context = new ConverterContext
{
DbValue = DBNull.Value
};
Subject.FromDB(context).Should().Be(null);
Subject.Parse(null).Should().BeNull();
}
}
}

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

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

View File

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

View File

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

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

View File

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

View File

@ -2,10 +2,8 @@ using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Movies;
using System.Collections.Generic;
using NzbDrone.Core.Languages;
@ -20,48 +18,6 @@ namespace NzbDrone.Core.Test.Datastore
// This is kinda hacky here, since we are kinda testing if the QualityDef converter works as well.
}
[Ignore("MovieFile isnt lazy loaded anymore so this will fail.")]
[Test]
//TODO: Update this!
public void one_to_one()
{
var episodeFile = Builder<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]
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 Dapper;
using FluentAssertions;
using Marr.Data;
using NUnit.Framework;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Datastore.Extensions;
using NzbDrone.Core.Movies;
namespace NzbDrone.Core.Test.Datastore
{
[TestFixture]
public class MappingExtensionFixture
public class TableMapperFixture
{
public class EmbeddedType : IEmbeddedDocument
@ -39,19 +38,16 @@ namespace NzbDrone.Core.Test.Datastore
[SetUp]
public void Setup()
{
MapRepository.Instance.RegisterTypeConverter(typeof(List<EmbeddedType>), new EmbeddedDocumentConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(EmbeddedType), new EmbeddedDocumentConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(int), new Int32Converter());
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<EmbeddedType>>());
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<EmbeddedType>());
}
[Test]
public void test_mappable_types()
{
var properties = typeof(TypeWithAllMappableProperties).GetProperties();
properties.Should().NotBeEmpty();
properties.Should().OnlyContain(c => MappingExtensions.IsMappableProperty(c));
properties.Should().OnlyContain(c => ColumnMapper<int>.IsMappableProperty(c));
}
[Test]
@ -59,7 +55,7 @@ namespace NzbDrone.Core.Test.Datastore
{
var properties = typeof(TypeWithNoMappableProperties).GetProperties();
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 FizzWare.NBuilder;
using FluentAssertions;
using Marr.Data;
using NUnit.Framework;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Parser.Model;
@ -33,7 +32,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
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();
remoteMovie = new RemoteMovie
@ -49,7 +48,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_allow_if_format_is_defined_in_profile()
{
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();
}
@ -58,7 +57,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_deny_if_format_is_defined_in_profile()
{
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();
}
@ -67,7 +66,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_deny_if_one_format_is_defined_in_profile()
{
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();
}
@ -76,7 +75,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_allow_if_all_format_is_defined_in_profile()
{
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();
}
@ -85,7 +84,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_deny_if_no_format_was_parsed_and_none_not_in_profile()
{
remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<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();
}
@ -94,7 +93,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_allow_if_no_format_was_parsed_and_none_in_profile()
{
remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<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();
}

View File

@ -1,9 +1,7 @@
using System.Collections.Generic;
using FluentAssertions;
using Marr.Data;
using NUnit.Framework;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Test.Framework;
@ -29,10 +27,10 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
},
Movie = new Movie
{
Profile = new LazyLoaded<Profile>(new Profile
{
Language = Language.English
})
Profile = new Profile
{
Language = Language.English
}
}
};
}
@ -66,10 +64,10 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test]
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
});
};
WithGermanRelease();

View File

@ -1,6 +1,5 @@
using FizzWare.NBuilder;
using FluentAssertions;
using Marr.Data;
using NUnit.Framework;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Parser.Model;
@ -35,7 +34,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void Setup()
{
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();
remoteMovie = new RemoteMovie
@ -49,7 +48,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_allow_if_quality_is_defined_in_profile(Quality qualityType)
{
remoteMovie.ParsedMovieInfo.Quality.Quality = qualityType;
remoteMovie.Movie.Profile.Value.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p);
remoteMovie.Movie.Profile.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p);
Subject.IsSatisfiedBy(remoteMovie, null).Accepted.Should().BeTrue();
}
@ -58,7 +57,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_not_allow_if_quality_is_not_defined_in_profile(Quality qualityType)
{
remoteMovie.ParsedMovieInfo.Quality.Quality = qualityType;
remoteMovie.Movie.Profile.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();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore;
@ -16,27 +15,16 @@ namespace NzbDrone.Core.Test.Framework
public class DirectDataMapper : IDirectDataMapper
{
private readonly DbProviderFactory _providerFactory;
private readonly string _connectionString;
private readonly IDatabase _database;
public DirectDataMapper(IDatabase database)
{
var dataMapper = database.GetDataMapper();
_providerFactory = dataMapper.ProviderFactory;
_connectionString = dataMapper.ConnectionString;
_database = database;
}
private DbConnection OpenConnection()
{
var connection = _providerFactory.CreateConnection();
connection.ConnectionString = _connectionString;
connection.Open();
return connection;
}
public DataTable GetDataTable(string sql)
{
using (var connection = OpenConnection())
using (var connection = _database.OpenConnection())
{
using (var cmd = connection.CreateCommand())
{

View File

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

View File

@ -1,4 +1,5 @@
using FizzWare.NBuilder;
using System;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Authentication;
@ -14,7 +15,11 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
public void should_delete_additional_users()
{
var specs = Builder<User>.CreateListOfSize(5)
.BuildListOfNew();
.All()
.With(x => x.Id = 0)
.BuildListOfNew();
specs.ForEach(x => x.Identifier = Guid.NewGuid());
Db.InsertMany(specs);
@ -26,7 +31,9 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
public void should_not_delete_if_only_one_user()
{
var spec = Builder<User>.CreateNew()
.BuildNew();
.With(x => x.Id = 0)
.With(x => x.Identifier = Guid.NewGuid())
.BuildNew();
Db.Insert(spec);
@ -34,4 +41,4 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
AllStoredModels.Should().HaveCount(1);
}
}
}
}

View File

@ -14,7 +14,10 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
[Test]
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);
Subject.Clean();
@ -24,11 +27,15 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
[Test]
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);
var restrictions = Builder<Restriction>.CreateListOfSize(2)
.All()
.With(v => v.Id = 0)
.With(v => v.Tags.Add(tags[0].Id))
.BuildList();
Db.InsertMany(restrictions);

View File

@ -1,7 +1,6 @@
using System;
using System.Threading;
using FluentAssertions;
using Marr.Data;
using NLog;
using NUnit.Framework;
using NzbDrone.Common.Instrumentation;
@ -10,7 +9,6 @@ using NzbDrone.Core.Instrumentation;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
using NzbDrone.Test.Common.Categories;
namespace NzbDrone.Core.Test.InstrumentationTests
{
@ -64,23 +62,6 @@ namespace NzbDrone.Core.Test.InstrumentationTests
VerifyLog(StoredModel, LogLevel.Info);
}
[Test]
[Explicit]
[ManualTest]
public void perf_test()
{
MapRepository.Instance.EnableTraceLogging = false;
for (int i = 0; i < 1000; i++)
{
_logger.Info(Guid.NewGuid());
}
Thread.Sleep(1000);
MapRepository.Instance.EnableTraceLogging = true;
}
[Test]
public void write_log_exception()
{

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Movies.AlternativeTitles;
using NzbDrone.Core.Parser;
@ -36,7 +34,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
.With(m => m.Title = "Fack Ju Göthe 2")
.With(m => m.CleanTitle = "fackjugoethe2")
.With(m => m.Year = 2015)
.With(m => m.AlternativeTitles = new 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();
_parsedMovieInfo = new ParsedMovieInfo

View File

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

View File

@ -20,12 +20,12 @@ namespace NzbDrone.Core.Authentication
public User FindUser(string username)
{
return Query.Where(u => u.Username == username).SingleOrDefault();
return Query(x => x.Username == username).SingleOrDefault();
}
public User FindUser(Guid identifier)
{
return Query.Where(u => u.Identifier == identifier).SingleOrDefault();
return Query(x => x.Identifier == identifier).SingleOrDefault();
}
}
}

View File

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

View File

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

View File

@ -18,10 +18,9 @@ namespace NzbDrone.Core.Configuration
{
}
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)

View File

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

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 @@ using NzbDrone.Core.Messaging.Events;
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)
: base(database, eventAggregator)

View File

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

View File

@ -3,11 +3,10 @@ using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using Marr.Data;
using Marr.Data.QGen;
using NzbDrone.Common.Extensions;
using System.Reflection;
using System.Text;
using Dapper;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.Datastore.Extensions;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Datastore
@ -31,43 +30,89 @@ namespace NzbDrone.Core.Datastore
bool HasItems();
void DeleteMany(IEnumerable<int> ids);
void SetFields(TModel model, params Expression<Func<TModel, object>>[] properties);
void SetFields(IList<TModel> models, params Expression<Func<TModel, object>>[] properties);
TModel Single();
PagingSpec<TModel> GetPaged(PagingSpec<TModel> pagingSpec);
}
public class BasicRepository<TModel> : IBasicRepository<TModel> where TModel : ModelBase, new()
{
private readonly IDatabase _database;
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)
{
_database = database;
_eventAggregator = eventAggregator;
var type = typeof(TModel);
_table = TableMapping.Mapper.TableNameMapping(type);
_keyProperty = type.GetProperty(nameof(ModelBase.Id));
var excluded = TableMapping.Mapper.ExcludeProperties(type).Select(x => x.Name).ToList();
excluded.Add(_keyProperty.Name);
_properties = type.GetProperties().Where(x => !excluded.Contains(x.Name)).ToList();
_insertSql = GetInsertSql();
_updateSql = GetUpdateSql(_properties);
_selectTemplate = $"SELECT /**select**/ FROM {_table} /**join**/ /**innerjoin**/ /**leftjoin**/ /**where**/ /**orderby**/";
_deleteTemplate = $"DELETE FROM {_table} /**where**/";
}
protected QueryBuilder<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()
{
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)
{
var model = Query.Where(c => c.Id == id).SingleOrDefault();
var model = Query(x => x.Id == id).FirstOrDefault();
if (model == null)
{
@ -79,13 +124,16 @@ namespace NzbDrone.Core.Datastore
public IEnumerable<TModel> Get(IEnumerable<int> ids)
{
var idList = ids.ToList();
var query = string.Format("Id IN ({0})", string.Join(",", idList));
var result = Query.Where(m => m.Id.In(idList)).ToList();
if (result.Count != idList.Count())
if (!ids.Any())
{
throw new ApplicationException($"Expected query to return {idList.Count} rows but returned {result.Count}");
return new List<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;
@ -108,13 +156,70 @@ namespace NzbDrone.Core.Datastore
throw new InvalidOperationException("Can't insert model with existing ID " + model.Id);
}
DataMapper.Insert(model);
using (var conn = _database.OpenConnection())
{
model = Insert(conn, null, model);
}
ModelCreated(model);
return model;
}
private string GetInsertSql()
{
var sbColumnList = new StringBuilder(null);
for (var i = 0; i < _properties.Count; i++)
{
var property = _properties[i];
sbColumnList.AppendFormat("\"{0}\"", property.Name);
if (i < _properties.Count - 1)
sbColumnList.Append(", ");
}
var sbParameterList = new StringBuilder(null);
for (var i = 0; i < _properties.Count; i++)
{
var property = _properties[i];
sbParameterList.AppendFormat("@{0}", property.Name);
if (i < _properties.Count - 1)
sbParameterList.Append(", ");
}
return $"INSERT INTO {_table} ({sbColumnList.ToString()}) VALUES ({sbParameterList.ToString()}); SELECT last_insert_rowid() id";
}
private TModel Insert(IDbConnection connection, IDbTransaction transaction, TModel model)
{
var multi = connection.QueryMultiple(_insertSql, model, transaction);
var id = (int)multi.Read().First().id;
_keyProperty.SetValue(model, id);
return model;
}
public void InsertMany(IList<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)
{
if (model.Id == 0)
@ -122,52 +227,59 @@ namespace NzbDrone.Core.Datastore
throw new InvalidOperationException("Can't update model with ID 0");
}
DataMapper.Update(model, c => c.Id == model.Id);
using (var conn = _database.OpenConnection())
{
UpdateFields(conn, null, model, _properties);
}
ModelUpdated(model);
return model;
}
public void Delete(TModel model)
public void UpdateMany(IList<TModel> models)
{
Delete(model.Id);
}
public void InsertMany(IList<TModel> models)
{
using (var unitOfWork = new UnitOfWork(() => DataMapper))
if (models.Any(x => x.Id == 0))
{
unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted);
throw new InvalidOperationException("Can't update model with ID 0");
}
foreach (var model in models)
{
unitOfWork.DB.Insert(model);
}
unitOfWork.Commit();
using (var conn = _database.OpenConnection())
{
UpdateFields(conn, null, models, _properties);
}
}
public void UpdateMany(IList<TModel> models)
protected void Delete(Expression<Func<TModel, bool>> where)
{
using (var unitOfWork = new UnitOfWork(() => DataMapper))
Delete(Builder().Where<TModel>(where));
}
protected void Delete(SqlBuilder builder)
{
var sql = builder.AddTemplate(_deleteTemplate).LogQuery();
using (var conn = _database.OpenConnection())
{
unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted);
conn.Execute(sql.RawSql, sql.Parameters);
}
}
foreach (var model in models)
{
var localModel = model;
public void Delete(TModel model)
{
Delete(x => x.Id == model.Id);
}
if (model.Id == 0)
{
throw new InvalidOperationException("Can't update model with ID 0");
}
public void Delete(int id)
{
Delete(x => x.Id == id);
}
unitOfWork.DB.Update(model, c => c.Id == localModel.Id);
}
unitOfWork.Commit();
public void DeleteMany(IEnumerable<int> ids)
{
if (ids.Any())
{
Delete(x => ids.Contains(x.Id));
}
}
@ -187,31 +299,13 @@ namespace NzbDrone.Core.Datastore
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)
{
DataMapper.Delete<TModel>(c => c.Id > -1);
using (var conn = _database.OpenConnection())
{
conn.Execute($"DELETE FROM [{_table}]");
}
if (vacuum)
{
Vacuum();
@ -232,43 +326,115 @@ namespace NzbDrone.Core.Datastore
{
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>()
.Where(c => c.Id == model.Id)
.ColumnsIncluding(properties)
.Entity(model)
.Execute();
var propertiesToUpdate = properties.Select(x => x.GetMemberName()).ToList();
using (var conn = _database.OpenConnection())
{
UpdateFields(conn, null, model, propertiesToUpdate);
}
ModelUpdated(model);
}
public void SetFields(IList<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)
{
pagingSpec.Records = GetPagedQuery(Query, pagingSpec).ToList();
pagingSpec.TotalRecords = GetPagedQuery(Query, pagingSpec).GetRowCount();
pagingSpec.Records = GetPagedRecords(PagedBuilder().SelectAll(), pagingSpec, PagedSelector);
pagingSpec.TotalRecords = GetPagedRecordCount(PagedBuilder().SelectCount(), pagingSpec);
return pagingSpec;
}
protected virtual SortBuilder<TModel> GetPagedQuery(QueryBuilder<TModel> query, PagingSpec<TModel> pagingSpec)
private void AddFilters(SqlBuilder builder, PagingSpec<TModel> pagingSpec)
{
var filterExpressions = pagingSpec.FilterExpressions;
var sortQuery = query.Where(filterExpressions.FirstOrDefault());
var filters = pagingSpec.FilterExpressions;
if (filterExpressions.Count > 1)
foreach (var filter in filters)
{
// Start at the second item for the AndWhere clauses
for (var i = 1; i < filterExpressions.Count; i++)
{
sortQuery.AndWhere(filterExpressions[i]);
}
builder.Where<TModel>(filter);
}
}
return sortQuery.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection())
.Skip(pagingSpec.PagingOffset())
.Take(pagingSpec.PageSize);
protected List<TModel> GetPagedRecords(SqlBuilder builder, PagingSpec<TModel> pagingSpec, Func<SqlBuilder.Template, IEnumerable<TModel>> queryFunc)
{
AddFilters(builder, pagingSpec);
var sortDirection = pagingSpec.SortDirection == SortDirection.Descending ? "DESC" : "ASC";
var pagingOffset = (pagingSpec.Page - 1)*pagingSpec.PageSize;
builder.OrderBy($"{pagingSpec.SortKey} {sortDirection} LIMIT {pagingSpec.PageSize} OFFSET {pagingOffset}");
var sql = builder.AddTemplate(_selectTemplate).LogQuery();
return queryFunc(sql).ToList();
}
protected int GetPagedRecordCount(SqlBuilder builder, PagingSpec<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)
@ -294,11 +460,6 @@ namespace NzbDrone.Core.Datastore
}
}
protected virtual QueryBuilder<TActual> AddJoinQueries<TActual>(QueryBuilder<TActual> baseQuery)
{
return baseQuery;
}
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 Marr.Data.Converters;
using System.Data;
using System.Text.Json;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Reflection;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Datastore.Converters
{
public class CommandConverter : EmbeddedDocumentConverter
public class CommandConverter : EmbeddedDocumentConverter<Command>
{
public override object FromDB(ConverterContext context)
public override Command Parse(object value)
{
if (context.DbValue == DBNull.Value)
{
return null;
}
var stringValue = (string)context.DbValue;
var stringValue = (string) value;
if (stringValue.IsNullOrWhiteSpace())
{
return null;
}
var ordinal = context.DataRecord.GetOrdinal("Name");
var contract = context.DataRecord.GetString(ordinal);
string contract;
using (JsonDocument body = JsonDocument.Parse(stringValue))
{
contract = body.RootElement.GetProperty("name").GetString();
}
var impType = typeof (Command).Assembly.FindTypeByName(contract + "Command");
if (impType == null)
@ -32,7 +30,12 @@ namespace NzbDrone.Core.Datastore.Converters
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.ServiceModel;
using Marr.Data.Converters;
using Marr.Data.Mapping;
using NzbDrone.Core.Qualities;
using Newtonsoft.Json;
using System.Data;
using System.Text.Json;
using System.Text.Json.Serialization;
using Dapper;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.CustomFormats;
namespace NzbDrone.Core.Datastore.Converters
{
public class CustomFormatIntConverter : JsonConverter, IConverter
public class DapperCustomFormatIntConverter : SqlMapper.TypeHandler<CustomFormat>
{
//TODO think of something better.
public object FromDB(ConverterContext context)
public override void SetValue(IDbDataParameter parameter, CustomFormat value)
{
if (context.DbValue == DBNull.Value)
parameter.Value = value.Id;
}
public override CustomFormat Parse(object value)
{
Console.WriteLine(value.ToJson());
if (value is DBNull)
{
return null;
}
var val = Convert.ToInt32(context.DbValue);
var val = Convert.ToInt32(value);
if (val == 0)
{
return CustomFormat.None;
}
if (CustomFormatService.AllCustomFormats == null)
{
throw new Exception("***FATAL*** WE TRIED ACCESSING ALL CUSTOM FORMATS BEFORE IT WAS INITIALIZED. PLEASE SAVE THIS LOG AND OPEN AN ISSUE ON GITHUB.");
}
return CustomFormatService.AllCustomFormats[val];
}
}
public object FromDB(ColumnMap map, object dbValue)
public class CustomFormatIntConverter : JsonConverter<CustomFormat>
{
public override CustomFormat Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
}
public object ToDB(object clrValue)
{
if(clrValue == DBNull.Value) return null;
if(!(clrValue is CustomFormat))
{
throw new InvalidOperationException("Attempted to save a quality definition that isn't really a quality definition");
}
var quality = (CustomFormat) clrValue;
if (CustomFormatService.AllCustomFormats?.ContainsKey(quality.Id) == false)
{
//throw new Exception("Attempted to save an unknown custom format! Make sure you do not have stale custom formats lying around!");
}
return quality.Id;
}
public Type DbType => typeof(int);
public override bool CanConvert(Type objectType)
{
return objectType == typeof(CustomFormat);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var item = reader.Value;
var val = Convert.ToInt32(item);
var val = reader.GetInt32();
if (val == 0)
{
return CustomFormat.None;
}
if (CustomFormatService.AllCustomFormats == null)
{
throw new Exception("***FATAL*** WE TRIED ACCESSING ALL CUSTOM FORMATS BEFORE IT WAS INITIALIZED. PLEASE SAVE THIS LOG AND OPEN AN ISSUE ON GITHUB.");
}
return CustomFormatService.AllCustomFormats[val];
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
public override void Write(Utf8JsonWriter writer, CustomFormat value, JsonSerializerOptions options)
{
writer.WriteValue(ToDB(value));
writer.WriteNumberValue(value.Id);
}
}
}

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

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

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 Marr.Data.Converters;
using Marr.Data.Mapping;
using Newtonsoft.Json;
using System.Data;
using System.Text.Json;
using System.Text.Json.Serialization;
using Dapper;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Datastore.Converters
{
public class LanguageIntConverter : JsonConverter, IConverter
public class DapperLanguageIntConverter : SqlMapper.TypeHandler<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;
}
var val = Convert.ToInt32(context.DbValue);
return (Language)val;
}
public object FromDB(ColumnMap map, object dbValue)
{
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
}
public object ToDB(object clrValue)
{
if (clrValue == DBNull.Value) return 0;
if (clrValue as Language == null)
{
throw new InvalidOperationException("Attempted to save a language that isn't really a language");
}
var language = clrValue as Language;
return (int)language;
}
public Type DbType
{
get
{
return typeof(int);
}
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Language);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var item = reader.Value;
return (Language)Convert.ToInt32(item);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(ToDB(value));
return (Language) Convert.ToInt32(value);
}
}
}
public class LanguageIntConverter : JsonConverter<Language>
{
public override Language Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var item = reader.GetInt32();
return (Language)item;
}
public override void Write(Utf8JsonWriter writer, Language value, JsonSerializerOptions options)
{
writer.WriteNumberValue((int) value);
}
}
}

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 Marr.Data.Converters;
using Marr.Data.Mapping;
using System.Data;
using Dapper;
using NzbDrone.Common.Disk;
namespace NzbDrone.Core.Datastore.Converters
{
public class OsPathConverter : IConverter
public class OsPathConverter : SqlMapper.TypeHandler<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)
{
if (value == null || value is DBNull)
{
return DBNull.Value;
return new OsPath(null);
}
var value = (string)context.DbValue;
return new OsPath(value);
return new OsPath((string) value);
}
public object FromDB(ColumnMap map, object dbValue)
{
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
}
public object ToDB(object clrValue)
{
var value = (OsPath)clrValue;
return value.FullPath;
}
public Type DbType => typeof(string);
}
}

View File

@ -1,40 +1,23 @@
using System;
using Marr.Data.Converters;
using NzbDrone.Common.Reflection;
using NzbDrone.Common.Serializer;
using System.Data;
using System.Text.Json;
using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Datastore.Converters
{
public class ProviderSettingConverter : EmbeddedDocumentConverter
public class ProviderSettingConverter : EmbeddedDocumentConverter<IProviderConfig>
{
public override object FromDB(ConverterContext context)
public override IProviderConfig Parse(object value)
{
if (context.DbValue == DBNull.Value)
{
return NullConfig.Instance;
}
// We can't deserialize based on another column, happens in ProviderRepository instead
return null;
}
var stringValue = (string)context.DbValue;
if (string.IsNullOrWhiteSpace(stringValue))
{
return NullConfig.Instance;
}
var ordinal = context.DataRecord.GetOrdinal("ConfigContract");
var contract = context.DataRecord.GetString(ordinal);
var impType = typeof (IProviderConfig).Assembly.FindTypeByName(contract);
if (impType == null)
{
throw new ConfigContractNotFoundException(contract);
}
return Json.Deserialize(stringValue, impType);
public override void SetValue(IDbDataParameter parameter, IProviderConfig value)
{
// Cast to object to get all properties written out
// https://github.com/dotnet/corefx/issues/38650
parameter.Value = JsonSerializer.Serialize((object)value, SerializerSettings);
}
}
}
}

View File

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

View File

@ -1,60 +1,41 @@
using System;
using Marr.Data.Converters;
using Marr.Data.Mapping;
using NzbDrone.Core.Qualities;
using Newtonsoft.Json;
using NzbDrone.Core.CustomFormats;
using System.Text.Json.Serialization;
using System.Text.Json;
using Dapper;
using System.Data;
namespace NzbDrone.Core.Datastore.Converters
{
public class QualityTagStringConverter : JsonConverter, IConverter
public class DapperQualityTagStringConverter : SqlMapper.TypeHandler<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!
}
var val = Convert.ToString(context.DbValue);
return new FormatTag(val);
return new FormatTag(Convert.ToString(value));
}
}
public object FromDB(ColumnMap map, object dbValue)
public class QualityTagStringConverter : JsonConverter<FormatTag>
{
public override FormatTag Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
}
public object ToDB(object clrValue)
{
if(clrValue == DBNull.Value) return 0;
if(!(clrValue is FormatTag))
{
throw new InvalidOperationException("Attempted to save a quality tag that isn't really a quality tag");
}
var quality = (FormatTag) clrValue;
return quality.Raw;
}
public Type DbType => typeof(string);
public override bool CanConvert(Type objectType)
{
return objectType == typeof(FormatTag);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var item = reader.Value;
var item = reader.GetString();
return new FormatTag(Convert.ToString(item));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
public override void Write(Utf8JsonWriter writer, FormatTag value, JsonSerializerOptions options)
{
writer.WriteValue(ToDB(value));
writer.WriteStringValue(value.Raw);
}
}
}

View File

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

View File

@ -1,32 +1,34 @@
using System;
using Marr.Data.Converters;
using Marr.Data.Mapping;
using System;
using System.Data;
using System.Text.Json;
using System.Text.Json.Serialization;
using Dapper;
namespace NzbDrone.Core.Datastore.Converters
{
public class UtcConverter : IConverter
public class DapperUtcConverter : SqlMapper.TypeHandler<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)
{
if (clrValue == DBNull.Value)
{
return clrValue;
}
var dateTime = (DateTime)clrValue;
return dateTime.ToUniversalTime();
}
public Type DbType => typeof(DateTime);
}
}
public class UtcConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateTime.Parse(reader.GetString());
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"));
}
}
}

View File

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

View File

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

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 Marr.Data;
using System.Data;
namespace NzbDrone.Core.Datastore
{
@ -17,9 +17,9 @@ namespace NzbDrone.Core.Datastore
_database = database;
}
public IDataMapper GetDataMapper()
public IDbConnection OpenConnection()
{
return _database.GetDataMapper();
return _database.OpenConnection();
}
public Version Version => _database.Version;

View File

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

View File

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

View File

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

View File

@ -1,13 +1,6 @@
using System.Collections.Generic;
using System.Data;
using System.Linq;
using FluentMigrator;
using Marr.Data.QGen;
using Newtonsoft.Json.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Serializer;
using System.Data;
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.Datastore.Migration
{

View File

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

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

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 @@ namespace NzbDrone.Core.DecisionEngine
private int CompareQuality(DownloadDecision x, DownloadDecision y)
{
return CompareAll(CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.Movie.Profile.Value.GetIndex(remoteMovie.ParsedMovieInfo.Quality.Quality)),
return CompareAll(CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.Movie.Profile.GetIndex(remoteMovie.ParsedMovieInfo.Quality.Quality)),
CompareCustomFormats(x, y),
CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.ParsedMovieInfo.Quality.Revision.Real),
CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.ParsedMovieInfo.Quality.Revision.Version));
@ -73,8 +73,8 @@ namespace NzbDrone.Core.DecisionEngine
var left = x.RemoteMovie.ParsedMovieInfo.Quality.CustomFormats.WithNone();
var right = y.RemoteMovie.ParsedMovieInfo.Quality.CustomFormats;
var leftIndicies = QualityModelComparer.GetIndicies(left, x.RemoteMovie.Movie.Profile.Value);
var rightIndicies = QualityModelComparer.GetIndicies(right, y.RemoteMovie.Movie.Profile.Value);
var leftIndicies = QualityModelComparer.GetIndicies(left, x.RemoteMovie.Movie.Profile);
var rightIndicies = QualityModelComparer.GetIndicies(right, y.RemoteMovie.Movie.Profile);
var leftTotal = leftIndicies.Sum();
var rightTotal = rightIndicies.Sum();
@ -87,8 +87,7 @@ namespace NzbDrone.Core.DecisionEngine
return CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie =>
{
var title = remoteMovie.Release.Title;
remoteMovie.Movie.Profile.LazyLoad();
var preferredWords = remoteMovie.Movie.Profile.Value.PreferredTags;
var preferredWords = remoteMovie.Movie.Profile.PreferredTags;
if (preferredWords == null)
{

View File

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

View File

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

View File

@ -19,7 +19,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
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)
{

View File

@ -20,7 +20,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
{
_logger.Debug("Checking if report meets quality requirements. {0}", subject.ParsedMovieInfo.Quality);
var profile = subject.Movie.Profile.Value;
var profile = subject.Movie.Profile;
var qualityIndex = profile.GetIndex(subject.ParsedMovieInfo.Quality.Quality);
var qualityOrGroup = profile.Items[qualityIndex.Index];

View File

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

View File

@ -37,14 +37,14 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
return Decision.Accept();
}
var profile = subject.Movie.Profile.Value;
var profile = subject.Movie.Profile;
var delayProfile = _delayProfileService.BestForTags(subject.Movie.Tags);
var delay = delayProfile.GetProtocolDelay(subject.Release.DownloadProtocol);
var isPreferredProtocol = subject.Release.DownloadProtocol == delayProfile.PreferredProtocol;
// Preferred word count
var title = subject.Release.Title;
var preferredWords = subject.Movie.Profile?.Value?.PreferredTags;
var preferredWords = subject.Movie.Profile?.PreferredTags;
var preferredCount = 0;
if (preferredWords == null)

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