mirror of https://github.com/lidarr/Lidarr
Swap to dapper with lazyload
This commit is contained in:
parent
20269aba9b
commit
53c0ffd129
|
@ -3,7 +3,6 @@
|
|||
<TargetFrameworks>net462;netcoreapp3.1</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Marr.Data\Marr.Data.csproj" />
|
||||
<ProjectReference Include="..\Lidarr.Http\Lidarr.Http.csproj" />
|
||||
<ProjectReference Include="..\NzbDrone.Common\Lidarr.Common.csproj" />
|
||||
<ProjectReference Include="..\NzbDrone.Core\Lidarr.Core.csproj" />
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
|
@ -10,34 +11,230 @@ using NzbDrone.Core.Test.Framework;
|
|||
namespace NzbDrone.Core.Test.Datastore
|
||||
{
|
||||
[TestFixture]
|
||||
public class
|
||||
BasicRepositoryFixture : DbTest<BasicRepository<ScheduledTask>, ScheduledTask>
|
||||
public class BasicRepositoryFixture : DbTest<BasicRepository<ScheduledTask>, ScheduledTask>
|
||||
{
|
||||
private ScheduledTask _basicType;
|
||||
private List<ScheduledTask> _basicList;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_basicType = Builder<ScheduledTask>
|
||||
.CreateNew()
|
||||
.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.Insert(_basicList[0]);
|
||||
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 insert_many_should_throw_if_id_not_zero()
|
||||
{
|
||||
_basicList[1].Id = 999;
|
||||
Assert.Throws<InvalidOperationException>(() => Subject.InsertMany(_basicList));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_get_count()
|
||||
{
|
||||
Subject.InsertMany(_basicList);
|
||||
Subject.Count().Should().Be(_basicList.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_find_by_id()
|
||||
{
|
||||
Subject.InsertMany(_basicList);
|
||||
var storeObject = Subject.Get(_basicList[1].Id);
|
||||
|
||||
storeObject.Should().BeEquivalentTo(_basicList[1], o => o.IncludingAllRuntimeProperties());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_update()
|
||||
{
|
||||
Subject.InsertMany(_basicList);
|
||||
|
||||
var item = _basicList[1];
|
||||
item.Interval = 999;
|
||||
|
||||
Subject.Update(item);
|
||||
|
||||
Subject.All().Should().BeEquivalentTo(_basicList);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_upsert_new()
|
||||
{
|
||||
Subject.Upsert(_basicList[0]);
|
||||
Subject.All().Should().HaveCount(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_upsert_existing()
|
||||
{
|
||||
Subject.InsertMany(_basicList);
|
||||
|
||||
var item = _basicList[1];
|
||||
item.Interval = 999;
|
||||
|
||||
Subject.Upsert(item);
|
||||
|
||||
Subject.All().Should().BeEquivalentTo(_basicList);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_update_single_field()
|
||||
{
|
||||
Subject.InsertMany(_basicList);
|
||||
|
||||
var item = _basicList[1];
|
||||
var executionBackup = item.LastExecution;
|
||||
item.Interval = 999;
|
||||
item.LastExecution = DateTime.UtcNow;
|
||||
|
||||
Subject.SetFields(item, x => x.Interval);
|
||||
|
||||
item.LastExecution = executionBackup;
|
||||
Subject.All().Should().BeEquivalentTo(_basicList);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void set_fields_should_throw_if_id_zero()
|
||||
{
|
||||
Subject.InsertMany(_basicList);
|
||||
_basicList[1].Id = 0;
|
||||
_basicList[1].LastExecution = DateTime.UtcNow;
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => Subject.SetFields(_basicList[1], x => x.Interval));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_delete_model_by_id()
|
||||
{
|
||||
Subject.InsertMany(_basicList);
|
||||
Subject.All().Should().HaveCount(5);
|
||||
|
||||
Subject.Delete(_basicList[0].Id);
|
||||
Subject.All().Select(x => x.Id).Should().BeEquivalentTo(_basicList.Skip(1).Select(x => x.Id));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_delete_model_by_object()
|
||||
{
|
||||
Subject.InsertMany(_basicList);
|
||||
Subject.All().Should().HaveCount(5);
|
||||
|
||||
Subject.Delete(_basicList[0]);
|
||||
Subject.All().Select(x => x.Id).Should().BeEquivalentTo(_basicList.Skip(1).Select(x => x.Id));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void get_many_should_return_empty_list_if_no_ids()
|
||||
{
|
||||
Subject.Get(new List<int>()).Should().BeEquivalentTo(new List<ScheduledTask>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void get_many_should_throw_if_not_all_found()
|
||||
{
|
||||
Subject.InsertMany(_basicList);
|
||||
Assert.Throws<ApplicationException>(() => Subject.Get(new[] { 999 }));
|
||||
}
|
||||
|
||||
[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.Select(x => x.Id).Should().BeEquivalentTo(_basicList.Take(2).Select(x => x.Id));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_update_many()
|
||||
{
|
||||
Subject.InsertMany(_basicList);
|
||||
_basicList.ForEach(x => x.Interval = 999);
|
||||
|
||||
Subject.UpdateMany(_basicList);
|
||||
Subject.All().Should().BeEquivalentTo(_basicList);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void update_many_should_throw_if_id_zero()
|
||||
{
|
||||
Subject.InsertMany(_basicList);
|
||||
_basicList[1].Id = 0;
|
||||
Assert.Throws<InvalidOperationException>(() => Subject.UpdateMany(_basicList));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_update_many_single_field()
|
||||
{
|
||||
Subject.InsertMany(_basicList);
|
||||
|
||||
var executionBackup = _basicList.Select(x => x.LastExecution).ToList();
|
||||
_basicList.ForEach(x => x.Interval = 999);
|
||||
_basicList.ForEach(x => x.LastExecution = DateTime.UtcNow);
|
||||
|
||||
Subject.SetFields(_basicList, x => x.Interval);
|
||||
|
||||
for (int i = 0; i < _basicList.Count; i++)
|
||||
{
|
||||
_basicList[i].LastExecution = executionBackup[i];
|
||||
}
|
||||
|
||||
Subject.All().Should().BeEquivalentTo(_basicList);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void set_fields_should_throw_if_any_id_zero()
|
||||
{
|
||||
Subject.InsertMany(_basicList);
|
||||
_basicList.ForEach(x => x.Interval = 999);
|
||||
_basicList[1].Id = 0;
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => Subject.SetFields(_basicList, x => x.Interval));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_delete_many_by_model()
|
||||
{
|
||||
Subject.InsertMany(_basicList);
|
||||
Subject.All().Should().HaveCount(5);
|
||||
|
||||
Subject.DeleteMany(_basicList.Take(2).ToList());
|
||||
Subject.All().Select(x => x.Id).Should().BeEquivalentTo(_basicList.Skip(2).Select(x => x.Id));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_delete_many_by_id()
|
||||
{
|
||||
Subject.InsertMany(_basicList);
|
||||
Subject.All().Should().HaveCount(5);
|
||||
|
||||
Subject.DeleteMany(_basicList.Take(2).Select(x => x.Id).ToList());
|
||||
Subject.All().Select(x => x.Id).Should().BeEquivalentTo(_basicList.Skip(2).Select(x => x.Id));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void purge_should_delete_all()
|
||||
{
|
||||
Subject.InsertMany(Builder<ScheduledTask>.CreateListOfSize(10).BuildListOfNew());
|
||||
Subject.InsertMany(_basicList);
|
||||
|
||||
AllStoredModels.Should().HaveCount(10);
|
||||
AllStoredModels.Should().HaveCount(5);
|
||||
|
||||
Subject.Purge();
|
||||
|
||||
|
@ -45,29 +242,29 @@ namespace NzbDrone.Core.Test.Datastore
|
|||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_delete_model()
|
||||
public void has_items_should_return_false_with_no_items()
|
||||
{
|
||||
Subject.Insert(_basicType);
|
||||
Subject.All().Should().HaveCount(1);
|
||||
|
||||
Subject.Delete(_basicType.Id);
|
||||
Subject.All().Should().BeEmpty();
|
||||
Subject.HasItems().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_find_by_id()
|
||||
public void has_items_should_return_true_with_items()
|
||||
{
|
||||
Subject.Insert(_basicType);
|
||||
var storeObject = Subject.Get(_basicType.Id);
|
||||
Subject.InsertMany(_basicList);
|
||||
Subject.HasItems().Should().BeTrue();
|
||||
}
|
||||
|
||||
storeObject.Should().BeEquivalentTo(_basicType, o => o.IncludingAllRuntimeProperties());
|
||||
[Test]
|
||||
public void single_should_throw_on_empty()
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() => Subject.Single());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_get_single()
|
||||
{
|
||||
Subject.Insert(_basicType);
|
||||
Subject.SingleOrDefault().Should().NotBeNull();
|
||||
Subject.Insert(_basicList[0]);
|
||||
Subject.Single().Should().BeEquivalentTo(_basicList[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -89,9 +286,21 @@ namespace NzbDrone.Core.Test.Datastore
|
|||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_call_ToList_on_empty_quariable()
|
||||
public void should_be_able_to_call_ToList_on_empty_queryable()
|
||||
{
|
||||
Subject.All().ToList().Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void get_paged_should_work()
|
||||
{
|
||||
Subject.InsertMany(_basicList);
|
||||
var data = Subject.GetPaged(new PagingSpec<ScheduledTask>() { Page = 0, PageSize = 2, SortKey = "LastExecution", SortDirection = SortDirection.Descending });
|
||||
|
||||
data.Page.Should().Be(0);
|
||||
data.PageSize.Should().Be(2);
|
||||
data.TotalRecords.Should().Be(_basicList.Count);
|
||||
data.Records.Should().BeEquivalentTo(_basicList.OrderByDescending(x => x.LastExecution).Take(2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 = 2L
|
||||
};
|
||||
|
||||
Assert.Throws<ConversionException>(() => Subject.FromDB(context));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,8 @@
|
|||
using System;
|
||||
using System.Data;
|
||||
using System.Data.SQLite;
|
||||
using FluentAssertions;
|
||||
using Marr.Data.Converters;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Datastore.Converters;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Music.Commands;
|
||||
|
@ -15,67 +13,50 @@ namespace NzbDrone.Core.Test.Datastore.Converters
|
|||
[TestFixture]
|
||||
public class CommandConverterFixture : CoreTest<CommandConverter>
|
||||
{
|
||||
private SQLiteParameter _param;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_param = new SQLiteParameter();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_json_string_when_saving_boolean_to_db()
|
||||
{
|
||||
var command = new RefreshArtistCommand();
|
||||
|
||||
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("RefreshArtist");
|
||||
var data = "{\"name\": \"RefreshArtist\"}";
|
||||
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DataRecord = dataRecordMock.Object,
|
||||
DbValue = new RefreshArtistCommand().ToJson()
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().BeOfType<RefreshArtistCommand>();
|
||||
Subject.Parse(data).Should().BeOfType<RefreshArtistCommand>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_unknown_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("MockRemovedCommand");
|
||||
var data = "{\"name\": \"EnsureMediaCovers\"}";
|
||||
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DataRecord = dataRecordMock.Object,
|
||||
DbValue = new RefreshArtistCommand(2).ToJson()
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().BeOfType<UnknownCommand>();
|
||||
Subject.Parse(data).Should().BeOfType<UnknownCommand>();
|
||||
}
|
||||
|
||||
[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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>>>
|
||||
{
|
||||
private 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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,57 +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.Music;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
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(ArtistStatusType.Continuing).Should().Be((int)ArtistStatusType.Continuing);
|
||||
}
|
||||
|
||||
[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(ArtistMetadata));
|
||||
mockMemberInfo.SetupGet(s => s.Name).Returns("Status");
|
||||
|
||||
var expected = ArtistStatusType.Continuing;
|
||||
|
||||
var context = new ConverterContext
|
||||
{
|
||||
ColumnMap = new ColumnMap(mockMemberInfo.Object) { FieldType = typeof(ArtistStatusType) },
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
{
|
||||
private 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,56 +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()
|
||||
{
|
||||
Subject.ToDB(5).Should().Be(5);
|
||||
}
|
||||
|
||||
[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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,38 +11,37 @@ namespace NzbDrone.Core.Test.Datastore.Converters
|
|||
[TestFixture]
|
||||
public class OsPathConverterFixture : CoreTest<OsPathConverter>
|
||||
{
|
||||
private 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\Music".AsOsAgnostic();
|
||||
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]
|
||||
public void should_return_os_path_when_getting_string_from_db()
|
||||
{
|
||||
var path = @"C:\Test\Music".AsOsAgnostic();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,30 +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>
|
||||
{
|
||||
private 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
{
|
||||
private SQLiteParameter _param;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_param = new SQLiteParameter();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_int_when_saving_quality_to_db()
|
||||
{
|
||||
var quality = Quality.FLAC;
|
||||
|
||||
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.FLAC;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
{
|
||||
private 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Dapper;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
@ -14,14 +15,14 @@ namespace NzbDrone.Core.Test.Datastore
|
|||
public void SingleOrDefault_should_return_null_on_empty_db()
|
||||
{
|
||||
Mocker.Resolve<IDatabase>()
|
||||
.GetDataMapper().Query<Artist>()
|
||||
.OpenConnection().Query<Artist>("SELECT * FROM Artists")
|
||||
.SingleOrDefault(c => c.CleanName == "SomeTitle")
|
||||
.Should()
|
||||
.BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void vaccume()
|
||||
public void vacuum()
|
||||
{
|
||||
Mocker.Resolve<IDatabase>().Vacuum();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dapper;
|
||||
using FizzWare.NBuilder;
|
||||
using Marr.Data.QGen;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
|
@ -13,11 +13,13 @@ using NzbDrone.Core.Test.Framework;
|
|||
namespace NzbDrone.Core.Test.Datastore
|
||||
{
|
||||
[TestFixture]
|
||||
public class MarrDataLazyLoadingFixture : DbTest
|
||||
public class LazyLoadingFixture : DbTest
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
SqlBuilderExtensions.LogSql = true;
|
||||
|
||||
var profile = new QualityProfile
|
||||
{
|
||||
Name = "Test",
|
||||
|
@ -81,69 +83,12 @@ namespace NzbDrone.Core.Test.Datastore
|
|||
Db.InsertMany(tracks);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_join_artist_when_query_for_albums()
|
||||
{
|
||||
var db = Mocker.Resolve<IDatabase>();
|
||||
var dataMapper = db.GetDataMapper();
|
||||
|
||||
var albums = dataMapper.Query<Album>()
|
||||
.Join<Album, Artist>(JoinType.Inner, v => v.Artist, (l, r) => l.ArtistMetadataId == r.ArtistMetadataId)
|
||||
.ToList();
|
||||
|
||||
foreach (var album in albums)
|
||||
{
|
||||
Assert.IsNotNull(album.Artist);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_lazy_load_profile_if_not_joined()
|
||||
{
|
||||
var db = Mocker.Resolve<IDatabase>();
|
||||
var dataMapper = db.GetDataMapper();
|
||||
|
||||
var tracks = dataMapper.Query<Track>()
|
||||
.Join<Track, AlbumRelease>(JoinType.Inner, v => v.AlbumRelease, (l, r) => l.AlbumReleaseId == r.Id)
|
||||
.Join<AlbumRelease, Album>(JoinType.Inner, v => v.Album, (l, r) => l.AlbumId == r.Id)
|
||||
.Join<Album, Artist>(JoinType.Inner, v => v.Artist, (l, r) => l.ArtistMetadataId == r.ArtistMetadataId)
|
||||
.ToList();
|
||||
|
||||
foreach (var track in tracks)
|
||||
{
|
||||
Assert.IsTrue(track.AlbumRelease.IsLoaded);
|
||||
Assert.IsTrue(track.AlbumRelease.Value.Album.IsLoaded);
|
||||
Assert.IsTrue(track.AlbumRelease.Value.Album.Value.Artist.IsLoaded);
|
||||
Assert.IsNotNull(track.AlbumRelease.Value.Album.Value.Artist.Value);
|
||||
Assert.IsFalse(track.AlbumRelease.Value.Album.Value.Artist.Value.QualityProfile.IsLoaded);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_explicit_load_trackfile_if_joined()
|
||||
{
|
||||
var db = Mocker.Resolve<IDatabase>();
|
||||
var dataMapper = db.GetDataMapper();
|
||||
|
||||
var tracks = dataMapper.Query<Track>()
|
||||
.Join<Track, TrackFile>(JoinType.Inner, v => v.TrackFile, (l, r) => l.TrackFileId == r.Id)
|
||||
.ToList();
|
||||
|
||||
foreach (var track in tracks)
|
||||
{
|
||||
Assert.IsFalse(track.Artist.IsLoaded);
|
||||
Assert.IsTrue(track.TrackFile.IsLoaded);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_lazy_load_artist_for_track()
|
||||
{
|
||||
var db = Mocker.Resolve<IDatabase>();
|
||||
var dataMapper = db.GetDataMapper();
|
||||
var db = Mocker.Resolve<TrackRepository>();
|
||||
|
||||
var tracks = dataMapper.Query<Track>()
|
||||
.ToList();
|
||||
var tracks = db.All();
|
||||
|
||||
Assert.IsNotEmpty(tracks);
|
||||
foreach (var track in tracks)
|
||||
|
@ -159,10 +104,7 @@ namespace NzbDrone.Core.Test.Datastore
|
|||
public void should_lazy_load_artist_for_trackfile()
|
||||
{
|
||||
var db = Mocker.Resolve<IDatabase>();
|
||||
var dataMapper = db.GetDataMapper();
|
||||
|
||||
var tracks = dataMapper.Query<TrackFile>()
|
||||
.ToList();
|
||||
var tracks = db.Query<TrackFile>(new SqlBuilder()).ToList();
|
||||
|
||||
Assert.IsNotEmpty(tracks);
|
||||
foreach (var track in tracks)
|
||||
|
@ -178,10 +120,7 @@ namespace NzbDrone.Core.Test.Datastore
|
|||
public void should_lazy_load_trackfile_if_not_joined()
|
||||
{
|
||||
var db = Mocker.Resolve<IDatabase>();
|
||||
var dataMapper = db.GetDataMapper();
|
||||
|
||||
var tracks = dataMapper.Query<Track>()
|
||||
.ToList();
|
||||
var tracks = db.Query<Track>(new SqlBuilder()).ToList();
|
||||
|
||||
foreach (var track in tracks)
|
||||
{
|
||||
|
@ -195,14 +134,12 @@ namespace NzbDrone.Core.Test.Datastore
|
|||
public void should_explicit_load_everything_if_joined()
|
||||
{
|
||||
var db = Mocker.Resolve<IDatabase>();
|
||||
var dataMapper = db.GetDataMapper();
|
||||
|
||||
var files = dataMapper.Query<TrackFile>()
|
||||
.Join<TrackFile, Track>(JoinType.Inner, f => f.Tracks, (f, t) => f.Id == t.TrackFileId)
|
||||
.Join<TrackFile, Album>(JoinType.Inner, t => t.Album, (t, a) => t.AlbumId == a.Id)
|
||||
.Join<TrackFile, Artist>(JoinType.Inner, t => t.Artist, (t, a) => t.Album.Value.ArtistMetadataId == a.ArtistMetadataId)
|
||||
.Join<Artist, ArtistMetadata>(JoinType.Inner, a => a.Metadata, (a, m) => a.ArtistMetadataId == m.Id)
|
||||
.ToList();
|
||||
var files = MediaFileRepository.Query(db,
|
||||
new SqlBuilder()
|
||||
.Join<TrackFile, Track>((f, t) => f.Id == t.TrackFileId)
|
||||
.Join<TrackFile, Album>((t, a) => t.AlbumId == a.Id)
|
||||
.Join<Album, Artist>((album, artist) => album.ArtistMetadataId == artist.ArtistMetadataId)
|
||||
.Join<Artist, ArtistMetadata>((a, m) => a.ArtistMetadataId == m.Id));
|
||||
|
||||
Assert.IsNotEmpty(files);
|
||||
foreach (var file in files)
|
||||
|
@ -219,13 +156,18 @@ namespace NzbDrone.Core.Test.Datastore
|
|||
public void should_lazy_load_tracks_if_not_joined_to_trackfile()
|
||||
{
|
||||
var db = Mocker.Resolve<IDatabase>();
|
||||
var dataMapper = db.GetDataMapper();
|
||||
|
||||
var files = dataMapper.Query<TrackFile>()
|
||||
.Join<TrackFile, Album>(JoinType.Inner, t => t.Album, (t, a) => t.AlbumId == a.Id)
|
||||
.Join<TrackFile, Artist>(JoinType.Inner, t => t.Artist, (t, a) => t.Album.Value.ArtistMetadataId == a.ArtistMetadataId)
|
||||
.Join<Artist, ArtistMetadata>(JoinType.Inner, a => a.Metadata, (a, m) => a.ArtistMetadataId == m.Id)
|
||||
.ToList();
|
||||
var files = db.QueryJoined<TrackFile, Album, Artist, ArtistMetadata>(
|
||||
new SqlBuilder()
|
||||
.Join<TrackFile, Album>((t, a) => t.AlbumId == a.Id)
|
||||
.Join<Album, Artist>((album, artist) => album.ArtistMetadataId == artist.ArtistMetadataId)
|
||||
.Join<Artist, ArtistMetadata>((a, m) => a.ArtistMetadataId == m.Id),
|
||||
(file, album, artist, metadata) =>
|
||||
{
|
||||
file.Album = album;
|
||||
file.Artist = artist;
|
||||
file.Artist.Value.Metadata = metadata;
|
||||
return file;
|
||||
});
|
||||
|
||||
Assert.IsNotEmpty(files);
|
||||
foreach (var file in files)
|
||||
|
@ -244,9 +186,7 @@ namespace NzbDrone.Core.Test.Datastore
|
|||
public void should_lazy_load_tracks_if_not_joined()
|
||||
{
|
||||
var db = Mocker.Resolve<IDatabase>();
|
||||
var dataMapper = db.GetDataMapper();
|
||||
|
||||
var release = dataMapper.Query<AlbumRelease>().Where(x => x.Id == 1).SingleOrDefault();
|
||||
var release = db.Query<AlbumRelease>(new SqlBuilder().Where<AlbumRelease>(x => x.Id == 1)).SingleOrDefault();
|
||||
|
||||
Assert.IsFalse(release.Tracks.IsLoaded);
|
||||
Assert.IsNotNull(release.Tracks.Value);
|
||||
|
@ -258,10 +198,7 @@ namespace NzbDrone.Core.Test.Datastore
|
|||
public void should_lazy_load_track_if_not_joined()
|
||||
{
|
||||
var db = Mocker.Resolve<IDatabase>();
|
||||
var dataMapper = db.GetDataMapper();
|
||||
|
||||
var tracks = dataMapper.Query<TrackFile>()
|
||||
.ToList();
|
||||
var tracks = db.Query<TrackFile>(new SqlBuilder()).ToList();
|
||||
|
||||
foreach (var track in tracks)
|
||||
{
|
||||
|
@ -270,28 +207,5 @@ namespace NzbDrone.Core.Test.Datastore
|
|||
Assert.IsTrue(track.Tracks.IsLoaded);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_explicit_load_profile_if_joined()
|
||||
{
|
||||
var db = Mocker.Resolve<IDatabase>();
|
||||
var dataMapper = db.GetDataMapper();
|
||||
|
||||
var tracks = dataMapper.Query<Track>()
|
||||
.Join<Track, AlbumRelease>(JoinType.Inner, v => v.AlbumRelease, (l, r) => l.AlbumReleaseId == r.Id)
|
||||
.Join<AlbumRelease, Album>(JoinType.Inner, v => v.Album, (l, r) => l.AlbumId == r.Id)
|
||||
.Join<Album, Artist>(JoinType.Inner, v => v.Artist, (l, r) => l.ArtistMetadataId == r.ArtistMetadataId)
|
||||
.Join<Artist, QualityProfile>(JoinType.Inner, v => v.QualityProfile, (l, r) => l.QualityProfileId == r.Id)
|
||||
.ToList();
|
||||
|
||||
foreach (var track in tracks)
|
||||
{
|
||||
Assert.IsTrue(track.AlbumRelease.IsLoaded);
|
||||
Assert.IsTrue(track.AlbumRelease.Value.Album.IsLoaded);
|
||||
Assert.IsTrue(track.AlbumRelease.Value.Album.Value.Artist.IsLoaded);
|
||||
Assert.IsNotNull(track.AlbumRelease.Value.Album.Value.Artist.Value);
|
||||
Assert.IsTrue(track.AlbumRelease.Value.Album.Value.Artist.Value.QualityProfile.IsLoaded);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Datastore.Extensions;
|
||||
using NzbDrone.Core.Music;
|
||||
|
||||
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<Album>
|
||||
{
|
||||
Page = page,
|
||||
PageSize = pageSize,
|
||||
SortDirection = SortDirection.Ascending,
|
||||
SortKey = "ReleaseDate"
|
||||
};
|
||||
|
||||
pagingSpec.PagingOffset().Should().Be(expected);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Datastore.Extensions;
|
||||
using NzbDrone.Core.Music;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.PagingSpecExtensionsTests
|
||||
{
|
||||
public class ToSortDirectionFixture
|
||||
{
|
||||
[Test]
|
||||
public void should_convert_default_to_asc()
|
||||
{
|
||||
var pagingSpec = new PagingSpec<Album>
|
||||
{
|
||||
Page = 1,
|
||||
PageSize = 10,
|
||||
SortDirection = SortDirection.Default,
|
||||
SortKey = "ReleaseDate"
|
||||
};
|
||||
|
||||
pagingSpec.ToSortDirection().Should().Be(Marr.Data.QGen.SortDirection.Asc);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_convert_ascending_to_asc()
|
||||
{
|
||||
var pagingSpec = new PagingSpec<Album>
|
||||
{
|
||||
Page = 1,
|
||||
PageSize = 10,
|
||||
SortDirection = SortDirection.Ascending,
|
||||
SortKey = "ReleaseDate"
|
||||
};
|
||||
|
||||
pagingSpec.ToSortDirection().Should().Be(Marr.Data.QGen.SortDirection.Asc);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_convert_descending_to_desc()
|
||||
{
|
||||
var pagingSpec = new PagingSpec<Album>
|
||||
{
|
||||
Page = 1,
|
||||
PageSize = 10,
|
||||
SortDirection = SortDirection.Descending,
|
||||
SortKey = "ReleaseDate"
|
||||
};
|
||||
|
||||
pagingSpec.ToSortDirection().Should().Be(Marr.Data.QGen.SortDirection.Desc);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,39 +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()
|
||||
{
|
||||
|
||||
|
||||
}*/
|
||||
}
|
||||
}
|
|
@ -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.Music;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore
|
||||
{
|
||||
[TestFixture]
|
||||
public class MappingExtensionFixture
|
||||
public class TableMapperFixture
|
||||
{
|
||||
public class EmbeddedType : IEmbeddedDocument
|
||||
{
|
||||
|
@ -37,9 +36,8 @@ 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]
|
||||
|
@ -47,7 +45,7 @@ namespace NzbDrone.Core.Test.Datastore
|
|||
{
|
||||
var properties = typeof(TypeWithAllMappableProperties).GetProperties();
|
||||
properties.Should().NotBeEmpty();
|
||||
properties.Should().OnlyContain(c => MappingExtensions.IsMappableProperty(c));
|
||||
properties.Should().OnlyContain(c => c.IsMappableProperty());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -55,7 +53,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 => c.IsMappableProperty());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,219 @@
|
|||
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.Music;
|
||||
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<Artist, bool>> filter)
|
||||
{
|
||||
return new WhereBuilder(filter, true, 0);
|
||||
}
|
||||
|
||||
private WhereBuilder WhereMetadata(Expression<Func<ArtistMetadata, bool>> filter)
|
||||
{
|
||||
return new WhereBuilder(filter, true, 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void where_equal_const()
|
||||
{
|
||||
_subject = Where(x => x.Id == 10);
|
||||
|
||||
_subject.ToString().Should().Be($"(\"Artists\".\"Id\" = @Clause1_P1)");
|
||||
_subject.Parameters.Get<int>("Clause1_P1").Should().Be(10);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void where_equal_variable()
|
||||
{
|
||||
var id = 10;
|
||||
_subject = Where(x => x.Id == id);
|
||||
|
||||
_subject.ToString().Should().Be($"(\"Artists\".\"Id\" = @Clause1_P1)");
|
||||
_subject.Parameters.Get<int>("Clause1_P1").Should().Be(id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void where_equal_property()
|
||||
{
|
||||
var artist = new Artist { Id = 10 };
|
||||
_subject = Where(x => x.Id == artist.Id);
|
||||
|
||||
_subject.Parameters.ParameterNames.Should().HaveCount(1);
|
||||
_subject.ToString().Should().Be($"(\"Artists\".\"Id\" = @Clause1_P1)");
|
||||
_subject.Parameters.Get<int>("Clause1_P1").Should().Be(artist.Id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void where_equal_lazy_property()
|
||||
{
|
||||
_subject = Where(x => x.QualityProfile.Value.Id == 1);
|
||||
|
||||
_subject.Parameters.ParameterNames.Should().HaveCount(1);
|
||||
_subject.ToString().Should().Be($"(\"QualityProfiles\".\"Id\" = @Clause1_P1)");
|
||||
_subject.Parameters.Get<int>("Clause1_P1").Should().Be(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void where_throws_without_concrete_condition_if_requiresConcreteCondition()
|
||||
{
|
||||
Expression<Func<Artist, Artist, bool>> filter = (x, y) => x.Id == y.Id;
|
||||
_subject = new WhereBuilder(filter, true, 0);
|
||||
Assert.Throws<InvalidOperationException>(() => _subject.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void where_allows_abstract_condition_if_not_requiresConcreteCondition()
|
||||
{
|
||||
Expression<Func<Artist, Artist, bool>> filter = (x, y) => x.Id == y.Id;
|
||||
_subject = new WhereBuilder(filter, false, 0);
|
||||
_subject.ToString().Should().Be($"(\"Artists\".\"Id\" = \"Artists\".\"Id\")");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void where_string_is_null()
|
||||
{
|
||||
_subject = Where(x => x.CleanName == null);
|
||||
|
||||
_subject.ToString().Should().Be($"(\"Artists\".\"CleanName\" IS NULL)");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void where_string_is_null_value()
|
||||
{
|
||||
string imdb = null;
|
||||
_subject = Where(x => x.CleanName == imdb);
|
||||
|
||||
_subject.ToString().Should().Be($"(\"Artists\".\"CleanName\" IS NULL)");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void where_equal_null_property()
|
||||
{
|
||||
var artist = new Artist { CleanName = null };
|
||||
_subject = Where(x => x.CleanName == artist.CleanName);
|
||||
|
||||
_subject.ToString().Should().Be($"(\"Artists\".\"CleanName\" IS NULL)");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void where_column_contains_string()
|
||||
{
|
||||
var test = "small";
|
||||
_subject = Where(x => x.CleanName.Contains(test));
|
||||
|
||||
_subject.ToString().Should().Be($"(\"Artists\".\"CleanName\" LIKE '%' || @Clause1_P1 || '%')");
|
||||
_subject.Parameters.Get<string>("Clause1_P1").Should().Be(test);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void where_string_contains_column()
|
||||
{
|
||||
var test = "small";
|
||||
_subject = Where(x => test.Contains(x.CleanName));
|
||||
|
||||
_subject.ToString().Should().Be($"(@Clause1_P1 LIKE '%' || \"Artists\".\"CleanName\" || '%')");
|
||||
_subject.Parameters.Get<string>("Clause1_P1").Should().Be(test);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void where_column_starts_with_string()
|
||||
{
|
||||
var test = "small";
|
||||
_subject = Where(x => x.CleanName.StartsWith(test));
|
||||
|
||||
_subject.ToString().Should().Be($"(\"Artists\".\"CleanName\" LIKE @Clause1_P1 || '%')");
|
||||
_subject.Parameters.Get<string>("Clause1_P1").Should().Be(test);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void where_column_ends_with_string()
|
||||
{
|
||||
var test = "small";
|
||||
_subject = Where(x => x.CleanName.EndsWith(test));
|
||||
|
||||
_subject.ToString().Should().Be($"(\"Artists\".\"CleanName\" LIKE '%' || @Clause1_P1)");
|
||||
_subject.Parameters.Get<string>("Clause1_P1").Should().Be(test);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void where_in_list()
|
||||
{
|
||||
var list = new List<int> { 1, 2, 3 };
|
||||
_subject = Where(x => list.Contains(x.Id));
|
||||
|
||||
_subject.ToString().Should().Be($"(\"Artists\".\"Id\" IN (1, 2, 3))");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void where_in_list_2()
|
||||
{
|
||||
var list = new List<int> { 1, 2, 3 };
|
||||
_subject = Where(x => x.CleanName == "test" && list.Contains(x.Id));
|
||||
|
||||
_subject.ToString().Should().Be($"((\"Artists\".\"CleanName\" = @Clause1_P1) AND (\"Artists\".\"Id\" IN (1, 2, 3)))");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void where_in_string_list()
|
||||
{
|
||||
var list = new List<string> { "first", "second", "third" };
|
||||
|
||||
_subject = Where(x => list.Contains(x.CleanName));
|
||||
|
||||
_subject.ToString().Should().Be($"(\"Artists\".\"CleanName\" IN @Clause1_P1)");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void where_in_string_list_column()
|
||||
{
|
||||
_subject = WhereMetadata(x => x.OldForeignArtistIds.Contains("foreignId"));
|
||||
|
||||
_subject.ToString().Should().Be($"(\"ArtistMetadata\".\"OldForeignArtistIds\" LIKE '%' || @Clause1_P1 || '%')");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void enum_as_int()
|
||||
{
|
||||
_subject = WhereMetadata(x => x.Status == ArtistStatusType.Continuing);
|
||||
|
||||
_subject.ToString().Should().Be($"(\"ArtistMetadata\".\"Status\" = @Clause1_P1)");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void enum_in_list()
|
||||
{
|
||||
var allowed = new List<ArtistStatusType> { ArtistStatusType.Continuing, ArtistStatusType.Ended };
|
||||
_subject = WhereMetadata(x => allowed.Contains(x.Status));
|
||||
|
||||
_subject.ToString().Should().Be($"(\"ArtistMetadata\".\"Status\" IN @Clause1_P1)");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void enum_in_array()
|
||||
{
|
||||
var allowed = new ArtistStatusType[] { ArtistStatusType.Continuing, ArtistStatusType.Ended };
|
||||
_subject = WhereMetadata(x => allowed.Contains(x.Status));
|
||||
|
||||
_subject.ToString().Should().Be($"(\"ArtistMetadata\".\"Status\" IN @Clause1_P1)");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,9 +2,9 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Marr.Data;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Marr.Data;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.Music;
|
||||
|
|
|
@ -2,10 +2,10 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using Marr.Data;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.Music;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using Marr.Data;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FizzWare.NBuilder;
|
||||
using Marr.Data;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Data.SQLite;
|
||||
using System.Linq;
|
||||
using Marr.Data;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NUnit.Framework;
|
||||
|
@ -112,7 +111,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]
|
||||
|
|
|
@ -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;
|
||||
|
@ -13,32 +12,20 @@ namespace NzbDrone.Core.Test.Framework
|
|||
List<Dictionary<string, object>> Query(string sql);
|
||||
List<T> Query<T>(string sql)
|
||||
where T : new();
|
||||
T QueryScalar<T>(string sql);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private DbConnection OpenConnection()
|
||||
{
|
||||
var connection = _providerFactory.CreateConnection();
|
||||
connection.ConnectionString = _connectionString;
|
||||
connection.Open();
|
||||
return connection;
|
||||
_database = database;
|
||||
}
|
||||
|
||||
public DataTable GetDataTable(string sql)
|
||||
{
|
||||
using (var connection = OpenConnection())
|
||||
using (var connection = _database.OpenConnection())
|
||||
{
|
||||
using (var cmd = connection.CreateCommand())
|
||||
{
|
||||
|
@ -65,13 +52,6 @@ namespace NzbDrone.Core.Test.Framework
|
|||
return dataTable.Rows.Cast<DataRow>().Select(MapToObject<T>).ToList();
|
||||
}
|
||||
|
||||
public T QueryScalar<T>(string sql)
|
||||
{
|
||||
var dataTable = GetDataTable(sql);
|
||||
|
||||
return dataTable.Rows.Cast<DataRow>().Select(d => MapValue(d, 0, typeof(T))).Cast<T>().FirstOrDefault();
|
||||
}
|
||||
|
||||
protected Dictionary<string, object> MapToDictionary(DataRow dataRow)
|
||||
{
|
||||
var item = new Dictionary<string, object>();
|
||||
|
@ -118,28 +98,24 @@ namespace NzbDrone.Core.Test.Framework
|
|||
propertyType = propertyType.GetGenericArguments()[0];
|
||||
}
|
||||
|
||||
object value = MapValue(dataRow, i, propertyType);
|
||||
object value;
|
||||
if (dataRow.ItemArray[i] == DBNull.Value)
|
||||
{
|
||||
value = null;
|
||||
}
|
||||
else if (dataRow.Table.Columns[i].DataType == typeof(string) && propertyType != typeof(string))
|
||||
{
|
||||
value = Json.Deserialize((string)dataRow.ItemArray[i], propertyType);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = Convert.ChangeType(dataRow.ItemArray[i], propertyType);
|
||||
}
|
||||
|
||||
propertyInfo.SetValue(item, value, null);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private object MapValue(DataRow dataRow, int i, Type targetType)
|
||||
{
|
||||
if (dataRow.ItemArray[i] == DBNull.Value)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (dataRow.Table.Columns[i].DataType == typeof(string) && targetType != typeof(string))
|
||||
{
|
||||
return Json.Deserialize((string)dataRow.ItemArray[i], targetType);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Convert.ChangeType(dataRow.ItemArray[i], targetType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
@ -21,6 +22,7 @@ namespace NzbDrone.Core.Test.Framework
|
|||
void Delete<T>(T childModel)
|
||||
where T : ModelBase, new();
|
||||
IDirectDataMapper GetDirectDataMapper();
|
||||
IDbConnection OpenConnection();
|
||||
}
|
||||
|
||||
public class TestDatabase : ITestDatabase
|
||||
|
@ -74,5 +76,10 @@ namespace NzbDrone.Core.Test.Framework
|
|||
{
|
||||
return new DirectDataMapper(_dbConnection);
|
||||
}
|
||||
|
||||
public IDbConnection OpenConnection()
|
||||
{
|
||||
return _dbConnection.OpenConnection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,11 @@ 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 +28,17 @@ 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<ReleaseProfile>.CreateListOfSize(2)
|
||||
.All()
|
||||
.With(x => x.Id = 0)
|
||||
.With(v => v.Tags.Add(tags[0].Id))
|
||||
.BuildList();
|
||||
Db.InsertMany(restrictions);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using FluentAssertions;
|
||||
using Marr.Data;
|
||||
using NLog;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Instrumentation;
|
||||
|
@ -64,22 +63,6 @@ namespace NzbDrone.Core.Test.Instrumentation
|
|||
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()
|
||||
{
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Marr.Data;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.TrackImport.Specifications;
|
||||
using NzbDrone.Core.Music;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Marr.Data;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.TrackImport.Specifications;
|
||||
using NzbDrone.Core.Music;
|
||||
|
|
|
@ -2,10 +2,10 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Marr.Data;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Music;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
|
|
@ -29,10 +29,7 @@ namespace NzbDrone.Core.Test.MusicTests.AlbumRepositoryTests
|
|||
Monitored = true,
|
||||
ForeignArtistId = "this is a fake id",
|
||||
Id = 1,
|
||||
Metadata = new ArtistMetadata
|
||||
{
|
||||
Id = 1
|
||||
}
|
||||
ArtistMetadataId = 1
|
||||
};
|
||||
|
||||
_albumRepo = Mocker.Resolve<AlbumRepository>();
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace NzbDrone.Core.Test.MusicTests.ArtistRepositoryTests
|
|||
public void Setup()
|
||||
{
|
||||
_artistMetadataRepo = Mocker.Resolve<ArtistMetadataRepository>();
|
||||
_metadataList = Builder<ArtistMetadata>.CreateListOfSize(10).BuildList();
|
||||
_metadataList = Builder<ArtistMetadata>.CreateListOfSize(10).All().With(x => x.Id = 0).BuildList();
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
|
@ -4,9 +4,9 @@ using System.Reflection;
|
|||
using AutoFixture;
|
||||
using Equ;
|
||||
using FluentAssertions;
|
||||
using Marr.Data;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Music;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Music;
|
||||
|
||||
namespace NzbDrone.Core.ArtistStats
|
||||
{
|
||||
|
@ -13,6 +16,8 @@ namespace NzbDrone.Core.ArtistStats
|
|||
|
||||
public class ArtistStatisticsRepository : IArtistStatisticsRepository
|
||||
{
|
||||
private const string _selectTemplate = "SELECT /**select**/ FROM Tracks /**join**/ /**innerjoin**/ /**leftjoin**/ /**where**/ /**groupby**/ /**having**/ /**orderby**/";
|
||||
|
||||
private readonly IMainDatabase _database;
|
||||
|
||||
public ArtistStatisticsRepository(IMainDatabase database)
|
||||
|
@ -22,57 +27,41 @@ namespace NzbDrone.Core.ArtistStats
|
|||
|
||||
public List<AlbumStatistics> ArtistStatistics()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.AddParameter("currentDate", DateTime.UtcNow);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine(GetSelectClause());
|
||||
sb.AppendLine("AND Albums.ReleaseDate < @currentDate");
|
||||
sb.AppendLine(GetGroupByClause());
|
||||
var queryText = sb.ToString();
|
||||
|
||||
return mapper.Query<AlbumStatistics>(queryText);
|
||||
var time = DateTime.UtcNow;
|
||||
return Query(Builder().Where<Album>(x => x.ReleaseDate < time));
|
||||
}
|
||||
|
||||
public List<AlbumStatistics> ArtistStatistics(int artistId)
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.AddParameter("currentDate", DateTime.UtcNow);
|
||||
mapper.AddParameter("artistId", artistId);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine(GetSelectClause());
|
||||
sb.AppendLine("AND Artists.Id = @artistId");
|
||||
sb.AppendLine("AND Albums.ReleaseDate < @currentDate");
|
||||
sb.AppendLine(GetGroupByClause());
|
||||
var queryText = sb.ToString();
|
||||
|
||||
return mapper.Query<AlbumStatistics>(queryText);
|
||||
var time = DateTime.UtcNow;
|
||||
return Query(Builder().Where<Album>(x => x.ReleaseDate < time)
|
||||
.Where<Artist>(x => x.Id == artistId));
|
||||
}
|
||||
|
||||
private string GetSelectClause()
|
||||
private List<AlbumStatistics> Query(SqlBuilder builder)
|
||||
{
|
||||
return @"SELECT
|
||||
Artists.Id AS ArtistId,
|
||||
var sql = builder.AddTemplate(_selectTemplate).LogQuery();
|
||||
|
||||
using (var conn = _database.OpenConnection())
|
||||
{
|
||||
return conn.Query<AlbumStatistics>(sql.RawSql, sql.Parameters).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private SqlBuilder Builder() => new SqlBuilder()
|
||||
.Select(@"Artists.Id AS ArtistId,
|
||||
Albums.Id AS AlbumId,
|
||||
SUM(COALESCE(TrackFiles.Size, 0)) AS SizeOnDisk,
|
||||
COUNT(Tracks.Id) AS TotalTrackCount,
|
||||
SUM(CASE WHEN Tracks.TrackFileId > 0 THEN 1 ELSE 0 END) AS AvailableTrackCount,
|
||||
SUM(CASE WHEN Albums.Monitored = 1 OR Tracks.TrackFileId > 0 THEN 1 ELSE 0 END) AS TrackCount,
|
||||
SUM(CASE WHEN TrackFiles.Id IS NULL THEN 0 ELSE 1 END) AS TrackFileCount
|
||||
FROM Tracks
|
||||
JOIN AlbumReleases ON Tracks.AlbumReleaseId = AlbumReleases.Id
|
||||
JOIN Albums ON AlbumReleases.AlbumId = Albums.Id
|
||||
JOIN Artists on Albums.ArtistMetadataId = Artists.ArtistMetadataId
|
||||
LEFT OUTER JOIN TrackFiles ON Tracks.TrackFileId = TrackFiles.Id
|
||||
WHERE AlbumReleases.Monitored = 1";
|
||||
}
|
||||
|
||||
private string GetGroupByClause()
|
||||
{
|
||||
return "GROUP BY Artists.Id, Albums.Id";
|
||||
}
|
||||
SUM(CASE WHEN TrackFiles.Id IS NULL THEN 0 ELSE 1 END) AS TrackFileCount")
|
||||
.Join<Track, AlbumRelease>((t, r) => t.AlbumReleaseId == r.Id)
|
||||
.Join<AlbumRelease, Album>((r, a) => r.AlbumId == a.Id)
|
||||
.Join<Album, Artist>((album, artist) => album.ArtistMetadataId == artist.ArtistMetadataId)
|
||||
.LeftJoin<Track, TrackFile>((t, f) => t.TrackFileId == f.Id)
|
||||
.Where<AlbumRelease>(x => x.Monitored == true)
|
||||
.GroupBy<Artist>(x => x.Id)
|
||||
.GroupBy<Album>(x => x.Id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,12 @@ namespace NzbDrone.Core.Backup
|
|||
|
||||
public void BackupDatabase(IDatabase database, string targetDirectory)
|
||||
{
|
||||
var sourceConnectionString = database.GetDataMapper().ConnectionString;
|
||||
var sourceConnectionString = "";
|
||||
using (var db = database.OpenConnection())
|
||||
{
|
||||
sourceConnectionString = db.ConnectionString;
|
||||
}
|
||||
|
||||
var backupConnectionStringBuilder = new SQLiteConnectionStringBuilder(sourceConnectionString);
|
||||
|
||||
backupConnectionStringBuilder.DataSource = Path.Combine(targetDirectory, Path.GetFileName(backupConnectionStringBuilder.DataSource));
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using Marr.Data.QGen;
|
||||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Music;
|
||||
|
@ -22,26 +22,24 @@ namespace NzbDrone.Core.Blacklisting
|
|||
|
||||
public List<Blacklist> BlacklistedByTitle(int artistId, string sourceTitle)
|
||||
{
|
||||
return Query.Where(e => e.ArtistId == artistId)
|
||||
.AndWhere(e => e.SourceTitle.Contains(sourceTitle));
|
||||
return Query(e => e.ArtistId == artistId && e.SourceTitle.Contains(sourceTitle));
|
||||
}
|
||||
|
||||
public List<Blacklist> BlacklistedByTorrentInfoHash(int artistId, string torrentInfoHash)
|
||||
{
|
||||
return Query.Where(e => e.ArtistId == artistId)
|
||||
.AndWhere(e => e.TorrentInfoHash.Contains(torrentInfoHash));
|
||||
return Query(e => e.ArtistId == artistId && e.TorrentInfoHash.Contains(torrentInfoHash));
|
||||
}
|
||||
|
||||
public List<Blacklist> BlacklistedByArtist(int artistId)
|
||||
{
|
||||
return Query.Where(b => b.ArtistId == artistId);
|
||||
return Query(b => b.ArtistId == artistId);
|
||||
}
|
||||
|
||||
protected override SortBuilder<Blacklist> GetPagedQuery(QueryBuilder<Blacklist> query, PagingSpec<Blacklist> pagingSpec)
|
||||
{
|
||||
var baseQuery = query.Join<Blacklist, Artist>(JoinType.Inner, h => h.Artist, (h, s) => h.ArtistId == s.Id);
|
||||
|
||||
return base.GetPagedQuery(baseQuery, pagingSpec);
|
||||
}
|
||||
protected override SqlBuilder PagedBuilder() => new SqlBuilder().Join<Blacklist, Artist>((b, m) => b.ArtistId == m.Id);
|
||||
protected override IEnumerable<Blacklist> PagedQuery(SqlBuilder builder) => _database.QueryJoined<Blacklist, Artist>(builder, (bl, artist) =>
|
||||
{
|
||||
bl.Artist = artist;
|
||||
return bl;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@ namespace NzbDrone.Core.Configuration
|
|||
bool AnalyticsEnabled { get; }
|
||||
string LogLevel { get; }
|
||||
string ConsoleLogLevel { get; }
|
||||
bool LogSql { get; }
|
||||
int LogRotate { get; }
|
||||
bool FilterSentryEvents { get; }
|
||||
string Branch { get; }
|
||||
string ApiKey { get; }
|
||||
|
@ -177,6 +179,8 @@ namespace NzbDrone.Core.Configuration
|
|||
|
||||
public string LogLevel => GetValue("LogLevel", "info");
|
||||
public string ConsoleLogLevel => GetValue("ConsoleLogLevel", string.Empty, persist: false);
|
||||
public bool LogSql => GetValueBoolean("LogSql", false, persist: false);
|
||||
public int LogRotate => GetValueInt("LogRotate", 50, persist: false);
|
||||
public bool FilterSentryEvents => GetValueBoolean("FilterSentryEvents", true, persist: false);
|
||||
public string SslCertPath => GetValue("SslCertPath", "");
|
||||
public string SslCertPassword => GetValue("SslCertPassword", "");
|
||||
|
@ -204,9 +208,9 @@ namespace NzbDrone.Core.Configuration
|
|||
|
||||
public string UpdateScriptPath => GetValue("UpdateScriptPath", "", false);
|
||||
|
||||
public int GetValueInt(string key, int defaultValue)
|
||||
public int GetValueInt(string key, int defaultValue, bool persist = true)
|
||||
{
|
||||
return Convert.ToInt32(GetValue(key, defaultValue));
|
||||
return Convert.ToInt32(GetValue(key, defaultValue, persist));
|
||||
}
|
||||
|
||||
public bool GetValueBoolean(string key, bool defaultValue, bool persist = true)
|
||||
|
|
|
@ -19,7 +19,7 @@ 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)
|
||||
|
|
|
@ -3,10 +3,10 @@ using System.Collections.Generic;
|
|||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using Marr.Data;
|
||||
using Marr.Data.QGen;
|
||||
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
|
||||
|
@ -17,59 +17,79 @@ namespace NzbDrone.Core.Datastore
|
|||
IEnumerable<TModel> All();
|
||||
int Count();
|
||||
TModel Get(int id);
|
||||
IEnumerable<TModel> Get(IEnumerable<int> ids);
|
||||
TModel SingleOrDefault();
|
||||
TModel Insert(TModel model);
|
||||
TModel Update(TModel model);
|
||||
TModel Upsert(TModel model);
|
||||
void Delete(int id);
|
||||
void SetFields(TModel model, params Expression<Func<TModel, object>>[] properties);
|
||||
void Delete(TModel model);
|
||||
void Delete(int id);
|
||||
IEnumerable<TModel> Get(IEnumerable<int> ids);
|
||||
void InsertMany(IList<TModel> model);
|
||||
void UpdateMany(IList<TModel> model);
|
||||
void SetFields(IList<TModel> models, params Expression<Func<TModel, object>>[] properties);
|
||||
void DeleteMany(List<TModel> model);
|
||||
void DeleteMany(IEnumerable<int> ids);
|
||||
void Purge(bool vacuum = false);
|
||||
bool HasItems();
|
||||
void DeleteMany(IEnumerable<int> ids);
|
||||
void SetFields(TModel model, params Expression<Func<TModel, object>>[] properties);
|
||||
void SetFields(IEnumerable<TModel> models, params Expression<Func<TModel, object>>[] properties);
|
||||
TModel Single();
|
||||
TModel SingleOrDefault();
|
||||
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;
|
||||
|
||||
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 => x.IsMappableProperty() && !excluded.Contains(x.Name)).ToList();
|
||||
|
||||
_insertSql = GetInsertSql();
|
||||
_updateSql = GetUpdateSql(_properties);
|
||||
}
|
||||
|
||||
protected virtual QueryBuilder<TModel> Query => DataMapper.Query<TModel>();
|
||||
protected virtual SqlBuilder Builder() => new SqlBuilder();
|
||||
|
||||
protected void Delete(Expression<Func<TModel, bool>> filter)
|
||||
{
|
||||
DataMapper.Delete(filter);
|
||||
}
|
||||
protected virtual List<TModel> Query(SqlBuilder builder) => _database.Query<TModel>(builder).ToList();
|
||||
|
||||
public IEnumerable<TModel> All()
|
||||
{
|
||||
return Query.ToList();
|
||||
}
|
||||
protected List<TModel> Query(Expression<Func<TModel, bool>> where) => Query(Builder().Where(where));
|
||||
|
||||
protected virtual List<TModel> QueryDistinct(SqlBuilder builder) => _database.QueryDistinct<TModel>(builder).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)
|
||||
{
|
||||
|
@ -81,13 +101,16 @@ namespace NzbDrone.Core.Datastore
|
|||
|
||||
public IEnumerable<TModel> Get(IEnumerable<int> ids)
|
||||
{
|
||||
var idList = ids.ToList();
|
||||
var query = string.Format("[t0].[Id] IN ({0})", string.Join(",", idList));
|
||||
var result = Query.Where(query).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;
|
||||
|
@ -110,13 +133,74 @@ 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)
|
||||
{
|
||||
SqlBuilderExtensions.LogQuery(_insertSql, 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)
|
||||
|
@ -124,52 +208,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 UpdateMany(IList<TModel> models)
|
||||
{
|
||||
if (models.Any(x => x.Id == 0))
|
||||
{
|
||||
throw new InvalidOperationException("Can't update model with ID 0");
|
||||
}
|
||||
|
||||
using (var conn = _database.OpenConnection())
|
||||
{
|
||||
UpdateFields(conn, null, models, _properties);
|
||||
}
|
||||
}
|
||||
|
||||
protected void Delete(Expression<Func<TModel, bool>> where)
|
||||
{
|
||||
Delete(Builder().Where<TModel>(where));
|
||||
}
|
||||
|
||||
protected void Delete(SqlBuilder builder)
|
||||
{
|
||||
var sql = builder.AddDeleteTemplate(typeof(TModel)).LogQuery();
|
||||
|
||||
using (var conn = _database.OpenConnection())
|
||||
{
|
||||
conn.Execute(sql.RawSql, sql.Parameters);
|
||||
}
|
||||
}
|
||||
|
||||
public void Delete(TModel model)
|
||||
{
|
||||
Delete(model.Id);
|
||||
}
|
||||
|
||||
public void InsertMany(IList<TModel> models)
|
||||
public void Delete(int id)
|
||||
{
|
||||
using (var unitOfWork = new UnitOfWork(() => DataMapper))
|
||||
{
|
||||
unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted);
|
||||
|
||||
foreach (var model in models)
|
||||
{
|
||||
unitOfWork.DB.Insert(model);
|
||||
}
|
||||
|
||||
unitOfWork.Commit();
|
||||
}
|
||||
Delete(x => x.Id == id);
|
||||
}
|
||||
|
||||
public void UpdateMany(IList<TModel> models)
|
||||
public void DeleteMany(IEnumerable<int> ids)
|
||||
{
|
||||
using (var unitOfWork = new UnitOfWork(() => DataMapper))
|
||||
if (ids.Any())
|
||||
{
|
||||
unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted);
|
||||
|
||||
foreach (var model in models)
|
||||
{
|
||||
var localModel = model;
|
||||
|
||||
if (model.Id == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Can't update model with ID 0");
|
||||
}
|
||||
|
||||
unitOfWork.DB.Update(model, c => c.Id == localModel.Id);
|
||||
}
|
||||
|
||||
unitOfWork.Commit();
|
||||
Delete(x => ids.Contains(x.Id));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,31 +281,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();
|
||||
|
@ -235,67 +308,130 @@ 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(IEnumerable<TModel> models, params Expression<Func<TModel, object>>[] properties)
|
||||
public void SetFields(IList<TModel> models, params Expression<Func<TModel, object>>[] properties)
|
||||
{
|
||||
using (var unitOfWork = new UnitOfWork(() => DataMapper))
|
||||
if (models.Any(x => x.Id == 0))
|
||||
{
|
||||
unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted);
|
||||
throw new InvalidOperationException("Attempted to update model without ID");
|
||||
}
|
||||
|
||||
foreach (var model in models)
|
||||
{
|
||||
if (model.Id == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Can't update model with ID 0");
|
||||
}
|
||||
var propertiesToUpdate = properties.Select(x => x.GetMemberName()).ToList();
|
||||
|
||||
unitOfWork.DB.Update<TModel>()
|
||||
.Where(c => c.Id == model.Id)
|
||||
.ColumnsIncluding(properties)
|
||||
.Entity(model)
|
||||
.Execute();
|
||||
}
|
||||
using (var conn = _database.OpenConnection())
|
||||
{
|
||||
UpdateFields(conn, null, models, propertiesToUpdate);
|
||||
}
|
||||
|
||||
unitOfWork.Commit();
|
||||
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);
|
||||
|
||||
SqlBuilderExtensions.LogQuery(sql, model);
|
||||
|
||||
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);
|
||||
|
||||
foreach (var model in models)
|
||||
{
|
||||
SqlBuilderExtensions.LogQuery(sql, model);
|
||||
}
|
||||
|
||||
connection.Execute(sql, models, transaction: transaction);
|
||||
}
|
||||
|
||||
protected virtual SqlBuilder PagedBuilder() => Builder();
|
||||
protected virtual IEnumerable<TModel> PagedQuery(SqlBuilder sql) => Query(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(), pagingSpec, PagedQuery);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
protected List<TModel> GetPagedRecords(SqlBuilder builder, PagingSpec<TModel> pagingSpec, Func<SqlBuilder, 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}");
|
||||
|
||||
return queryFunc(builder).ToList();
|
||||
}
|
||||
|
||||
protected int GetPagedRecordCount(SqlBuilder builder, PagingSpec<TModel> pagingSpec, string template = null)
|
||||
{
|
||||
AddFilters(builder, pagingSpec);
|
||||
|
||||
SqlBuilder.Template sql;
|
||||
if (template != null)
|
||||
{
|
||||
sql = builder.AddTemplate(template).LogQuery();
|
||||
}
|
||||
else
|
||||
{
|
||||
sql = builder.AddPageCountTemplate(typeof(TModel));
|
||||
}
|
||||
|
||||
return sortQuery.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection())
|
||||
.Skip(pagingSpec.PagingOffset())
|
||||
.Take(pagingSpec.PageSize);
|
||||
using (var conn = _database.OpenConnection())
|
||||
{
|
||||
return conn.ExecuteScalar<int>(sql.RawSql, sql.Parameters);
|
||||
}
|
||||
}
|
||||
|
||||
protected void ModelCreated(TModel model)
|
||||
|
|
|
@ -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 = (bool?)clrValue;
|
||||
|
||||
switch (val)
|
||||
{
|
||||
case true:
|
||||
return 1;
|
||||
case false:
|
||||
return 0;
|
||||
default:
|
||||
return DBNull.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public Type DbType => typeof(int);
|
||||
}
|
||||
}
|
|
@ -1,42 +1,47 @@
|
|||
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)
|
||||
{
|
||||
var result = Json.Deserialize<UnknownCommand>(stringValue);
|
||||
var result = JsonSerializer.Deserialize<UnknownCommand>(stringValue, SerializerSettings);
|
||||
|
||||
result.ContractName = contract;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return Json.Deserialize(stringValue, impType);
|
||||
return (Command)JsonSerializer.Deserialize(stringValue, impType, SerializerSettings);
|
||||
}
|
||||
|
||||
public override void SetValue(IDbDataParameter parameter, Command value)
|
||||
{
|
||||
// Cast to object to get all properties written out
|
||||
// https://github.com/dotnet/corefx/issues/38650
|
||||
parameter.Value = value == null ? null : JsonSerializer.Serialize((object)value, SerializerSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -1,73 +1,52 @@
|
|||
using System;
|
||||
using Marr.Data.Converters;
|
||||
using Marr.Data.Mapping;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
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 = true,
|
||||
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 JsonStringEnumConverter(JsonNamingPolicy.CamelCase, true));
|
||||
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);
|
||||
// Cast to object to get all properties written out
|
||||
// https://github.com/dotnet/corefx/issues/38650
|
||||
parameter.Value = JsonSerializer.Serialize((object)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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,40 +1,24 @@
|
|||
using System;
|
||||
using Marr.Data.Converters;
|
||||
using Marr.Data.Mapping;
|
||||
using System;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,62 +1,21 @@
|
|||
using System;
|
||||
using Marr.Data.Converters;
|
||||
using Marr.Data.Mapping;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using NzbDrone.Core.Music;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Converters
|
||||
{
|
||||
public class PrimaryAlbumTypeIntConverter : JsonConverter, IConverter
|
||||
public class PrimaryAlbumTypeIntConverter : JsonConverter<PrimaryAlbumType>
|
||||
{
|
||||
public object FromDB(ConverterContext context)
|
||||
public override PrimaryAlbumType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (context.DbValue == DBNull.Value)
|
||||
{
|
||||
return PrimaryAlbumType.Album;
|
||||
}
|
||||
|
||||
var val = Convert.ToInt32(context.DbValue);
|
||||
|
||||
return (PrimaryAlbumType)val;
|
||||
var item = reader.GetInt32();
|
||||
return (PrimaryAlbumType)item;
|
||||
}
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
public override void Write(Utf8JsonWriter writer, PrimaryAlbumType 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 PrimaryAlbumType == null)
|
||||
{
|
||||
throw new InvalidOperationException("Attempted to save an album type that isn't really an album type");
|
||||
}
|
||||
|
||||
var primType = (PrimaryAlbumType)clrValue;
|
||||
return (int)primType;
|
||||
}
|
||||
|
||||
public Type DbType => typeof(int);
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(PrimaryAlbumType);
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
var item = reader.Value;
|
||||
return (PrimaryAlbumType)Convert.ToInt32(item);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
writer.WriteValue(ToDB(value));
|
||||
writer.WriteNumberValue((int)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,22 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,62 +1,36 @@
|
|||
using System;
|
||||
using Marr.Data.Converters;
|
||||
using Marr.Data.Mapping;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Dapper;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
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 });
|
||||
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 object ToDB(object clrValue)
|
||||
public override Quality Parse(object value)
|
||||
{
|
||||
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));
|
||||
return (Quality)Convert.ToInt32(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,62 +1,21 @@
|
|||
using System;
|
||||
using Marr.Data.Converters;
|
||||
using Marr.Data.Mapping;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using NzbDrone.Core.Music;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Converters
|
||||
{
|
||||
public class ReleaseStatusIntConverter : JsonConverter, IConverter
|
||||
public class ReleaseStatusIntConverter : JsonConverter<ReleaseStatus>
|
||||
{
|
||||
public object FromDB(ConverterContext context)
|
||||
public override ReleaseStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (context.DbValue == DBNull.Value)
|
||||
{
|
||||
return ReleaseStatus.Official;
|
||||
}
|
||||
|
||||
var val = Convert.ToInt32(context.DbValue);
|
||||
|
||||
return (ReleaseStatus)val;
|
||||
var item = reader.GetInt32();
|
||||
return (ReleaseStatus)item;
|
||||
}
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
public override void Write(Utf8JsonWriter writer, ReleaseStatus 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 ReleaseStatus == null)
|
||||
{
|
||||
throw new InvalidOperationException("Attempted to save a release status that isn't really a release status");
|
||||
}
|
||||
|
||||
var releaseStatus = (ReleaseStatus)clrValue;
|
||||
return (int)releaseStatus;
|
||||
}
|
||||
|
||||
public Type DbType => typeof(int);
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(ReleaseStatus);
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
var item = reader.Value;
|
||||
return (ReleaseStatus)Convert.ToInt32(item);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
writer.WriteValue(ToDB(value));
|
||||
writer.WriteNumberValue((int)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,65 +1,21 @@
|
|||
using System;
|
||||
using Marr.Data.Converters;
|
||||
using Marr.Data.Mapping;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using NzbDrone.Core.Music;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Converters
|
||||
{
|
||||
public class SecondaryAlbumTypeIntConverter : JsonConverter, IConverter
|
||||
public class SecondaryAlbumTypeIntConverter : JsonConverter<SecondaryAlbumType>
|
||||
{
|
||||
public object FromDB(ConverterContext context)
|
||||
public override SecondaryAlbumType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (context.DbValue == DBNull.Value)
|
||||
{
|
||||
return SecondaryAlbumType.Studio;
|
||||
}
|
||||
|
||||
var val = Convert.ToInt32(context.DbValue);
|
||||
|
||||
return (SecondaryAlbumType)val;
|
||||
var item = reader.GetInt32();
|
||||
return (SecondaryAlbumType)item;
|
||||
}
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
public override void Write(Utf8JsonWriter writer, SecondaryAlbumType 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 SecondaryAlbumType == null)
|
||||
{
|
||||
throw new InvalidOperationException("Attempted to save an album type that isn't really an album type");
|
||||
}
|
||||
|
||||
var secType = (SecondaryAlbumType)clrValue;
|
||||
return (int)secType;
|
||||
}
|
||||
|
||||
public Type DbType => typeof(int);
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(SecondaryAlbumType);
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader,
|
||||
Type objectType,
|
||||
object existingValue,
|
||||
JsonSerializer serializer)
|
||||
{
|
||||
var item = reader.Value;
|
||||
return (SecondaryAlbumType)Convert.ToInt32(item);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
writer.WriteValue(ToDB(value));
|
||||
writer.WriteNumberValue((int)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,43 +1,19 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using Marr.Data.Converters;
|
||||
using Marr.Data.Mapping;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using System;
|
||||
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; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 class UtcConverter : JsonConverter<DateTime>
|
||||
{
|
||||
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return DateTime.Parse(reader.GetString());
|
||||
}
|
||||
|
||||
public object ToDB(object clrValue)
|
||||
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
|
||||
{
|
||||
if (clrValue == DBNull.Value)
|
||||
{
|
||||
return clrValue;
|
||||
}
|
||||
|
||||
var dateTime = (DateTime)clrValue;
|
||||
return dateTime.ToUniversalTime();
|
||||
writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"));
|
||||
}
|
||||
|
||||
public Type DbType => typeof(DateTime);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using Marr.Data;
|
||||
using System;
|
||||
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();
|
||||
}
|
||||
|
@ -35,8 +36,11 @@ namespace NzbDrone.Core.Datastore
|
|||
{
|
||||
get
|
||||
{
|
||||
var version = _datamapperFactory().ExecuteScalar("SELECT sqlite_version()").ToString();
|
||||
return new Version(version);
|
||||
using (var db = _datamapperFactory())
|
||||
{
|
||||
var version = db.QueryFirstOrDefault<string>("SELECT sqlite_version()");
|
||||
return new Version(version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,7 +60,11 @@ namespace NzbDrone.Core.Datastore
|
|||
try
|
||||
{
|
||||
_logger.Info("Vacuuming {0} database", _databaseName);
|
||||
_datamapperFactory().ExecuteNonQuery("Vacuum;");
|
||||
using (var db = _datamapperFactory())
|
||||
{
|
||||
db.Execute("Vacuum;");
|
||||
}
|
||||
|
||||
_logger.Info("{0} database compressed", _databaseName);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
|
@ -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;
|
||||
|
@ -30,7 +28,6 @@ namespace NzbDrone.Core.Datastore
|
|||
{
|
||||
InitializeEnvironment();
|
||||
|
||||
MapRepository.Instance.ReflectionStrategy = new SimpleReflectionStrategy();
|
||||
TableMapping.Map();
|
||||
}
|
||||
|
||||
|
@ -99,12 +96,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;
|
||||
|
@ -123,7 +119,7 @@ namespace NzbDrone.Core.Datastore
|
|||
|
||||
if (OsInfo.IsOsx)
|
||||
{
|
||||
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://github.com/Lidarr/Lidarr/wiki/FAQ#i-use-lidarr-on-a-mac-and-it-suddenly-stopped-working-what-happened", e, fileName);
|
||||
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://github.com/Sonarr/Sonarr/wiki/FAQ#i-use-sonarr-on-a-mac-and-it-suddenly-stopped-working-what-happened", e, fileName);
|
||||
}
|
||||
|
||||
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://github.com/Lidarr/Lidarr/wiki/FAQ#i-am-getting-an-error-database-disk-image-is-malformed", e, fileName);
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Dapper;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Instrumentation;
|
||||
using NzbDrone.Common.Serializer;
|
||||
|
||||
namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
public static class SqlBuilderExtensions
|
||||
{
|
||||
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(SqlBuilderExtensions));
|
||||
|
||||
public static bool LogSql { get; set; }
|
||||
|
||||
public static SqlBuilder Select(this SqlBuilder builder, params Type[] types)
|
||||
{
|
||||
return builder.Select(types.Select(x => TableMapping.Mapper.TableNameMapping(x) + ".*").Join(", "));
|
||||
}
|
||||
|
||||
public static SqlBuilder SelectDistinct(this SqlBuilder builder, params Type[] types)
|
||||
{
|
||||
return builder.Select("DISTINCT " + types.Select(x => TableMapping.Mapper.TableNameMapping(x) + ".*").Join(", "));
|
||||
}
|
||||
|
||||
public static SqlBuilder SelectCount(this SqlBuilder builder)
|
||||
{
|
||||
return builder.Select("COUNT(*)");
|
||||
}
|
||||
|
||||
public static SqlBuilder SelectCountDistinct<TModel>(this SqlBuilder builder, Expression<Func<TModel, object>> property)
|
||||
{
|
||||
var table = TableMapping.Mapper.TableNameMapping(typeof(TModel));
|
||||
var propName = property.GetMemberName().Name;
|
||||
return builder.Select($"COUNT(DISTINCT \"{table}\".\"{propName}\")");
|
||||
}
|
||||
|
||||
public static SqlBuilder Where<TModel>(this SqlBuilder builder, Expression<Func<TModel, bool>> filter)
|
||||
{
|
||||
var wb = new WhereBuilder(filter, true, builder.Sequence);
|
||||
|
||||
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, true, builder.Sequence);
|
||||
|
||||
return builder.OrWhere(wb.ToString(), wb.Parameters);
|
||||
}
|
||||
|
||||
public static SqlBuilder Join<TLeft, TRight>(this SqlBuilder builder, Expression<Func<TLeft, TRight, bool>> filter)
|
||||
{
|
||||
var wb = new WhereBuilder(filter, false, builder.Sequence);
|
||||
|
||||
var rightTable = TableMapping.Mapper.TableNameMapping(typeof(TRight));
|
||||
|
||||
return builder.Join($"{rightTable} ON {wb.ToString()}");
|
||||
}
|
||||
|
||||
public static SqlBuilder LeftJoin<TLeft, TRight>(this SqlBuilder builder, Expression<Func<TLeft, TRight, bool>> filter)
|
||||
{
|
||||
var wb = new WhereBuilder(filter, false, builder.Sequence);
|
||||
|
||||
var rightTable = TableMapping.Mapper.TableNameMapping(typeof(TRight));
|
||||
|
||||
return builder.LeftJoin($"{rightTable} ON {wb.ToString()}");
|
||||
}
|
||||
|
||||
public static SqlBuilder GroupBy<TModel>(this SqlBuilder builder, Expression<Func<TModel, object>> property)
|
||||
{
|
||||
var table = TableMapping.Mapper.TableNameMapping(typeof(TModel));
|
||||
var propName = property.GetMemberName().Name;
|
||||
return builder.GroupBy($"{table}.{propName}");
|
||||
}
|
||||
|
||||
public static SqlBuilder.Template AddSelectTemplate(this SqlBuilder builder, Type type)
|
||||
{
|
||||
return builder.AddTemplate(TableMapping.Mapper.SelectTemplate(type)).LogQuery();
|
||||
}
|
||||
|
||||
public static SqlBuilder.Template AddPageCountTemplate(this SqlBuilder builder, Type type)
|
||||
{
|
||||
return builder.AddTemplate(TableMapping.Mapper.PageCountTemplate(type)).LogQuery();
|
||||
}
|
||||
|
||||
public static SqlBuilder.Template AddDeleteTemplate(this SqlBuilder builder, Type type)
|
||||
{
|
||||
return builder.AddTemplate(TableMapping.Mapper.DeleteTemplate(type)).LogQuery();
|
||||
}
|
||||
|
||||
public static SqlBuilder.Template LogQuery(this SqlBuilder.Template template)
|
||||
{
|
||||
if (LogSql)
|
||||
{
|
||||
LogQuery(template.RawSql, (DynamicParameters)template.Parameters);
|
||||
}
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
public static void LogQuery(string sql, object parameters)
|
||||
{
|
||||
if (LogSql)
|
||||
{
|
||||
LogQuery(sql, new DynamicParameters(parameters));
|
||||
}
|
||||
}
|
||||
|
||||
private static void LogQuery(string sql, DynamicParameters parameters)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("==== Begin Query Trace ====");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("QUERY TEXT:");
|
||||
sb.AppendLine(sql);
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("PARAMETERS:");
|
||||
foreach (var p in parameters.ToDictionary())
|
||||
{
|
||||
var 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();
|
||||
|
||||
Logger.Trace(sb.ToString());
|
||||
}
|
||||
|
||||
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 && templates.GetValue(dynamicParams) is List<object> list)
|
||||
{
|
||||
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)
|
||||
{
|
||||
var type = obj.GetType();
|
||||
var pairs = type.GetProperties().Where(x => x.IsMappableProperty())
|
||||
.DistinctBy(propertyInfo => propertyInfo.Name)
|
||||
.ToDictionary(
|
||||
propertyInfo => propertyInfo.Name,
|
||||
propertyInfo => propertyInfo.GetValue(obj, null));
|
||||
return pairs;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,48 +1,24 @@
|
|||
using System.Reflection;
|
||||
using Marr.Data;
|
||||
using Marr.Data.Mapping;
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using Dapper;
|
||||
using NzbDrone.Common.Reflection;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Extensions
|
||||
namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
public static class MappingExtensions
|
||||
{
|
||||
public static ColumnMapBuilder<T> MapResultSet<T>(this FluentMappings.MappingsFluentEntity<T> mapBuilder)
|
||||
where T : ResultSet, new()
|
||||
public static PropertyInfo GetMemberName<T, TChild>(this Expression<Func<T, TChild>> member)
|
||||
{
|
||||
return mapBuilder
|
||||
.Columns
|
||||
.AutoMapPropertiesWhere(IsMappableProperty);
|
||||
if (!(member.Body is MemberExpression memberExpression))
|
||||
{
|
||||
memberExpression = (member.Body as UnaryExpression).Operand as MemberExpression;
|
||||
}
|
||||
|
||||
return (PropertyInfo)memberExpression.Member;
|
||||
}
|
||||
|
||||
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)
|
||||
public static bool IsMappableProperty(this MemberInfo memberInfo)
|
||||
{
|
||||
var propertyInfo = memberInfo as PropertyInfo;
|
||||
|
||||
|
@ -56,7 +32,11 @@ namespace NzbDrone.Core.Datastore.Extensions
|
|||
return false;
|
||||
}
|
||||
|
||||
if (propertyInfo.PropertyType.IsSimpleType() || MapRepository.Instance.TypeConverters.ContainsKey(propertyInfo.PropertyType))
|
||||
// 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;
|
||||
}
|
||||
|
|
|
@ -1,46 +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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +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.MapProperties<TParent>();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using Dapper;
|
||||
|
||||
namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
public static class SqlMapperExtensions
|
||||
{
|
||||
public static IEnumerable<T> Query<T>(this IDatabase db, string sql, object param = null)
|
||||
{
|
||||
using (var conn = db.OpenConnection())
|
||||
{
|
||||
var items = SqlMapper.Query<T>(conn, sql, param);
|
||||
if (TableMapping.Mapper.LazyLoadList.TryGetValue(typeof(T), out var lazyProperties))
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
ApplyLazyLoad(db, item, lazyProperties);
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TReturn> Query<TFirst, TSecond, TReturn>(this IDatabase db, string sql, Func<TFirst, TSecond, TReturn> map, object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
|
||||
{
|
||||
TReturn MapWithLazy(TFirst first, TSecond second)
|
||||
{
|
||||
ApplyLazyLoad(db, first);
|
||||
ApplyLazyLoad(db, second);
|
||||
return map(first, second);
|
||||
}
|
||||
|
||||
IEnumerable<TReturn> result = null;
|
||||
using (var conn = db.OpenConnection())
|
||||
{
|
||||
result = SqlMapper.Query<TFirst, TSecond, TReturn>(conn, sql, MapWithLazy, param, transaction, buffered, splitOn, commandTimeout, commandType);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TReturn>(this IDatabase db, string sql, Func<TFirst, TSecond, TThird, TReturn> map, object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
|
||||
{
|
||||
TReturn MapWithLazy(TFirst first, TSecond second, TThird third)
|
||||
{
|
||||
ApplyLazyLoad(db, first);
|
||||
ApplyLazyLoad(db, second);
|
||||
ApplyLazyLoad(db, third);
|
||||
return map(first, second, third);
|
||||
}
|
||||
|
||||
IEnumerable<TReturn> result = null;
|
||||
using (var conn = db.OpenConnection())
|
||||
{
|
||||
result = SqlMapper.Query<TFirst, TSecond, TThird, TReturn>(conn, sql, MapWithLazy, param, transaction, buffered, splitOn, commandTimeout, commandType);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TReturn>(this IDatabase db, string sql, Func<TFirst, TSecond, TThird, TFourth, TReturn> map, object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
|
||||
{
|
||||
TReturn MapWithLazy(TFirst first, TSecond second, TThird third, TFourth fourth)
|
||||
{
|
||||
ApplyLazyLoad(db, first);
|
||||
ApplyLazyLoad(db, second);
|
||||
ApplyLazyLoad(db, third);
|
||||
ApplyLazyLoad(db, fourth);
|
||||
return map(first, second, third, fourth);
|
||||
}
|
||||
|
||||
IEnumerable<TReturn> result = null;
|
||||
using (var conn = db.OpenConnection())
|
||||
{
|
||||
result = SqlMapper.Query<TFirst, TSecond, TThird, TFourth, TReturn>(conn, sql, MapWithLazy, param, transaction, buffered, splitOn, commandTimeout, commandType);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(this IDatabase db, string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TReturn> map, object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
|
||||
{
|
||||
TReturn MapWithLazy(TFirst first, TSecond second, TThird third, TFourth fourth, TFifth fifth)
|
||||
{
|
||||
ApplyLazyLoad(db, first);
|
||||
ApplyLazyLoad(db, second);
|
||||
ApplyLazyLoad(db, third);
|
||||
ApplyLazyLoad(db, fourth);
|
||||
ApplyLazyLoad(db, fifth);
|
||||
return map(first, second, third, fourth, fifth);
|
||||
}
|
||||
|
||||
IEnumerable<TReturn> result = null;
|
||||
using (var conn = db.OpenConnection())
|
||||
{
|
||||
result = SqlMapper.Query<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(conn, sql, MapWithLazy, param, transaction, buffered, splitOn, commandTimeout, commandType);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Query<T>(this IDatabase db, SqlBuilder builder)
|
||||
{
|
||||
var type = typeof(T);
|
||||
var sql = builder.Select(type).AddSelectTemplate(type);
|
||||
|
||||
return db.Query<T>(sql.RawSql, sql.Parameters);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> QueryDistinct<T>(this IDatabase db, SqlBuilder builder)
|
||||
{
|
||||
var type = typeof(T);
|
||||
var sql = builder.SelectDistinct(type).AddSelectTemplate(type);
|
||||
|
||||
return db.Query<T>(sql.RawSql, sql.Parameters);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> QueryJoined<T, T2>(this IDatabase db, SqlBuilder builder, Func<T, T2, T> mapper)
|
||||
{
|
||||
var type = typeof(T);
|
||||
var sql = builder.Select(type, typeof(T2)).AddSelectTemplate(type);
|
||||
|
||||
return db.Query(sql.RawSql, mapper, sql.Parameters);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> QueryJoined<T, T2, T3>(this IDatabase db, SqlBuilder builder, Func<T, T2, T3, T> mapper)
|
||||
{
|
||||
var type = typeof(T);
|
||||
var sql = builder.Select(type, typeof(T2), typeof(T3)).AddSelectTemplate(type);
|
||||
|
||||
return db.Query(sql.RawSql, mapper, sql.Parameters);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> QueryJoined<T, T2, T3, T4>(this IDatabase db, SqlBuilder builder, Func<T, T2, T3, T4, T> mapper)
|
||||
{
|
||||
var type = typeof(T);
|
||||
var sql = builder.Select(type, typeof(T2), typeof(T3), typeof(T4)).AddSelectTemplate(type);
|
||||
|
||||
return db.Query(sql.RawSql, mapper, sql.Parameters);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> QueryJoined<T, T2, T3, T4, T5>(this IDatabase db, SqlBuilder builder, Func<T, T2, T3, T4, T5, T> mapper)
|
||||
{
|
||||
var type = typeof(T);
|
||||
var sql = builder.Select(type, typeof(T2), typeof(T3), typeof(T4), typeof(T5)).AddSelectTemplate(type);
|
||||
|
||||
return db.Query(sql.RawSql, mapper, sql.Parameters);
|
||||
}
|
||||
|
||||
private static void ApplyLazyLoad<TModel>(IDatabase db, TModel model)
|
||||
{
|
||||
if (TableMapping.Mapper.LazyLoadList.TryGetValue(typeof(TModel), out var lazyProperties))
|
||||
{
|
||||
ApplyLazyLoad(db, model, lazyProperties);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ApplyLazyLoad<TModel>(IDatabase db, TModel model, List<LazyLoadedProperty> lazyProperties)
|
||||
{
|
||||
if (model == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var lazyProperty in lazyProperties)
|
||||
{
|
||||
var lazy = (ILazyLoaded)lazyProperty.LazyLoad.Clone();
|
||||
lazy.Prepare(db, model);
|
||||
lazyProperty.Property.SetValue(model, lazy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
using System;
|
||||
|
||||
namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
public interface ILazyLoaded : ICloneable
|
||||
{
|
||||
bool IsLoaded { get; }
|
||||
void Prepare(IDatabase database, object parent);
|
||||
void LazyLoad();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows a field to be lazy loaded.
|
||||
/// </summary>
|
||||
/// <typeparam name="TChild"></typeparam>
|
||||
public class LazyLoaded<TChild> : ILazyLoaded
|
||||
{
|
||||
protected TChild _value;
|
||||
|
||||
public LazyLoaded()
|
||||
{
|
||||
}
|
||||
|
||||
public LazyLoaded(TChild val)
|
||||
{
|
||||
_value = val;
|
||||
IsLoaded = true;
|
||||
}
|
||||
|
||||
public TChild Value
|
||||
{
|
||||
get
|
||||
{
|
||||
LazyLoad();
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLoaded { get; protected set; }
|
||||
|
||||
public static implicit operator LazyLoaded<TChild>(TChild val)
|
||||
{
|
||||
return new LazyLoaded<TChild>(val);
|
||||
}
|
||||
|
||||
public static implicit operator TChild(LazyLoaded<TChild> lazy)
|
||||
{
|
||||
return lazy.Value;
|
||||
}
|
||||
|
||||
public virtual void Prepare(IDatabase database, object parent)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void LazyLoad()
|
||||
{
|
||||
}
|
||||
|
||||
public object Clone()
|
||||
{
|
||||
return MemberwiseClone();
|
||||
}
|
||||
|
||||
public bool ShouldSerializeValue()
|
||||
{
|
||||
return IsLoaded;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is the lazy loading proxy.
|
||||
/// </summary>
|
||||
/// <typeparam name="TParent">The parent entity that contains the lazy loaded entity.</typeparam>
|
||||
/// <typeparam name="TChild">The child entity that is being lazy loaded.</typeparam>
|
||||
internal class LazyLoaded<TParent, TChild> : LazyLoaded<TChild>
|
||||
{
|
||||
private readonly Func<IDatabase, TParent, TChild> _query;
|
||||
private readonly Func<TParent, bool> _condition;
|
||||
|
||||
private IDatabase _database;
|
||||
private TParent _parent;
|
||||
|
||||
public LazyLoaded(TChild val)
|
||||
: base(val)
|
||||
{
|
||||
_value = val;
|
||||
IsLoaded = true;
|
||||
}
|
||||
|
||||
internal LazyLoaded(Func<IDatabase, TParent, TChild> query, Func<TParent, bool> condition = null)
|
||||
{
|
||||
_query = query;
|
||||
_condition = condition;
|
||||
}
|
||||
|
||||
public static implicit operator LazyLoaded<TParent, TChild>(TChild val)
|
||||
{
|
||||
return new LazyLoaded<TParent, TChild>(val);
|
||||
}
|
||||
|
||||
public static implicit operator TChild(LazyLoaded<TParent, TChild> lazy)
|
||||
{
|
||||
return lazy.Value;
|
||||
}
|
||||
|
||||
public override void Prepare(IDatabase database, object parent)
|
||||
{
|
||||
_database = database;
|
||||
_parent = (TParent)parent;
|
||||
}
|
||||
|
||||
public override void LazyLoad()
|
||||
{
|
||||
if (!IsLoaded)
|
||||
{
|
||||
if (_condition != null && _condition(_parent))
|
||||
{
|
||||
_value = _query(_database, _parent);
|
||||
}
|
||||
else
|
||||
{
|
||||
_value = default;
|
||||
}
|
||||
|
||||
IsLoaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
using System;
|
||||
using Marr.Data;
|
||||
using System;
|
||||
using System.Data;
|
||||
|
||||
namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
|
@ -16,9 +16,9 @@ namespace NzbDrone.Core.Datastore
|
|||
_database = database;
|
||||
}
|
||||
|
||||
public IDataMapper GetDataMapper()
|
||||
public IDbConnection OpenConnection()
|
||||
{
|
||||
return _database.GetDataMapper();
|
||||
return _database.OpenConnection();
|
||||
}
|
||||
|
||||
public Version Version => _database.Version;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using System;
|
||||
using Marr.Data;
|
||||
using System;
|
||||
using System.Data;
|
||||
|
||||
namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
|
@ -16,9 +16,9 @@ namespace NzbDrone.Core.Datastore
|
|||
_database = database;
|
||||
}
|
||||
|
||||
public IDataMapper GetDataMapper()
|
||||
public IDbConnection OpenConnection()
|
||||
{
|
||||
return _database.GetDataMapper();
|
||||
return _database.OpenConnection();
|
||||
}
|
||||
|
||||
public Version Version => _database.Version;
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Dapper;
|
||||
|
||||
namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
public class SqlBuilder
|
||||
{
|
||||
private readonly Dictionary<string, Clauses> _data = new Dictionary<string, Clauses>();
|
||||
|
||||
public int Sequence { get; private set; }
|
||||
|
||||
public Template AddTemplate(string sql, dynamic parameters = null) =>
|
||||
new Template(this, sql, parameters);
|
||||
|
||||
public SqlBuilder Intersect(string sql, dynamic parameters = null) =>
|
||||
AddClause("intersect", sql, parameters, "\nINTERSECT\n ", "\n ", "\n", false);
|
||||
|
||||
public SqlBuilder InnerJoin(string sql, dynamic parameters = null) =>
|
||||
AddClause("innerjoin", sql, parameters, "\nINNER JOIN ", "\nINNER JOIN ", "\n", false);
|
||||
|
||||
public SqlBuilder LeftJoin(string sql, dynamic parameters = null) =>
|
||||
AddClause("leftjoin", sql, parameters, "\nLEFT JOIN ", "\nLEFT JOIN ", "\n", false);
|
||||
|
||||
public SqlBuilder RightJoin(string sql, dynamic parameters = null) =>
|
||||
AddClause("rightjoin", sql, parameters, "\nRIGHT JOIN ", "\nRIGHT JOIN ", "\n", false);
|
||||
|
||||
public SqlBuilder Where(string sql, dynamic parameters = null) =>
|
||||
AddClause("where", sql, parameters, " AND ", "WHERE ", "\n", false);
|
||||
|
||||
public SqlBuilder OrWhere(string sql, dynamic parameters = null) =>
|
||||
AddClause("where", sql, parameters, " OR ", "WHERE ", "\n", true);
|
||||
|
||||
public SqlBuilder OrderBy(string sql, dynamic parameters = null) =>
|
||||
AddClause("orderby", sql, parameters, " , ", "ORDER BY ", "\n", false);
|
||||
|
||||
public SqlBuilder Select(string sql, dynamic parameters = null) =>
|
||||
AddClause("select", sql, parameters, " , ", "", "\n", false);
|
||||
|
||||
public SqlBuilder AddParameters(dynamic parameters) =>
|
||||
AddClause("--parameters", "", parameters, "", "", "", false);
|
||||
|
||||
public SqlBuilder Join(string sql, dynamic parameters = null) =>
|
||||
AddClause("join", sql, parameters, "\nJOIN ", "\nJOIN ", "\n", false);
|
||||
|
||||
public SqlBuilder GroupBy(string sql, dynamic parameters = null) =>
|
||||
AddClause("groupby", sql, parameters, " , ", "\nGROUP BY ", "\n", false);
|
||||
|
||||
public SqlBuilder Having(string sql, dynamic parameters = null) =>
|
||||
AddClause("having", sql, parameters, "\nAND ", "HAVING ", "\n", false);
|
||||
|
||||
protected SqlBuilder AddClause(string name, string sql, object parameters, string joiner, string prefix = "", string postfix = "", bool isInclusive = false)
|
||||
{
|
||||
if (!_data.TryGetValue(name, out var clauses))
|
||||
{
|
||||
clauses = new Clauses(joiner, prefix, postfix);
|
||||
_data[name] = clauses;
|
||||
}
|
||||
|
||||
clauses.Add(new Clause { Sql = sql, Parameters = parameters, IsInclusive = isInclusive });
|
||||
Sequence++;
|
||||
return this;
|
||||
}
|
||||
|
||||
public class Template
|
||||
{
|
||||
private static readonly Regex _regex = new Regex(@"\/\*\*.+?\*\*\/", RegexOptions.Compiled | RegexOptions.Multiline);
|
||||
|
||||
private readonly string _sql;
|
||||
private readonly SqlBuilder _builder;
|
||||
private readonly object _initParams;
|
||||
|
||||
private int _dataSeq = -1; // Unresolved
|
||||
private string _rawSql;
|
||||
private object _parameters;
|
||||
|
||||
public Template(SqlBuilder builder, string sql, dynamic parameters)
|
||||
{
|
||||
_initParams = parameters;
|
||||
_sql = sql;
|
||||
_builder = builder;
|
||||
}
|
||||
|
||||
public string RawSql
|
||||
{
|
||||
get
|
||||
{
|
||||
ResolveSql();
|
||||
return _rawSql;
|
||||
}
|
||||
}
|
||||
|
||||
public object Parameters
|
||||
{
|
||||
get
|
||||
{
|
||||
ResolveSql();
|
||||
return _parameters;
|
||||
}
|
||||
}
|
||||
|
||||
private void ResolveSql()
|
||||
{
|
||||
if (_dataSeq != _builder.Sequence)
|
||||
{
|
||||
var p = new DynamicParameters(_initParams);
|
||||
|
||||
_rawSql = _sql;
|
||||
|
||||
foreach (var pair in _builder._data)
|
||||
{
|
||||
_rawSql = _rawSql.Replace("/**" + pair.Key + "**/", pair.Value.ResolveClauses(p));
|
||||
}
|
||||
|
||||
_parameters = p;
|
||||
|
||||
// replace all that is left with empty
|
||||
_rawSql = _regex.Replace(_rawSql, "");
|
||||
|
||||
_dataSeq = _builder.Sequence;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Clause
|
||||
{
|
||||
public string Sql { get; set; }
|
||||
public object Parameters { get; set; }
|
||||
public bool IsInclusive { get; set; }
|
||||
}
|
||||
|
||||
private class Clauses : List<Clause>
|
||||
{
|
||||
private readonly string _joiner;
|
||||
private readonly string _prefix;
|
||||
private readonly string _postfix;
|
||||
|
||||
public Clauses(string joiner, string prefix = "", string postfix = "")
|
||||
{
|
||||
_joiner = joiner;
|
||||
_prefix = prefix;
|
||||
_postfix = postfix;
|
||||
}
|
||||
|
||||
public string ResolveClauses(DynamicParameters p)
|
||||
{
|
||||
foreach (var item in this)
|
||||
{
|
||||
p.AddDynamicParams(item.Parameters);
|
||||
}
|
||||
|
||||
return this.Any(a => a.IsInclusive)
|
||||
? _prefix +
|
||||
string.Join(_joiner,
|
||||
this.Where(a => !a.IsInclusive)
|
||||
.Select(c => c.Sql)
|
||||
.Union(new[]
|
||||
{
|
||||
" ( " +
|
||||
string.Join(" OR ", this.Where(a => a.IsInclusive).Select(c => c.Sql).ToArray()) +
|
||||
" ) "
|
||||
}).ToArray()) + _postfix
|
||||
: _prefix + string.Join(_joiner, this.Select(c => c.Sql).ToArray()) + _postfix;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
public class TableMapper
|
||||
{
|
||||
public TableMapper()
|
||||
{
|
||||
IgnoreList = new Dictionary<Type, List<PropertyInfo>>();
|
||||
LazyLoadList = new Dictionary<Type, List<LazyLoadedProperty>>();
|
||||
TableMap = new Dictionary<Type, string>();
|
||||
}
|
||||
|
||||
public Dictionary<Type, List<PropertyInfo>> IgnoreList { get; set; }
|
||||
public Dictionary<Type, List<LazyLoadedProperty>> LazyLoadList { get; set; }
|
||||
public Dictionary<Type, string> TableMap { get; set; }
|
||||
|
||||
public ColumnMapper<TEntity> Entity<TEntity>(string tableName)
|
||||
where TEntity : ModelBase
|
||||
{
|
||||
var type = typeof(TEntity);
|
||||
TableMap.Add(type, tableName);
|
||||
|
||||
if (IgnoreList.TryGetValue(type, out var list))
|
||||
{
|
||||
return new ColumnMapper<TEntity>(list, LazyLoadList[type]);
|
||||
}
|
||||
|
||||
IgnoreList[type] = new List<PropertyInfo>();
|
||||
LazyLoadList[type] = new List<LazyLoadedProperty>();
|
||||
return new ColumnMapper<TEntity>(IgnoreList[type], LazyLoadList[type]);
|
||||
}
|
||||
|
||||
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 string SelectTemplate(Type x)
|
||||
{
|
||||
return $"SELECT /**select**/ FROM {TableMap[x]} /**join**/ /**innerjoin**/ /**leftjoin**/ /**where**/ /**groupby**/ /**having**/ /**orderby**/";
|
||||
}
|
||||
|
||||
public string DeleteTemplate(Type x)
|
||||
{
|
||||
return $"DELETE FROM {TableMap[x]} /**where**/";
|
||||
}
|
||||
|
||||
public string PageCountTemplate(Type x)
|
||||
{
|
||||
return $"SELECT /**select**/ FROM {TableMap[x]} /**join**/ /**innerjoin**/ /**leftjoin**/ /**where**/";
|
||||
}
|
||||
}
|
||||
|
||||
public class LazyLoadedProperty
|
||||
{
|
||||
public PropertyInfo Property { get; set; }
|
||||
public ILazyLoaded LazyLoad { get; set; }
|
||||
}
|
||||
|
||||
public class ColumnMapper<T>
|
||||
where T : ModelBase
|
||||
{
|
||||
private readonly List<PropertyInfo> _ignoreList;
|
||||
private readonly List<LazyLoadedProperty> _lazyLoadList;
|
||||
|
||||
public ColumnMapper(List<PropertyInfo> ignoreList, List<LazyLoadedProperty> lazyLoadList)
|
||||
{
|
||||
_ignoreList = ignoreList;
|
||||
_lazyLoadList = lazyLoadList;
|
||||
}
|
||||
|
||||
public ColumnMapper<T> AutoMapPropertiesWhere(Func<PropertyInfo, bool> predicate)
|
||||
{
|
||||
var properties = typeof(T).GetProperties();
|
||||
_ignoreList.AddRange(properties.Where(x => !predicate(x)));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColumnMapper<T> RegisterModel()
|
||||
{
|
||||
return AutoMapPropertiesWhere(x => x.IsMappableProperty());
|
||||
}
|
||||
|
||||
public ColumnMapper<T> Ignore(Expression<Func<T, object>> property)
|
||||
{
|
||||
_ignoreList.Add(property.GetMemberName());
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColumnMapper<T> LazyLoad<TChild>(Expression<Func<T, LazyLoaded<TChild>>> property, Func<IDatabase, T, TChild> query, Func<T, bool> condition)
|
||||
{
|
||||
var lazyLoad = new LazyLoaded<T, TChild>(query, condition);
|
||||
|
||||
var item = new LazyLoadedProperty
|
||||
{
|
||||
Property = property.GetMemberName(),
|
||||
LazyLoad = lazyLoad
|
||||
};
|
||||
|
||||
_lazyLoadList.Add(item);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColumnMapper<T> HasOne<TChild>(Expression<Func<T, LazyLoaded<TChild>>> portalExpression, Func<T, int> childIdSelector)
|
||||
where TChild : ModelBase
|
||||
{
|
||||
return LazyLoad(portalExpression,
|
||||
(db, parent) =>
|
||||
{
|
||||
var id = childIdSelector(parent);
|
||||
return db.Query<TChild>(new SqlBuilder().Where<TChild>(x => x.Id == id)).SingleOrDefault();
|
||||
},
|
||||
parent => childIdSelector(parent) > 0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Marr.Data;
|
||||
using Marr.Data.Mapping;
|
||||
using Marr.Data.QGen;
|
||||
using NzbDrone.Common.Disk;
|
||||
using Dapper;
|
||||
using NzbDrone.Common.Reflection;
|
||||
using NzbDrone.Core.ArtistStats;
|
||||
using NzbDrone.Core.Authentication;
|
||||
using NzbDrone.Core.Blacklisting;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.CustomFilters;
|
||||
using NzbDrone.Core.Datastore.Converters;
|
||||
using NzbDrone.Core.Datastore.Extensions;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.Extras.Lyrics;
|
||||
|
@ -39,38 +34,47 @@ using NzbDrone.Core.RemotePathMappings;
|
|||
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<ImportListDefinition>().RegisterDefinition("ImportLists")
|
||||
Mapper.Entity<ImportListDefinition>("ImportLists").RegisterModel()
|
||||
.Ignore(x => x.ImplementationName)
|
||||
.Ignore(i => i.Enable)
|
||||
.Ignore(i => i.ListType);
|
||||
|
||||
Mapper.Entity<NotificationDefinition>().RegisterDefinition("Notifications")
|
||||
Mapper.Entity<NotificationDefinition>("Notifications").RegisterModel()
|
||||
.Ignore(x => x.ImplementationName)
|
||||
.Ignore(i => i.SupportsOnGrab)
|
||||
.Ignore(i => i.SupportsOnReleaseImport)
|
||||
.Ignore(i => i.SupportsOnUpgrade)
|
||||
|
@ -80,118 +84,110 @@ namespace NzbDrone.Core.Datastore
|
|||
.Ignore(i => i.SupportsOnImportFailure)
|
||||
.Ignore(i => i.SupportsOnTrackRetag);
|
||||
|
||||
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<Artist>().RegisterModel("Artists")
|
||||
Mapper.Entity<Artist>("Artists")
|
||||
.Ignore(s => s.RootFolderPath)
|
||||
.Ignore(s => s.Name)
|
||||
.Ignore(s => s.ForeignArtistId)
|
||||
.Relationship()
|
||||
.HasOne(a => a.Metadata, a => a.ArtistMetadataId)
|
||||
.HasOne(a => a.QualityProfile, a => a.QualityProfileId)
|
||||
.HasOne(s => s.MetadataProfile, s => s.MetadataProfileId)
|
||||
.For(a => a.Albums)
|
||||
.LazyLoad(condition: a => a.Id > 0, query: (db, a) => db.Query<Album>().Where(rg => rg.ArtistMetadataId == a.Id).ToList());
|
||||
.LazyLoad(a => a.Albums, (db, a) => db.Query<Album>(new SqlBuilder().Where<Album>(rg => rg.ArtistMetadataId == a.Id)).ToList(), a => a.Id > 0);
|
||||
|
||||
Mapper.Entity<ArtistMetadata>().RegisterModel("ArtistMetadata");
|
||||
Mapper.Entity<ArtistMetadata>("ArtistMetadata").RegisterModel();
|
||||
|
||||
Mapper.Entity<Album>().RegisterModel("Albums")
|
||||
.Ignore(r => r.ArtistId)
|
||||
.Relationship()
|
||||
Mapper.Entity<Album>("Albums").RegisterModel()
|
||||
.Ignore(x => x.ArtistId)
|
||||
.HasOne(r => r.ArtistMetadata, r => r.ArtistMetadataId)
|
||||
.For(rg => rg.AlbumReleases)
|
||||
.LazyLoad(condition: rg => rg.Id > 0, query: (db, rg) => db.Query<AlbumRelease>().Where(r => r.AlbumId == rg.Id).ToList())
|
||||
.For(rg => rg.Artist)
|
||||
.LazyLoad(condition: rg => rg.ArtistMetadataId > 0,
|
||||
query: (db, rg) => db.Query<Artist>()
|
||||
.Join<Artist, ArtistMetadata>(JoinType.Inner, a => a.Metadata, (a, m) => a.ArtistMetadataId == m.Id)
|
||||
.Where(a => a.ArtistMetadataId == rg.ArtistMetadataId).SingleOrDefault());
|
||||
.LazyLoad(a => a.AlbumReleases, (db, album) => db.Query<AlbumRelease>(new SqlBuilder().Where<AlbumRelease>(r => r.AlbumId == album.Id)).ToList(), a => a.Id > 0)
|
||||
.LazyLoad(a => a.Artist,
|
||||
(db, album) => ArtistRepository.Query(db,
|
||||
new SqlBuilder()
|
||||
.Join<Artist, ArtistMetadata>((a, m) => a.ArtistMetadataId == m.Id)
|
||||
.Where<Artist>(a => a.ArtistMetadataId == album.ArtistMetadataId)).SingleOrDefault(),
|
||||
a => a.ArtistMetadataId > 0);
|
||||
|
||||
Mapper.Entity<AlbumRelease>().RegisterModel("AlbumReleases")
|
||||
.Relationship()
|
||||
Mapper.Entity<AlbumRelease>("AlbumReleases").RegisterModel()
|
||||
.HasOne(r => r.Album, r => r.AlbumId)
|
||||
.For(r => r.Tracks)
|
||||
.LazyLoad(condition: r => r.Id > 0, query: (db, r) => db.Query<Track>().Where(t => t.AlbumReleaseId == r.Id).ToList());
|
||||
.LazyLoad(x => x.Tracks, (db, release) => db.Query<Track>(new SqlBuilder().Where<Track>(t => t.AlbumReleaseId == release.Id)).ToList(), r => r.Id > 0);
|
||||
|
||||
Mapper.Entity<Track>().RegisterModel("Tracks")
|
||||
Mapper.Entity<Track>("Tracks").RegisterModel()
|
||||
.Ignore(t => t.HasFile)
|
||||
.Ignore(t => t.AlbumId)
|
||||
.Ignore(t => t.Album)
|
||||
.Relationship()
|
||||
.HasOne(track => track.AlbumRelease, track => track.AlbumReleaseId)
|
||||
.HasOne(track => track.ArtistMetadata, track => track.ArtistMetadataId)
|
||||
.For(track => track.TrackFile)
|
||||
.LazyLoad(condition: track => track.TrackFileId > 0,
|
||||
query: (db, track) => db.Query<TrackFile>()
|
||||
.Join<TrackFile, Track>(JoinType.Inner, t => t.Tracks, (t, x) => t.Id == x.TrackFileId)
|
||||
.Join<TrackFile, Album>(JoinType.Inner, t => t.Album, (t, a) => t.AlbumId == a.Id)
|
||||
.Join<TrackFile, Artist>(JoinType.Inner, t => t.Artist, (t, a) => t.Album.Value.ArtistMetadataId == a.ArtistMetadataId)
|
||||
.Join<Artist, ArtistMetadata>(JoinType.Inner, a => a.Metadata, (a, m) => a.ArtistMetadataId == m.Id)
|
||||
.Where(t => t.Id == track.TrackFileId)
|
||||
.SingleOrDefault())
|
||||
.For(t => t.Artist)
|
||||
.LazyLoad(condition: t => t.AlbumReleaseId > 0, query: (db, t) => db.Query<Artist>()
|
||||
.Join<Artist, ArtistMetadata>(JoinType.Inner, a => a.Metadata, (a, m) => a.ArtistMetadataId == m.Id)
|
||||
.Join<Artist, Album>(JoinType.Inner, a => a.Albums, (l, r) => l.ArtistMetadataId == r.ArtistMetadataId)
|
||||
.Join<Album, AlbumRelease>(JoinType.Inner, a => a.AlbumReleases, (l, r) => l.Id == r.AlbumId)
|
||||
.Where<AlbumRelease>(r => r.Id == t.AlbumReleaseId)
|
||||
.SingleOrDefault());
|
||||
.LazyLoad(t => t.TrackFile,
|
||||
(db, track) => MediaFileRepository.Query(db,
|
||||
new SqlBuilder()
|
||||
.Join<TrackFile, Track>((l, r) => l.Id == r.TrackFileId)
|
||||
.Join<TrackFile, Album>((l, r) => l.AlbumId == r.Id)
|
||||
.Join<Album, Artist>((l, r) => l.ArtistMetadataId == r.ArtistMetadataId)
|
||||
.Join<Artist, ArtistMetadata>((l, r) => l.ArtistMetadataId == r.Id)
|
||||
.Where<TrackFile>(t => t.Id == track.TrackFileId)).SingleOrDefault(),
|
||||
t => t.TrackFileId > 0)
|
||||
.LazyLoad(x => x.Artist,
|
||||
(db, t) => ArtistRepository.Query(db,
|
||||
new SqlBuilder()
|
||||
.Join<Artist, ArtistMetadata>((a, m) => a.ArtistMetadataId == m.Id)
|
||||
.Join<Artist, Album>((l, r) => l.ArtistMetadataId == r.ArtistMetadataId)
|
||||
.Join<Album, AlbumRelease>((l, r) => l.Id == r.AlbumId)
|
||||
.Where<AlbumRelease>(r => r.Id == t.AlbumReleaseId)).SingleOrDefault(),
|
||||
t => t.Id > 0);
|
||||
|
||||
Mapper.Entity<TrackFile>().RegisterModel("TrackFiles")
|
||||
.Relationship()
|
||||
Mapper.Entity<TrackFile>("TrackFiles").RegisterModel()
|
||||
.HasOne(f => f.Album, f => f.AlbumId)
|
||||
.For(f => f.Tracks)
|
||||
.LazyLoad(condition: f => f.Id > 0, query: (db, f) => db.Query<Track>()
|
||||
.Where(x => x.TrackFileId == f.Id)
|
||||
.ToList())
|
||||
.For(t => t.Artist)
|
||||
.LazyLoad(condition: f => f.Id > 0, query: (db, f) => db.Query<Artist>()
|
||||
.Join<Artist, ArtistMetadata>(JoinType.Inner, a => a.Metadata, (a, m) => a.ArtistMetadataId == m.Id)
|
||||
.Join<Artist, Album>(JoinType.Inner, a => a.Albums, (l, r) => l.ArtistMetadataId == r.ArtistMetadataId)
|
||||
.Where<Album>(r => r.Id == f.AlbumId)
|
||||
.SingleOrDefault());
|
||||
.LazyLoad(x => x.Tracks, (db, file) => db.Query<Track>(new SqlBuilder().Where<Track>(t => t.TrackFileId == file.Id)).ToList(), x => x.Id > 0)
|
||||
.LazyLoad(x => x.Artist,
|
||||
(db, f) => ArtistRepository.Query(db,
|
||||
new SqlBuilder()
|
||||
.Join<Artist, ArtistMetadata>((a, m) => a.ArtistMetadataId == m.Id)
|
||||
.Join<Artist, Album>((l, r) => l.ArtistMetadataId == r.ArtistMetadataId)
|
||||
.Where<Album>(a => a.Id == f.AlbumId)).SingleOrDefault(),
|
||||
t => t.Id > 0);
|
||||
|
||||
Mapper.Entity<QualityDefinition>().RegisterModel("QualityDefinitions")
|
||||
Mapper.Entity<QualityDefinition>("QualityDefinitions").RegisterModel()
|
||||
.Ignore(d => d.GroupName)
|
||||
.Ignore(d => d.GroupWeight)
|
||||
.Ignore(d => d.Weight);
|
||||
|
||||
Mapper.Entity<QualityProfile>().RegisterModel("QualityProfiles");
|
||||
Mapper.Entity<MetadataProfile>().RegisterModel("MetadataProfiles");
|
||||
Mapper.Entity<Log>().RegisterModel("Logs");
|
||||
Mapper.Entity<NamingConfig>().RegisterModel("NamingConfig");
|
||||
Mapper.Entity<AlbumStatistics>().MapResultSet();
|
||||
Mapper.Entity<Blacklist>().RegisterModel("Blacklist");
|
||||
Mapper.Entity<MetadataFile>().RegisterModel("MetadataFiles");
|
||||
Mapper.Entity<LyricFile>().RegisterModel("LyricFiles");
|
||||
Mapper.Entity<OtherExtraFile>().RegisterModel("ExtraFiles");
|
||||
Mapper.Entity<QualityProfile>("QualityProfiles").RegisterModel();
|
||||
Mapper.Entity<MetadataProfile>("MetadataProfiles").RegisterModel();
|
||||
Mapper.Entity<Log>("Logs").RegisterModel();
|
||||
Mapper.Entity<NamingConfig>("NamingConfig").RegisterModel();
|
||||
|
||||
Mapper.Entity<PendingRelease>().RegisterModel("PendingReleases")
|
||||
Mapper.Entity<Blacklist>("Blacklist").RegisterModel();
|
||||
Mapper.Entity<MetadataFile>("MetadataFiles").RegisterModel();
|
||||
Mapper.Entity<LyricFile>("LyricFiles").RegisterModel();
|
||||
Mapper.Entity<OtherExtraFile>("ExtraFiles").RegisterModel();
|
||||
|
||||
Mapper.Entity<PendingRelease>("PendingReleases").RegisterModel()
|
||||
.Ignore(e => e.RemoteAlbum);
|
||||
|
||||
Mapper.Entity<RemotePathMapping>().RegisterModel("RemotePathMappings");
|
||||
Mapper.Entity<Tag>().RegisterModel("Tags");
|
||||
Mapper.Entity<ReleaseProfile>().RegisterModel("ReleaseProfiles");
|
||||
Mapper.Entity<RemotePathMapping>("RemotePathMappings").RegisterModel();
|
||||
Mapper.Entity<Tag>("Tags").RegisterModel();
|
||||
Mapper.Entity<ReleaseProfile>("ReleaseProfiles").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<ImportListStatus>().RegisterModel("ImportListStatus");
|
||||
Mapper.Entity<IndexerStatus>("IndexerStatus").RegisterModel();
|
||||
Mapper.Entity<DownloadClientStatus>("DownloadClientStatus").RegisterModel();
|
||||
Mapper.Entity<ImportListStatus>("ImportListStatus").RegisterModel();
|
||||
|
||||
Mapper.Entity<CustomFilter>().RegisterModel("CustomFilters");
|
||||
Mapper.Entity<ImportListExclusion>().RegisterModel("ImportListExclusions");
|
||||
Mapper.Entity<CustomFilter>("CustomFilters").RegisterModel();
|
||||
Mapper.Entity<ImportListExclusion>("ImportListExclusions").RegisterModel();
|
||||
}
|
||||
|
||||
private static void RegisterMappers()
|
||||
|
@ -199,40 +195,40 @@ 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(List<QualityProfileQualityItem>), new EmbeddedDocumentConverter(new QualityIntConverter()));
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(QualityModel), new EmbeddedDocumentConverter(new QualityIntConverter()));
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(Dictionary<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(List<string>), new EmbeddedDocumentConverter());
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(List<ProfilePrimaryAlbumTypeItem>), new EmbeddedDocumentConverter(new PrimaryAlbumTypeIntConverter()));
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(List<ProfileSecondaryAlbumTypeItem>), new EmbeddedDocumentConverter(new SecondaryAlbumTypeIntConverter()));
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(List<ProfileReleaseStatusItem>), new EmbeddedDocumentConverter(new ReleaseStatusIntConverter()));
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(ParsedAlbumInfo), new EmbeddedDocumentConverter());
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(ParsedTrackInfo), 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 EmbeddedDocumentConverter<List<QualityProfileQualityItem>>(new QualityIntConverter()));
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<QualityModel>(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 EmbeddedDocumentConverter<List<string>>());
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<ProfilePrimaryAlbumTypeItem>>(new PrimaryAlbumTypeIntConverter()));
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<ProfileSecondaryAlbumTypeItem>>(new SecondaryAlbumTypeIntConverter()));
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<ProfileReleaseStatusItem>>(new ReleaseStatusIntConverter()));
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<ParsedAlbumInfo>());
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<ParsedTrackInfo>());
|
||||
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()
|
||||
{
|
||||
var settingTypes = typeof(IProviderConfig).Assembly.ImplementationsOf<IProviderConfig>();
|
||||
var settingTypes = typeof(IProviderConfig).Assembly.ImplementationsOf<IProviderConfig>()
|
||||
.Where(x => !x.ContainsGenericParameters);
|
||||
|
||||
var providerSettingConverter = new ProviderSettingConverter();
|
||||
foreach (var embeddedType in settingTypes)
|
||||
{
|
||||
MapRepository.Instance.RegisterTypeConverter(embeddedType, providerSettingConverter);
|
||||
SqlMapper.AddTypeHandler(embeddedType, providerSettingConverter);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,16 +236,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,391 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Dapper;
|
||||
|
||||
namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
public class WhereBuilder : ExpressionVisitor
|
||||
{
|
||||
protected StringBuilder _sb;
|
||||
|
||||
private const DbType EnumerableMultiParameter = (DbType)(-1);
|
||||
private readonly string _paramNamePrefix;
|
||||
private readonly bool _requireConcreteValue = false;
|
||||
private int _paramCount = 0;
|
||||
private bool _gotConcreteValue = false;
|
||||
|
||||
public WhereBuilder(Expression filter, bool requireConcreteValue, int seq)
|
||||
{
|
||||
_paramNamePrefix = string.Format("Clause{0}", seq + 1);
|
||||
_requireConcreteValue = requireConcreteValue;
|
||||
_sb = new StringBuilder();
|
||||
|
||||
Parameters = new DynamicParameters();
|
||||
|
||||
if (filter != null)
|
||||
{
|
||||
Visit(filter);
|
||||
}
|
||||
}
|
||||
|
||||
public DynamicParameters Parameters { get; private set; }
|
||||
|
||||
private string AddParameter(object value, DbType? dbType = null)
|
||||
{
|
||||
_gotConcreteValue = true;
|
||||
_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)
|
||||
{
|
||||
var method = expression.Method.Name;
|
||||
|
||||
switch (expression.Method.Name)
|
||||
{
|
||||
case "Contains":
|
||||
ParseContainsExpression(expression);
|
||||
break;
|
||||
|
||||
case "StartsWith":
|
||||
ParseStartsWith(expression);
|
||||
break;
|
||||
|
||||
case "EndsWith":
|
||||
ParseEndsWith(expression);
|
||||
break;
|
||||
|
||||
default:
|
||||
var 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)
|
||||
{
|
||||
var tableName = expression?.Expression?.Type != null ? TableMapping.Mapper.TableNameMapping(expression.Expression.Type) : null;
|
||||
var gotValue = TryGetRightValue(expression, out var value);
|
||||
|
||||
// Only use the SQL condition if the expression didn't resolve to an actual value
|
||||
if (tableName != null && !gotValue)
|
||||
{
|
||||
_sb.Append($"\"{tableName}\".\"{expression.Member.Name}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
// string is IEnumerable<Char> but we don't want to pick up that case
|
||||
var type = value.GetType();
|
||||
var typeInfo = type.GetTypeInfo();
|
||||
var isEnumerable =
|
||||
type != typeof(string) && (
|
||||
typeInfo.ImplementedInterfaces.Any(ti => ti.IsGenericType && ti.GetGenericTypeDefinition() == typeof(IEnumerable<>)) ||
|
||||
(typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(IEnumerable<>)));
|
||||
|
||||
var paramName = isEnumerable ? AddParameter(value, EnumerableMultiParameter) : AddParameter(value);
|
||||
_sb.Append(paramName);
|
||||
}
|
||||
else
|
||||
{
|
||||
_gotConcreteValue = true;
|
||||
_sb.Append("NULL");
|
||||
}
|
||||
}
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
protected override Expression VisitConstant(ConstantExpression expression)
|
||||
{
|
||||
if (expression.Value != null)
|
||||
{
|
||||
var paramName = AddParameter(expression.Value);
|
||||
_sb.Append(paramName);
|
||||
}
|
||||
else
|
||||
{
|
||||
_gotConcreteValue = true;
|
||||
_sb.Append("NULL");
|
||||
}
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
private bool TryGetConstantValue(Expression expression, out object result)
|
||||
{
|
||||
result = null;
|
||||
|
||||
if (expression is ConstantExpression constExp)
|
||||
{
|
||||
result = constExp.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetPropertyValue(MemberExpression expression, out object result)
|
||||
{
|
||||
result = null;
|
||||
|
||||
if (expression.Expression is MemberExpression nested)
|
||||
{
|
||||
// Value is passed in as a property on a parent entity
|
||||
var container = (nested.Expression as ConstantExpression)?.Value;
|
||||
|
||||
if (container == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var entity = GetFieldValue(container, nested.Member);
|
||||
result = GetFieldValue(entity, expression.Member);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetVariableValue(MemberExpression expression, out object result)
|
||||
{
|
||||
result = null;
|
||||
|
||||
// Value is passed in as a variable
|
||||
if (expression.Expression is ConstantExpression nested)
|
||||
{
|
||||
result = GetFieldValue(nested.Value, expression.Member);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetRightValue(Expression expression, out object value)
|
||||
{
|
||||
value = null;
|
||||
|
||||
if (TryGetConstantValue(expression, out value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var memberExp = expression as MemberExpression;
|
||||
|
||||
if (TryGetPropertyValue(memberExp, out value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TryGetVariableValue(memberExp, out value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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 bool IsNullVariable(Expression expression)
|
||||
{
|
||||
if (expression.NodeType == ExpressionType.Constant &&
|
||||
TryGetConstantValue(expression, out var constResult) &&
|
||||
constResult == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (expression.NodeType == ExpressionType.MemberAccess &&
|
||||
expression is MemberExpression member &&
|
||||
((TryGetPropertyValue(member, out var result) && result == null) ||
|
||||
(TryGetVariableValue(member, out result) && result == null)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private string Decode(BinaryExpression expression)
|
||||
{
|
||||
if (IsNullVariable(expression.Right))
|
||||
{
|
||||
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) ||
|
||||
(list.Type == typeof(List<string>) && !TryGetRightValue(list, out var _))))
|
||||
{
|
||||
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 ");
|
||||
|
||||
// hardcode the integer list if it exists to bypass parameter limit
|
||||
if (item.Type == typeof(int) && TryGetRightValue(list, out var value))
|
||||
{
|
||||
var items = (IEnumerable<int>)value;
|
||||
_sb.Append("(");
|
||||
_sb.Append(string.Join(", ", items));
|
||||
_sb.Append(")");
|
||||
|
||||
_gotConcreteValue = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
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()
|
||||
{
|
||||
var sql = _sb.ToString();
|
||||
|
||||
if (_requireConcreteValue && !_gotConcreteValue)
|
||||
{
|
||||
var e = new InvalidOperationException("WhereBuilder requires a concrete condition");
|
||||
e.Data.Add("sql", sql);
|
||||
throw e;
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,17 +20,17 @@ namespace NzbDrone.Core.Download.Pending
|
|||
|
||||
public void DeleteByArtistId(int artistId)
|
||||
{
|
||||
Delete(r => r.ArtistId == artistId);
|
||||
Delete(artistId);
|
||||
}
|
||||
|
||||
public List<PendingRelease> AllByArtistId(int artistId)
|
||||
{
|
||||
return Query.Where(p => p.ArtistId == artistId);
|
||||
return Query(p => p.ArtistId == artistId);
|
||||
}
|
||||
|
||||
public List<PendingRelease> WithoutFallback()
|
||||
{
|
||||
return Query.Where(p => p.Reason != PendingReleaseReason.Fallback);
|
||||
return Query(p => p.Reason != PendingReleaseReason.Fallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -173,7 +173,7 @@ namespace NzbDrone.Core.Extras
|
|||
foreach (var trackFile in trackFiles)
|
||||
{
|
||||
var localTrackFile = trackFile;
|
||||
trackFile.Tracks = new LazyList<Track>(tracks.Where(e => e.TrackFileId == localTrackFile.Id));
|
||||
trackFile.Tracks = tracks.Where(e => e.TrackFileId == localTrackFile.Id).ToList();
|
||||
}
|
||||
|
||||
return trackFiles;
|
||||
|
|
|
@ -42,22 +42,22 @@ namespace NzbDrone.Core.Extras.Files
|
|||
|
||||
public List<TExtraFile> GetFilesByArtist(int artistId)
|
||||
{
|
||||
return Query.Where(c => c.ArtistId == artistId);
|
||||
return Query(c => c.ArtistId == artistId);
|
||||
}
|
||||
|
||||
public List<TExtraFile> GetFilesByAlbum(int artistId, int albumId)
|
||||
{
|
||||
return Query.Where(c => c.ArtistId == artistId && c.AlbumId == albumId);
|
||||
return Query(c => c.ArtistId == artistId && c.AlbumId == albumId);
|
||||
}
|
||||
|
||||
public List<TExtraFile> GetFilesByTrackFile(int trackFileId)
|
||||
{
|
||||
return Query.Where(c => c.TrackFileId == trackFileId);
|
||||
return Query(c => c.TrackFileId == trackFileId);
|
||||
}
|
||||
|
||||
public TExtraFile FindByPath(string path)
|
||||
{
|
||||
return Query.Where(c => c.RelativePath == path).SingleOrDefault();
|
||||
return Query(c => c.RelativePath == path).SingleOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Marr.Data.QGen;
|
||||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Music;
|
||||
|
@ -30,63 +30,72 @@ namespace NzbDrone.Core.History
|
|||
|
||||
public History MostRecentForAlbum(int albumId)
|
||||
{
|
||||
return Query.Where(h => h.AlbumId == albumId)
|
||||
.OrderByDescending(h => h.Date)
|
||||
.FirstOrDefault();
|
||||
return Query(h => h.AlbumId == albumId)
|
||||
.OrderByDescending(h => h.Date)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
public History MostRecentForDownloadId(string downloadId)
|
||||
{
|
||||
return Query.Where(h => h.DownloadId == downloadId)
|
||||
.OrderByDescending(h => h.Date)
|
||||
.FirstOrDefault();
|
||||
return Query(h => h.DownloadId == downloadId)
|
||||
.OrderByDescending(h => h.Date)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
public List<History> FindByDownloadId(string downloadId)
|
||||
{
|
||||
return Query.Join<History, Artist>(JoinType.Left, h => h.Artist, (h, a) => h.ArtistId == a.Id)
|
||||
.Join<History, Album>(JoinType.Left, h => h.Album, (h, r) => h.AlbumId == r.Id)
|
||||
.Where(h => h.DownloadId == downloadId);
|
||||
return _database.QueryJoined<History, Artist, Album>(
|
||||
Builder()
|
||||
.Join<History, Artist>((h, a) => h.ArtistId == a.Id)
|
||||
.Join<History, Album>((h, a) => h.AlbumId == a.Id)
|
||||
.Where<History>(h => h.DownloadId == downloadId),
|
||||
(history, artist, album) =>
|
||||
{
|
||||
history.Artist = artist;
|
||||
history.Album = album;
|
||||
return history;
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
public List<History> GetByArtist(int artistId, HistoryEventType? eventType)
|
||||
{
|
||||
var query = Query.Where(h => h.ArtistId == artistId);
|
||||
var builder = Builder().Where<History>(h => h.ArtistId == artistId);
|
||||
|
||||
if (eventType.HasValue)
|
||||
{
|
||||
query.AndWhere(h => h.EventType == eventType);
|
||||
builder.Where<History>(h => h.EventType == eventType);
|
||||
}
|
||||
|
||||
query.OrderByDescending(h => h.Date);
|
||||
|
||||
return query;
|
||||
return Query(builder).OrderByDescending(h => h.Date).ToList();
|
||||
}
|
||||
|
||||
public List<History> GetByAlbum(int albumId, HistoryEventType? eventType)
|
||||
{
|
||||
var query = Query.Join<History, Album>(JoinType.Inner, h => h.Album, (h, e) => h.AlbumId == e.Id)
|
||||
.Where(h => h.AlbumId == albumId);
|
||||
var builder = Builder()
|
||||
.Join<History, Album>((h, a) => h.AlbumId == a.Id)
|
||||
.Where<History>(h => h.AlbumId == albumId);
|
||||
|
||||
if (eventType.HasValue)
|
||||
{
|
||||
query.AndWhere(h => h.EventType == eventType);
|
||||
builder.Where<History>(h => h.EventType == eventType);
|
||||
}
|
||||
|
||||
query.OrderByDescending(h => h.Date);
|
||||
|
||||
return query;
|
||||
return _database.QueryJoined<History, Album>(
|
||||
builder,
|
||||
(history, album) =>
|
||||
{
|
||||
history.Album = album;
|
||||
return history;
|
||||
}).OrderByDescending(h => h.Date).ToList();
|
||||
}
|
||||
|
||||
public List<History> FindDownloadHistory(int idArtistId, QualityModel quality)
|
||||
{
|
||||
return Query.Where(h =>
|
||||
h.ArtistId == idArtistId &&
|
||||
h.Quality == quality &&
|
||||
(h.EventType == HistoryEventType.Grabbed ||
|
||||
h.EventType == HistoryEventType.DownloadFailed ||
|
||||
h.EventType == HistoryEventType.TrackFileImported))
|
||||
.ToList();
|
||||
var allowed = new[] { HistoryEventType.Grabbed, HistoryEventType.DownloadFailed, HistoryEventType.TrackFileImported };
|
||||
|
||||
return Query(h => h.ArtistId == idArtistId &&
|
||||
h.Quality == quality &&
|
||||
allowed.Contains(h.EventType));
|
||||
}
|
||||
|
||||
public void DeleteForArtist(int artistId)
|
||||
|
@ -94,27 +103,29 @@ namespace NzbDrone.Core.History
|
|||
Delete(c => c.ArtistId == artistId);
|
||||
}
|
||||
|
||||
protected override SortBuilder<History> GetPagedQuery(QueryBuilder<History> query, PagingSpec<History> pagingSpec)
|
||||
{
|
||||
var baseQuery = query.Join<History, Artist>(JoinType.Inner, h => h.Artist, (h, a) => h.ArtistId == a.Id)
|
||||
.Join<History, Album>(JoinType.Inner, h => h.Album, (h, r) => h.AlbumId == r.Id)
|
||||
.Join<History, Track>(JoinType.Left, h => h.Track, (h, t) => h.TrackId == t.Id);
|
||||
|
||||
return base.GetPagedQuery(baseQuery, pagingSpec);
|
||||
}
|
||||
protected override SqlBuilder PagedBuilder() => new SqlBuilder()
|
||||
.Join<History, Artist>((h, a) => h.ArtistId == a.Id)
|
||||
.Join<History, Album>((h, a) => h.AlbumId == a.Id)
|
||||
.LeftJoin<History, Track>((h, t) => h.TrackId == t.Id);
|
||||
protected override IEnumerable<History> PagedQuery(SqlBuilder builder) =>
|
||||
_database.QueryJoined<History, Artist, Album, Track>(builder, (history, artist, album, track) =>
|
||||
{
|
||||
history.Artist = artist;
|
||||
history.Album = album;
|
||||
history.Track = track;
|
||||
return history;
|
||||
});
|
||||
|
||||
public List<History> Since(DateTime date, HistoryEventType? eventType)
|
||||
{
|
||||
var query = Query.Where(h => h.Date >= date);
|
||||
var builder = Builder().Where<History>(x => x.Date >= date);
|
||||
|
||||
if (eventType.HasValue)
|
||||
{
|
||||
query.AndWhere(h => h.EventType == eventType);
|
||||
builder.Where<History>(h => h.EventType == eventType);
|
||||
}
|
||||
|
||||
query.OrderBy(h => h.Date);
|
||||
|
||||
return query;
|
||||
return Query(builder).OrderBy(h => h.Date).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using NzbDrone.Core.Datastore;
|
||||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
{
|
||||
|
@ -13,9 +14,9 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
|
||||
public void Clean()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||
WHERE Id IN (
|
||||
SELECT Id FROM MetadataFiles
|
||||
WHERE RelativePath
|
||||
|
@ -25,6 +26,7 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
OR RelativePath
|
||||
LIKE '/%'
|
||||
)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using NzbDrone.Core.Datastore;
|
||||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
{
|
||||
|
@ -13,12 +14,13 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
|
||||
public void Clean()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM NamingConfig
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM NamingConfig
|
||||
WHERE ID NOT IN (
|
||||
SELECT ID FROM NamingConfig
|
||||
LIMIT 1)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using NzbDrone.Core.Datastore;
|
||||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
{
|
||||
|
@ -13,12 +14,13 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
|
||||
public void Clean()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM Users
|
||||
WHERE ID NOT IN (
|
||||
SELECT ID FROM Users
|
||||
LIMIT 1)");
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM Users
|
||||
WHERE ID NOT IN (
|
||||
SELECT ID FROM Users
|
||||
LIMIT 1)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System;
|
||||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
|
||||
|
@ -15,18 +16,17 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
|
||||
public void Clean()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
var twoWeeksAgo = DateTime.UtcNow.AddDays(-14);
|
||||
|
||||
mapper.Delete<PendingRelease>(p => p.Added < twoWeeksAgo &&
|
||||
(p.Reason == PendingReleaseReason.DownloadClientUnavailable ||
|
||||
p.Reason == PendingReleaseReason.Fallback));
|
||||
|
||||
// mapper.AddParameter("twoWeeksAgo", $"{DateTime.UtcNow.AddDays(-14).ToString("s")}Z");
|
||||
|
||||
// mapper.ExecuteNonQuery(@"DELETE FROM PendingReleases
|
||||
// WHERE Added < @twoWeeksAgo
|
||||
// AND (Reason = 'DownloadClientUnavailable' OR Reason = 'Fallback')");
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM PendingReleases
|
||||
WHERE Added < @TwoWeeksAgo
|
||||
AND REASON IN @Reasons",
|
||||
new
|
||||
{
|
||||
TwoWeeksAgo = DateTime.UtcNow.AddDays(-14),
|
||||
Reasons = new[] { (int)PendingReleaseReason.DownloadClientUnavailable, (int)PendingReleaseReason.Fallback }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
|
@ -21,54 +22,58 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
|
||||
private void DeleteDuplicateArtistMetadata()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||
WHERE Id IN (
|
||||
SELECT Id FROM MetadataFiles
|
||||
WHERE Type = 1
|
||||
GROUP BY ArtistId, Consumer
|
||||
HAVING COUNT(ArtistId) > 1
|
||||
)");
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteDuplicateAlbumMetadata()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
||||
WHERE Id IN (
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||
WHERE Id IN (
|
||||
SELECT Id FROM MetadataFiles
|
||||
WHERE Type = 6
|
||||
GROUP BY AlbumId, Consumer
|
||||
HAVING COUNT(AlbumId) > 1
|
||||
)");
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteDuplicateTrackMetadata()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
||||
WHERE Id IN (
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||
WHERE Id IN (
|
||||
SELECT Id FROM MetadataFiles
|
||||
WHERE Type = 2
|
||||
GROUP BY TrackFileId, Consumer
|
||||
HAVING COUNT(TrackFileId) > 1
|
||||
)");
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteDuplicateTrackImages()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
||||
WHERE Id IN (
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||
WHERE Id IN (
|
||||
SELECT Id FROM MetadataFiles
|
||||
WHERE Type = 5
|
||||
GROUP BY TrackFileId, Consumer
|
||||
HAVING COUNT(TrackFileId) > 1
|
||||
)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
|
@ -13,14 +14,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
|
||||
public void Clean()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM Albums
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM Albums
|
||||
WHERE Id IN (
|
||||
SELECT Albums.Id FROM Albums
|
||||
LEFT OUTER JOIN Artists
|
||||
ON Albums.ArtistMetadataId = Artists.ArtistMetadataId
|
||||
WHERE Artists.Id IS NULL)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
|
@ -13,15 +14,16 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
|
||||
public void Clean()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM ArtistMetadata
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM ArtistMetadata
|
||||
WHERE Id IN (
|
||||
SELECT ArtistMetadata.Id FROM ArtistMetadata
|
||||
LEFT OUTER JOIN Albums ON Albums.ArtistMetadataId = ArtistMetadata.Id
|
||||
LEFT OUTER JOIN Tracks ON Tracks.ArtistMetadataId = ArtistMetadata.Id
|
||||
LEFT OUTER JOIN Artists ON Artists.ArtistMetadataId = ArtistMetadata.Id
|
||||
WHERE Albums.Id IS NULL AND Tracks.Id IS NULL AND Artists.Id IS NULL)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using NzbDrone.Core.Datastore;
|
||||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
{
|
||||
|
@ -13,14 +14,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
|
||||
public void Clean()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM Blacklist
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM Blacklist
|
||||
WHERE Id IN (
|
||||
SELECT Blacklist.Id FROM Blacklist
|
||||
LEFT OUTER JOIN Artists
|
||||
ON Blacklist.ArtistId = Artists.Id
|
||||
WHERE Artists.Id IS NULL)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
|
@ -13,14 +14,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
|
||||
public void Clean()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM DownloadClientStatus
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM DownloadClientStatus
|
||||
WHERE Id IN (
|
||||
SELECT DownloadClientStatus.Id FROM DownloadClientStatus
|
||||
LEFT OUTER JOIN DownloadClients
|
||||
ON DownloadClientStatus.ProviderId = DownloadClients.Id
|
||||
WHERE DownloadClients.Id IS NULL)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using NzbDrone.Core.Datastore;
|
||||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
{
|
||||
|
@ -19,26 +20,28 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
|
||||
private void CleanupOrphanedByArtist()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM History
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM History
|
||||
WHERE Id IN (
|
||||
SELECT History.Id FROM History
|
||||
LEFT OUTER JOIN Artists
|
||||
ON History.ArtistId = Artists.Id
|
||||
WHERE Artists.Id IS NULL)");
|
||||
}
|
||||
}
|
||||
|
||||
private void CleanupOrphanedByAlbum()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM History
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM History
|
||||
WHERE Id IN (
|
||||
SELECT History.Id FROM History
|
||||
LEFT OUTER JOIN Albums
|
||||
ON History.AlbumId = Albums.Id
|
||||
WHERE Albums.Id IS NULL)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
|
@ -13,14 +14,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
|
||||
public void Clean()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM ImportListStatus
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM ImportListStatus
|
||||
WHERE Id IN (
|
||||
SELECT ImportListStatus.Id FROM ImportListStatus
|
||||
LEFT OUTER JOIN ImportLists
|
||||
ON ImportListStatus.ProviderId = ImportLists.Id
|
||||
WHERE ImportLists.Id IS NULL)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
|
@ -13,14 +14,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
|
||||
public void Clean()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM IndexerStatus
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM IndexerStatus
|
||||
WHERE Id IN (
|
||||
SELECT IndexerStatus.Id FROM IndexerStatus
|
||||
LEFT OUTER JOIN Indexers
|
||||
ON IndexerStatus.ProviderId = Indexers.Id
|
||||
WHERE Indexers.Id IS NULL)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
|
@ -22,62 +23,67 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
|
||||
private void DeleteOrphanedByArtist()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||
WHERE Id IN (
|
||||
SELECT MetadataFiles.Id FROM MetadataFiles
|
||||
LEFT OUTER JOIN Artists
|
||||
ON MetadataFiles.ArtistId = Artists.Id
|
||||
WHERE Artists.Id IS NULL)");
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteOrphanedByAlbum()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||
WHERE Id IN (
|
||||
SELECT MetadataFiles.Id FROM MetadataFiles
|
||||
LEFT OUTER JOIN Albums
|
||||
ON MetadataFiles.AlbumId = Albums.Id
|
||||
WHERE MetadataFiles.AlbumId > 0
|
||||
AND Albums.Id IS NULL)");
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteOrphanedByTrackFile()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||
WHERE Id IN (
|
||||
SELECT MetadataFiles.Id FROM MetadataFiles
|
||||
LEFT OUTER JOIN TrackFiles
|
||||
ON MetadataFiles.TrackFileId = TrackFiles.Id
|
||||
WHERE MetadataFiles.TrackFileId > 0
|
||||
AND TrackFiles.Id IS NULL)");
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteWhereAlbumIdIsZero()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||
WHERE Id IN (
|
||||
SELECT Id FROM MetadataFiles
|
||||
WHERE Type IN (4, 6)
|
||||
AND AlbumId = 0)");
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteWhereTrackFileIsZero()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||
WHERE Id IN (
|
||||
SELECT Id FROM MetadataFiles
|
||||
WHERE Type IN (2, 5)
|
||||
AND TrackFileId = 0)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using NzbDrone.Core.Datastore;
|
||||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
{
|
||||
|
@ -13,14 +14,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
|
||||
public void Clean()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM PendingReleases
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM PendingReleases
|
||||
WHERE Id IN (
|
||||
SELECT PendingReleases.Id FROM PendingReleases
|
||||
LEFT OUTER JOIN Artists
|
||||
ON PendingReleases.ArtistId = Artists.Id
|
||||
WHERE Artists.Id IS NULL)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
|
@ -13,14 +14,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
|
||||
public void Clean()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM AlbumReleases
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM AlbumReleases
|
||||
WHERE Id IN (
|
||||
SELECT AlbumReleases.Id FROM AlbumReleases
|
||||
LEFT OUTER JOIN Albums
|
||||
ON AlbumReleases.AlbumId = Albums.Id
|
||||
WHERE Albums.Id IS NULL)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
|
@ -13,10 +14,10 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
|
||||
public void Clean()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
// Unlink where track no longer exists
|
||||
mapper.ExecuteNonQuery(@"UPDATE TrackFiles
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
// Unlink where track no longer exists
|
||||
mapper.Execute(@"UPDATE TrackFiles
|
||||
SET AlbumId = 0
|
||||
WHERE Id IN (
|
||||
SELECT TrackFiles.Id FROM TrackFiles
|
||||
|
@ -24,14 +25,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
ON TrackFiles.Id = Tracks.TrackFileId
|
||||
WHERE Tracks.Id IS NULL)");
|
||||
|
||||
// Unlink Tracks where the Trackfiles entry no longer exists
|
||||
mapper.ExecuteNonQuery(@"UPDATE Tracks
|
||||
// Unlink Tracks where the Trackfiles entry no longer exists
|
||||
mapper.Execute(@"UPDATE Tracks
|
||||
SET TrackFileId = 0
|
||||
WHERE Id IN (
|
||||
SELECT Tracks.Id FROM Tracks
|
||||
LEFT OUTER JOIN TrackFiles
|
||||
ON Tracks.TrackFileId = TrackFiles.Id
|
||||
WHERE TrackFiles.Id IS NULL)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
|
@ -13,14 +14,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
|
||||
public void Clean()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM Tracks
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"DELETE FROM Tracks
|
||||
WHERE Id IN (
|
||||
SELECT Tracks.Id FROM Tracks
|
||||
LEFT OUTER JOIN AlbumReleases
|
||||
ON Tracks.AlbumReleaseId = AlbumReleases.Id
|
||||
WHERE AlbumReleases.Id IS NULL)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using Marr.Data;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
|
@ -17,24 +17,25 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
|
||||
public void Clean()
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
var usedTags = new[] { "Artists", "Notifications", "DelayProfiles", "ReleaseProfiles" }
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
var usedTags = new[] { "Artists", "Notifications", "DelayProfiles", "ReleaseProfiles" }
|
||||
.SelectMany(v => GetUsedTags(v, mapper))
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
|
||||
var usedTagsList = string.Join(",", usedTags.Select(d => d.ToString()).ToArray());
|
||||
var usedTagsList = string.Join(",", usedTags.Select(d => d.ToString()).ToArray());
|
||||
|
||||
mapper.ExecuteNonQuery($"DELETE FROM Tags WHERE NOT Id IN ({usedTagsList})");
|
||||
mapper.Execute($"DELETE FROM Tags WHERE NOT Id IN ({usedTagsList})");
|
||||
}
|
||||
}
|
||||
|
||||
private int[] GetUsedTags(string table, IDataMapper mapper)
|
||||
private int[] GetUsedTags(string table, IDbConnection mapper)
|
||||
{
|
||||
return mapper.ExecuteReader($"SELECT DISTINCT Tags FROM {table} WHERE NOT Tags = '[]'", reader => reader.GetString(0))
|
||||
.SelectMany(Json.Deserialize<List<int>>)
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
return mapper.Query<List<int>>($"SELECT DISTINCT Tags FROM {table} WHERE NOT Tags = '[]'")
|
||||
.SelectMany(x => x)
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using Dapper;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
@ -23,12 +24,13 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
|||
_logger.Debug("Not running scheduled task last execution cleanup during debug");
|
||||
}
|
||||
|
||||
var mapper = _database.GetDataMapper();
|
||||
mapper.AddParameter("time", DateTime.UtcNow);
|
||||
|
||||
mapper.ExecuteNonQuery(@"UPDATE ScheduledTasks
|
||||
SET LastExecution = @time
|
||||
WHERE LastExecution > @time");
|
||||
using (var mapper = _database.OpenConnection())
|
||||
{
|
||||
mapper.Execute(@"UPDATE ScheduledTasks
|
||||
SET LastExecution = @time
|
||||
WHERE LastExecution > @time",
|
||||
new { time = DateTime.UtcNow });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,12 +20,14 @@ namespace NzbDrone.Core.ImportLists.Exclusions
|
|||
|
||||
public ImportListExclusion FindByForeignId(string foreignId)
|
||||
{
|
||||
return Query.Where<ImportListExclusion>(m => m.ForeignId == foreignId).SingleOrDefault();
|
||||
return Query(m => m.ForeignId == foreignId).SingleOrDefault();
|
||||
}
|
||||
|
||||
public List<ImportListExclusion> FindByForeignId(List<string> ids)
|
||||
{
|
||||
return Query.Where($"[ForeignId] IN ('{string.Join("', '", ids)}')").ToList();
|
||||
// Using Enumerable.Contains forces the builder to create an 'IN'
|
||||
// and not a string 'LIKE' expression
|
||||
return Query(x => Enumerable.Contains(ids, x.ForeignId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace NzbDrone.Core.Indexers.Gazelle
|
|||
public int MinimumSeeders { get; set; }
|
||||
|
||||
[FieldDefinition(4)]
|
||||
public SeedCriteriaSettings SeedCriteria { get; } = new SeedCriteriaSettings();
|
||||
public SeedCriteriaSettings SeedCriteria { get; set; } = new SeedCriteriaSettings();
|
||||
|
||||
[FieldDefinition(5, Type = FieldType.Number, Label = "Early Download Limit", Unit = "days", HelpText = "Time before release date Lidarr will download from this indexer, empty is no limit", Advanced = true)]
|
||||
public int? EarlyReleaseLimit { get; set; }
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue