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>
|
<TargetFrameworks>net462;netcoreapp3.1</TargetFrameworks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Marr.Data\Marr.Data.csproj" />
|
|
||||||
<ProjectReference Include="..\Lidarr.Http\Lidarr.Http.csproj" />
|
<ProjectReference Include="..\Lidarr.Http\Lidarr.Http.csproj" />
|
||||||
<ProjectReference Include="..\NzbDrone.Common\Lidarr.Common.csproj" />
|
<ProjectReference Include="..\NzbDrone.Common\Lidarr.Common.csproj" />
|
||||||
<ProjectReference Include="..\NzbDrone.Core\Lidarr.Core.csproj" />
|
<ProjectReference Include="..\NzbDrone.Core\Lidarr.Core.csproj" />
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FizzWare.NBuilder;
|
using FizzWare.NBuilder;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
@ -10,34 +11,230 @@ using NzbDrone.Core.Test.Framework;
|
||||||
namespace NzbDrone.Core.Test.Datastore
|
namespace NzbDrone.Core.Test.Datastore
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class
|
public class BasicRepositoryFixture : DbTest<BasicRepository<ScheduledTask>, ScheduledTask>
|
||||||
BasicRepositoryFixture : DbTest<BasicRepository<ScheduledTask>, ScheduledTask>
|
|
||||||
{
|
{
|
||||||
private ScheduledTask _basicType;
|
private List<ScheduledTask> _basicList;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
_basicType = Builder<ScheduledTask>
|
_basicList = Builder<ScheduledTask>
|
||||||
.CreateNew()
|
.CreateListOfSize(5)
|
||||||
.With(c => c.Id = 0)
|
.All()
|
||||||
.With(c => c.LastExecution = DateTime.UtcNow)
|
.With(x => x.Id = 0)
|
||||||
.Build();
|
.BuildList();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[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);
|
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]
|
[Test]
|
||||||
public void purge_should_delete_all()
|
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();
|
Subject.Purge();
|
||||||
|
|
||||||
|
@ -45,29 +242,29 @@ namespace NzbDrone.Core.Test.Datastore
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_be_able_to_delete_model()
|
public void has_items_should_return_false_with_no_items()
|
||||||
{
|
{
|
||||||
Subject.Insert(_basicType);
|
Subject.HasItems().Should().BeFalse();
|
||||||
Subject.All().Should().HaveCount(1);
|
|
||||||
|
|
||||||
Subject.Delete(_basicType.Id);
|
|
||||||
Subject.All().Should().BeEmpty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_be_able_to_find_by_id()
|
public void has_items_should_return_true_with_items()
|
||||||
{
|
{
|
||||||
Subject.Insert(_basicType);
|
Subject.InsertMany(_basicList);
|
||||||
var storeObject = Subject.Get(_basicType.Id);
|
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]
|
[Test]
|
||||||
public void should_be_able_to_get_single()
|
public void should_be_able_to_get_single()
|
||||||
{
|
{
|
||||||
Subject.Insert(_basicType);
|
Subject.Insert(_basicList[0]);
|
||||||
Subject.SingleOrDefault().Should().NotBeNull();
|
Subject.Single().Should().BeEquivalentTo(_basicList[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -89,9 +286,21 @@ namespace NzbDrone.Core.Test.Datastore
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[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();
|
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;
|
||||||
|
using System.Data.SQLite;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Marr.Data.Converters;
|
|
||||||
using Moq;
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Common.Serializer;
|
|
||||||
using NzbDrone.Core.Datastore.Converters;
|
using NzbDrone.Core.Datastore.Converters;
|
||||||
using NzbDrone.Core.Messaging.Commands;
|
using NzbDrone.Core.Messaging.Commands;
|
||||||
using NzbDrone.Core.Music.Commands;
|
using NzbDrone.Core.Music.Commands;
|
||||||
|
@ -15,67 +13,50 @@ namespace NzbDrone.Core.Test.Datastore.Converters
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class CommandConverterFixture : CoreTest<CommandConverter>
|
public class CommandConverterFixture : CoreTest<CommandConverter>
|
||||||
{
|
{
|
||||||
|
private SQLiteParameter _param;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_param = new SQLiteParameter();
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_return_json_string_when_saving_boolean_to_db()
|
public void should_return_json_string_when_saving_boolean_to_db()
|
||||||
{
|
{
|
||||||
var command = new RefreshArtistCommand();
|
var command = new RefreshArtistCommand();
|
||||||
|
|
||||||
Subject.ToDB(command).Should().BeOfType<string>();
|
Subject.SetValue(_param, command);
|
||||||
|
_param.Value.Should().BeOfType<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_return_null_for_null_value_when_saving_to_db()
|
public void should_return_null_for_null_value_when_saving_to_db()
|
||||||
{
|
{
|
||||||
Subject.ToDB(null).Should().Be(null);
|
Subject.SetValue(_param, null);
|
||||||
}
|
_param.Value.Should().BeNull();
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_return_db_null_for_db_null_value_when_saving_to_db()
|
|
||||||
{
|
|
||||||
Subject.ToDB(DBNull.Value).Should().Be(DBNull.Value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_return_command_when_getting_json_from_db()
|
public void should_return_command_when_getting_json_from_db()
|
||||||
{
|
{
|
||||||
var dataRecordMock = new Mock<IDataRecord>();
|
var data = "{\"name\": \"RefreshArtist\"}";
|
||||||
dataRecordMock.Setup(s => s.GetOrdinal("Name")).Returns(0);
|
|
||||||
dataRecordMock.Setup(s => s.GetString(0)).Returns("RefreshArtist");
|
|
||||||
|
|
||||||
var context = new ConverterContext
|
Subject.Parse(data).Should().BeOfType<RefreshArtistCommand>();
|
||||||
{
|
|
||||||
DataRecord = dataRecordMock.Object,
|
|
||||||
DbValue = new RefreshArtistCommand().ToJson()
|
|
||||||
};
|
|
||||||
|
|
||||||
Subject.FromDB(context).Should().BeOfType<RefreshArtistCommand>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_return_unknown_command_when_getting_json_from_db()
|
public void should_return_unknown_command_when_getting_json_from_db()
|
||||||
{
|
{
|
||||||
var dataRecordMock = new Mock<IDataRecord>();
|
var data = "{\"name\": \"EnsureMediaCovers\"}";
|
||||||
dataRecordMock.Setup(s => s.GetOrdinal("Name")).Returns(0);
|
|
||||||
dataRecordMock.Setup(s => s.GetString(0)).Returns("MockRemovedCommand");
|
|
||||||
|
|
||||||
var context = new ConverterContext
|
Subject.Parse(data).Should().BeOfType<UnknownCommand>();
|
||||||
{
|
|
||||||
DataRecord = dataRecordMock.Object,
|
|
||||||
DbValue = new RefreshArtistCommand(2).ToJson()
|
|
||||||
};
|
|
||||||
|
|
||||||
Subject.FromDB(context).Should().BeOfType<UnknownCommand>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_return_null_for_null_value_when_getting_from_db()
|
public void should_return_null_for_null_value_when_getting_from_db()
|
||||||
{
|
{
|
||||||
var context = new ConverterContext
|
Subject.Parse(null).Should().BeNull();
|
||||||
{
|
|
||||||
DbValue = DBNull.Value
|
|
||||||
};
|
|
||||||
|
|
||||||
Subject.FromDB(context).Should().Be(null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
using System.Data.SQLite;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Marr.Data.Converters;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Core.Datastore.Converters;
|
using NzbDrone.Core.Datastore.Converters;
|
||||||
using NzbDrone.Core.Test.Framework;
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
@ -10,18 +10,21 @@ namespace NzbDrone.Core.Test.Datastore.Converters
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class GuidConverterFixture : CoreTest<GuidConverter>
|
public class GuidConverterFixture : CoreTest<GuidConverter>
|
||||||
{
|
{
|
||||||
|
private SQLiteParameter _param;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_param = new SQLiteParameter();
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_return_string_when_saving_guid_to_db()
|
public void should_return_string_when_saving_guid_to_db()
|
||||||
{
|
{
|
||||||
var guid = Guid.NewGuid();
|
var guid = Guid.NewGuid();
|
||||||
|
|
||||||
Subject.ToDB(guid).Should().Be(guid.ToString());
|
Subject.SetValue(_param, guid);
|
||||||
}
|
_param.Value.Should().Be(guid.ToString());
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_return_db_null_for_null_value_when_saving_to_db()
|
|
||||||
{
|
|
||||||
Subject.ToDB(null).Should().Be(DBNull.Value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -29,23 +32,13 @@ namespace NzbDrone.Core.Test.Datastore.Converters
|
||||||
{
|
{
|
||||||
var guid = Guid.NewGuid();
|
var guid = Guid.NewGuid();
|
||||||
|
|
||||||
var context = new ConverterContext
|
Subject.Parse(guid.ToString()).Should().Be(guid);
|
||||||
{
|
|
||||||
DbValue = guid.ToString()
|
|
||||||
};
|
|
||||||
|
|
||||||
Subject.FromDB(context).Should().Be(guid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_return_empty_guid_for_db_null_value_when_getting_from_db()
|
public void should_return_empty_guid_for_db_null_value_when_getting_from_db()
|
||||||
{
|
{
|
||||||
var context = new ConverterContext
|
Subject.Parse(null).Should().Be(Guid.Empty);
|
||||||
{
|
|
||||||
DbValue = DBNull.Value
|
|
||||||
};
|
|
||||||
|
|
||||||
Subject.FromDB(context).Should().Be(Guid.Empty);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 FluentAssertions;
|
||||||
using Marr.Data.Converters;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Core.Datastore.Converters;
|
using NzbDrone.Core.Datastore.Converters;
|
||||||
|
@ -12,38 +11,37 @@ namespace NzbDrone.Core.Test.Datastore.Converters
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class OsPathConverterFixture : CoreTest<OsPathConverter>
|
public class OsPathConverterFixture : CoreTest<OsPathConverter>
|
||||||
{
|
{
|
||||||
|
private SQLiteParameter _param;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_param = new SQLiteParameter();
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_return_string_when_saving_os_path_to_db()
|
public void should_return_string_when_saving_os_path_to_db()
|
||||||
{
|
{
|
||||||
var path = @"C:\Test\Music".AsOsAgnostic();
|
var path = @"C:\Test\TV".AsOsAgnostic();
|
||||||
var osPath = new OsPath(path);
|
var osPath = new OsPath(path);
|
||||||
|
|
||||||
Subject.ToDB(osPath).Should().Be(path);
|
Subject.SetValue(_param, osPath);
|
||||||
|
_param.Value.Should().Be(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_return_os_path_when_getting_string_from_db()
|
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 osPath = new OsPath(path);
|
||||||
|
|
||||||
var context = new ConverterContext
|
Subject.Parse(path).Should().Be(osPath);
|
||||||
{
|
|
||||||
DbValue = path
|
|
||||||
};
|
|
||||||
|
|
||||||
Subject.FromDB(context).Should().Be(osPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_return_db_null_for_db_null_value_when_getting_from_db()
|
public void should_return_empty_for_null_value_when_getting_from_db()
|
||||||
{
|
{
|
||||||
var context = new ConverterContext
|
Subject.Parse(null).IsEmpty.Should().BeTrue();
|
||||||
{
|
|
||||||
DbValue = DBNull.Value
|
|
||||||
};
|
|
||||||
|
|
||||||
Subject.FromDB(context).Should().Be(DBNull.Value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System.Data.SQLite;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Marr.Data.Converters;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Core.Datastore.Converters;
|
using NzbDrone.Core.Datastore.Converters;
|
||||||
using NzbDrone.Core.Test.Framework;
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
@ -8,30 +7,29 @@ using NzbDrone.Core.ThingiProvider;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Test.Datastore.Converters
|
namespace NzbDrone.Core.Test.Datastore.Converters
|
||||||
{
|
{
|
||||||
|
[Ignore("To reinstate once dapper changes worked out")]
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class ProviderSettingConverterFixture : CoreTest<ProviderSettingConverter>
|
public class ProviderSettingConverterFixture : CoreTest<ProviderSettingConverter>
|
||||||
{
|
{
|
||||||
|
private SQLiteParameter _param;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_param = new SQLiteParameter();
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_return_null_config_if_config_is_null()
|
public void should_return_null_config_if_config_is_null()
|
||||||
{
|
{
|
||||||
var result = Subject.FromDB(new ConverterContext()
|
Subject.Parse(null).Should().Be(NullConfig.Instance);
|
||||||
{
|
|
||||||
DbValue = DBNull.Value
|
|
||||||
});
|
|
||||||
|
|
||||||
result.Should().Be(NullConfig.Instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase(null)]
|
[TestCase(null)]
|
||||||
[TestCase("")]
|
[TestCase("")]
|
||||||
public void should_return_null_config_if_config_is_empty(object dbValue)
|
public void should_return_null_config_if_config_is_empty(object dbValue)
|
||||||
{
|
{
|
||||||
var result = Subject.FromDB(new ConverterContext()
|
Subject.Parse(dbValue).Should().Be(NullConfig.Instance);
|
||||||
{
|
|
||||||
DbValue = dbValue
|
|
||||||
});
|
|
||||||
|
|
||||||
result.Should().Be(NullConfig.Instance);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System.Data.SQLite;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Marr.Data.Converters;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Core.Datastore.Converters;
|
using NzbDrone.Core.Datastore.Converters;
|
||||||
using NzbDrone.Core.Qualities;
|
using NzbDrone.Core.Qualities;
|
||||||
|
@ -9,26 +8,30 @@ using NzbDrone.Core.Test.Framework;
|
||||||
namespace NzbDrone.Core.Test.Datastore.Converters
|
namespace NzbDrone.Core.Test.Datastore.Converters
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class QualityIntConverterFixture : CoreTest<QualityIntConverter>
|
public class QualityIntConverterFixture : CoreTest<DapperQualityIntConverter>
|
||||||
{
|
{
|
||||||
|
private SQLiteParameter _param;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_param = new SQLiteParameter();
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_return_int_when_saving_quality_to_db()
|
public void should_return_int_when_saving_quality_to_db()
|
||||||
{
|
{
|
||||||
var quality = Quality.FLAC;
|
var quality = Quality.FLAC;
|
||||||
|
|
||||||
Subject.ToDB(quality).Should().Be(quality.Id);
|
Subject.SetValue(_param, quality);
|
||||||
|
_param.Value.Should().Be(quality.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_return_0_when_saving_db_null_to_db()
|
public void should_return_0_when_saving_db_null_to_db()
|
||||||
{
|
{
|
||||||
Subject.ToDB(DBNull.Value).Should().Be(0);
|
Subject.SetValue(_param, null);
|
||||||
}
|
_param.Value.Should().Be(0);
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_throw_when_saving_another_object_to_db()
|
|
||||||
{
|
|
||||||
Assert.Throws<InvalidOperationException>(() => Subject.ToDB("Not a quality"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -36,23 +39,13 @@ namespace NzbDrone.Core.Test.Datastore.Converters
|
||||||
{
|
{
|
||||||
var quality = Quality.FLAC;
|
var quality = Quality.FLAC;
|
||||||
|
|
||||||
var context = new ConverterContext
|
Subject.Parse(quality.Id).Should().Be(quality);
|
||||||
{
|
|
||||||
DbValue = quality.Id
|
|
||||||
};
|
|
||||||
|
|
||||||
Subject.FromDB(context).Should().Be(quality);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_return_db_null_for_db_null_value_when_getting_from_db()
|
public void should_return_unknown_for_null_value_when_getting_from_db()
|
||||||
{
|
{
|
||||||
var context = new ConverterContext
|
Subject.Parse(null).Should().Be(Quality.Unknown);
|
||||||
{
|
|
||||||
DbValue = DBNull.Value
|
|
||||||
};
|
|
||||||
|
|
||||||
Subject.FromDB(context).Should().Be(Quality.Unknown);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
using System.Data.SQLite;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Marr.Data.Converters;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Core.Datastore.Converters;
|
using NzbDrone.Core.Datastore.Converters;
|
||||||
using NzbDrone.Core.Test.Framework;
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
@ -8,20 +8,23 @@ using NzbDrone.Core.Test.Framework;
|
||||||
namespace NzbDrone.Core.Test.Datastore.Converters
|
namespace NzbDrone.Core.Test.Datastore.Converters
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class UtcConverterFixture : CoreTest<UtcConverter>
|
public class UtcConverterFixture : CoreTest<DapperUtcConverter>
|
||||||
{
|
{
|
||||||
|
private SQLiteParameter _param;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_param = new SQLiteParameter();
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_return_date_time_when_saving_date_time_to_db()
|
public void should_return_date_time_when_saving_date_time_to_db()
|
||||||
{
|
{
|
||||||
var dateTime = DateTime.Now;
|
var dateTime = DateTime.Now;
|
||||||
|
|
||||||
Subject.ToDB(dateTime).Should().Be(dateTime.ToUniversalTime());
|
Subject.SetValue(_param, dateTime);
|
||||||
}
|
_param.Value.Should().Be(dateTime.ToUniversalTime());
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_return_db_null_when_saving_db_null_to_db()
|
|
||||||
{
|
|
||||||
Subject.ToDB(DBNull.Value).Should().Be(DBNull.Value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -29,23 +32,7 @@ namespace NzbDrone.Core.Test.Datastore.Converters
|
||||||
{
|
{
|
||||||
var dateTime = DateTime.Now.ToUniversalTime();
|
var dateTime = DateTime.Now.ToUniversalTime();
|
||||||
|
|
||||||
var context = new ConverterContext
|
Subject.Parse(dateTime).Should().Be(dateTime);
|
||||||
{
|
|
||||||
DbValue = dateTime
|
|
||||||
};
|
|
||||||
|
|
||||||
Subject.FromDB(context).Should().Be(dateTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_return_db_null_for_db_null_value_when_getting_from_db()
|
|
||||||
{
|
|
||||||
var context = new ConverterContext
|
|
||||||
{
|
|
||||||
DbValue = DBNull.Value
|
|
||||||
};
|
|
||||||
|
|
||||||
Subject.FromDB(context).Should().Be(DBNull.Value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Dapper;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
|
@ -14,14 +15,14 @@ namespace NzbDrone.Core.Test.Datastore
|
||||||
public void SingleOrDefault_should_return_null_on_empty_db()
|
public void SingleOrDefault_should_return_null_on_empty_db()
|
||||||
{
|
{
|
||||||
Mocker.Resolve<IDatabase>()
|
Mocker.Resolve<IDatabase>()
|
||||||
.GetDataMapper().Query<Artist>()
|
.OpenConnection().Query<Artist>("SELECT * FROM Artists")
|
||||||
.SingleOrDefault(c => c.CleanName == "SomeTitle")
|
.SingleOrDefault(c => c.CleanName == "SomeTitle")
|
||||||
.Should()
|
.Should()
|
||||||
.BeNull();
|
.BeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void vaccume()
|
public void vacuum()
|
||||||
{
|
{
|
||||||
Mocker.Resolve<IDatabase>().Vacuum();
|
Mocker.Resolve<IDatabase>().Vacuum();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Dapper;
|
||||||
using FizzWare.NBuilder;
|
using FizzWare.NBuilder;
|
||||||
using Marr.Data.QGen;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
@ -13,11 +13,13 @@ using NzbDrone.Core.Test.Framework;
|
||||||
namespace NzbDrone.Core.Test.Datastore
|
namespace NzbDrone.Core.Test.Datastore
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class MarrDataLazyLoadingFixture : DbTest
|
public class LazyLoadingFixture : DbTest
|
||||||
{
|
{
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
|
SqlBuilderExtensions.LogSql = true;
|
||||||
|
|
||||||
var profile = new QualityProfile
|
var profile = new QualityProfile
|
||||||
{
|
{
|
||||||
Name = "Test",
|
Name = "Test",
|
||||||
|
@ -81,69 +83,12 @@ namespace NzbDrone.Core.Test.Datastore
|
||||||
Db.InsertMany(tracks);
|
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]
|
[Test]
|
||||||
public void should_lazy_load_artist_for_track()
|
public void should_lazy_load_artist_for_track()
|
||||||
{
|
{
|
||||||
var db = Mocker.Resolve<IDatabase>();
|
var db = Mocker.Resolve<TrackRepository>();
|
||||||
var dataMapper = db.GetDataMapper();
|
|
||||||
|
|
||||||
var tracks = dataMapper.Query<Track>()
|
var tracks = db.All();
|
||||||
.ToList();
|
|
||||||
|
|
||||||
Assert.IsNotEmpty(tracks);
|
Assert.IsNotEmpty(tracks);
|
||||||
foreach (var track in tracks)
|
foreach (var track in tracks)
|
||||||
|
@ -159,10 +104,7 @@ namespace NzbDrone.Core.Test.Datastore
|
||||||
public void should_lazy_load_artist_for_trackfile()
|
public void should_lazy_load_artist_for_trackfile()
|
||||||
{
|
{
|
||||||
var db = Mocker.Resolve<IDatabase>();
|
var db = Mocker.Resolve<IDatabase>();
|
||||||
var dataMapper = db.GetDataMapper();
|
var tracks = db.Query<TrackFile>(new SqlBuilder()).ToList();
|
||||||
|
|
||||||
var tracks = dataMapper.Query<TrackFile>()
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
Assert.IsNotEmpty(tracks);
|
Assert.IsNotEmpty(tracks);
|
||||||
foreach (var track in tracks)
|
foreach (var track in tracks)
|
||||||
|
@ -178,10 +120,7 @@ namespace NzbDrone.Core.Test.Datastore
|
||||||
public void should_lazy_load_trackfile_if_not_joined()
|
public void should_lazy_load_trackfile_if_not_joined()
|
||||||
{
|
{
|
||||||
var db = Mocker.Resolve<IDatabase>();
|
var db = Mocker.Resolve<IDatabase>();
|
||||||
var dataMapper = db.GetDataMapper();
|
var tracks = db.Query<Track>(new SqlBuilder()).ToList();
|
||||||
|
|
||||||
var tracks = dataMapper.Query<Track>()
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
foreach (var track in tracks)
|
foreach (var track in tracks)
|
||||||
{
|
{
|
||||||
|
@ -195,14 +134,12 @@ namespace NzbDrone.Core.Test.Datastore
|
||||||
public void should_explicit_load_everything_if_joined()
|
public void should_explicit_load_everything_if_joined()
|
||||||
{
|
{
|
||||||
var db = Mocker.Resolve<IDatabase>();
|
var db = Mocker.Resolve<IDatabase>();
|
||||||
var dataMapper = db.GetDataMapper();
|
var files = MediaFileRepository.Query(db,
|
||||||
|
new SqlBuilder()
|
||||||
var files = dataMapper.Query<TrackFile>()
|
.Join<TrackFile, Track>((f, t) => f.Id == t.TrackFileId)
|
||||||
.Join<TrackFile, Track>(JoinType.Inner, f => f.Tracks, (f, t) => f.Id == t.TrackFileId)
|
.Join<TrackFile, Album>((t, a) => t.AlbumId == a.Id)
|
||||||
.Join<TrackFile, Album>(JoinType.Inner, t => t.Album, (t, a) => t.AlbumId == a.Id)
|
.Join<Album, Artist>((album, artist) => album.ArtistMetadataId == artist.ArtistMetadataId)
|
||||||
.Join<TrackFile, Artist>(JoinType.Inner, t => t.Artist, (t, a) => t.Album.Value.ArtistMetadataId == a.ArtistMetadataId)
|
.Join<Artist, ArtistMetadata>((a, m) => a.ArtistMetadataId == m.Id));
|
||||||
.Join<Artist, ArtistMetadata>(JoinType.Inner, a => a.Metadata, (a, m) => a.ArtistMetadataId == m.Id)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
Assert.IsNotEmpty(files);
|
Assert.IsNotEmpty(files);
|
||||||
foreach (var file in 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()
|
public void should_lazy_load_tracks_if_not_joined_to_trackfile()
|
||||||
{
|
{
|
||||||
var db = Mocker.Resolve<IDatabase>();
|
var db = Mocker.Resolve<IDatabase>();
|
||||||
var dataMapper = db.GetDataMapper();
|
var files = db.QueryJoined<TrackFile, Album, Artist, ArtistMetadata>(
|
||||||
|
new SqlBuilder()
|
||||||
var files = dataMapper.Query<TrackFile>()
|
.Join<TrackFile, Album>((t, a) => t.AlbumId == a.Id)
|
||||||
.Join<TrackFile, Album>(JoinType.Inner, t => t.Album, (t, a) => t.AlbumId == a.Id)
|
.Join<Album, Artist>((album, artist) => album.ArtistMetadataId == artist.ArtistMetadataId)
|
||||||
.Join<TrackFile, Artist>(JoinType.Inner, t => t.Artist, (t, a) => t.Album.Value.ArtistMetadataId == a.ArtistMetadataId)
|
.Join<Artist, ArtistMetadata>((a, m) => a.ArtistMetadataId == m.Id),
|
||||||
.Join<Artist, ArtistMetadata>(JoinType.Inner, a => a.Metadata, (a, m) => a.ArtistMetadataId == m.Id)
|
(file, album, artist, metadata) =>
|
||||||
.ToList();
|
{
|
||||||
|
file.Album = album;
|
||||||
|
file.Artist = artist;
|
||||||
|
file.Artist.Value.Metadata = metadata;
|
||||||
|
return file;
|
||||||
|
});
|
||||||
|
|
||||||
Assert.IsNotEmpty(files);
|
Assert.IsNotEmpty(files);
|
||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
|
@ -244,9 +186,7 @@ namespace NzbDrone.Core.Test.Datastore
|
||||||
public void should_lazy_load_tracks_if_not_joined()
|
public void should_lazy_load_tracks_if_not_joined()
|
||||||
{
|
{
|
||||||
var db = Mocker.Resolve<IDatabase>();
|
var db = Mocker.Resolve<IDatabase>();
|
||||||
var dataMapper = db.GetDataMapper();
|
var release = db.Query<AlbumRelease>(new SqlBuilder().Where<AlbumRelease>(x => x.Id == 1)).SingleOrDefault();
|
||||||
|
|
||||||
var release = dataMapper.Query<AlbumRelease>().Where(x => x.Id == 1).SingleOrDefault();
|
|
||||||
|
|
||||||
Assert.IsFalse(release.Tracks.IsLoaded);
|
Assert.IsFalse(release.Tracks.IsLoaded);
|
||||||
Assert.IsNotNull(release.Tracks.Value);
|
Assert.IsNotNull(release.Tracks.Value);
|
||||||
|
@ -258,10 +198,7 @@ namespace NzbDrone.Core.Test.Datastore
|
||||||
public void should_lazy_load_track_if_not_joined()
|
public void should_lazy_load_track_if_not_joined()
|
||||||
{
|
{
|
||||||
var db = Mocker.Resolve<IDatabase>();
|
var db = Mocker.Resolve<IDatabase>();
|
||||||
var dataMapper = db.GetDataMapper();
|
var tracks = db.Query<TrackFile>(new SqlBuilder()).ToList();
|
||||||
|
|
||||||
var tracks = dataMapper.Query<TrackFile>()
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
foreach (var track in tracks)
|
foreach (var track in tracks)
|
||||||
{
|
{
|
||||||
|
@ -270,28 +207,5 @@ namespace NzbDrone.Core.Test.Datastore
|
||||||
Assert.IsTrue(track.Tracks.IsLoaded);
|
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 System.Collections.Generic;
|
||||||
|
using Dapper;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Marr.Data;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.Datastore.Converters;
|
using NzbDrone.Core.Datastore.Converters;
|
||||||
using NzbDrone.Core.Datastore.Extensions;
|
|
||||||
using NzbDrone.Core.Music;
|
using NzbDrone.Core.Music;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Test.Datastore
|
namespace NzbDrone.Core.Test.Datastore
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class MappingExtensionFixture
|
public class TableMapperFixture
|
||||||
{
|
{
|
||||||
public class EmbeddedType : IEmbeddedDocument
|
public class EmbeddedType : IEmbeddedDocument
|
||||||
{
|
{
|
||||||
|
@ -37,9 +36,8 @@ namespace NzbDrone.Core.Test.Datastore
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(List<EmbeddedType>), new EmbeddedDocumentConverter());
|
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<EmbeddedType>>());
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(EmbeddedType), new EmbeddedDocumentConverter());
|
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<EmbeddedType>());
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(int), new Int32Converter());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -47,7 +45,7 @@ namespace NzbDrone.Core.Test.Datastore
|
||||||
{
|
{
|
||||||
var properties = typeof(TypeWithAllMappableProperties).GetProperties();
|
var properties = typeof(TypeWithAllMappableProperties).GetProperties();
|
||||||
properties.Should().NotBeEmpty();
|
properties.Should().NotBeEmpty();
|
||||||
properties.Should().OnlyContain(c => MappingExtensions.IsMappableProperty(c));
|
properties.Should().OnlyContain(c => c.IsMappableProperty());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -55,7 +53,7 @@ namespace NzbDrone.Core.Test.Datastore
|
||||||
{
|
{
|
||||||
var properties = typeof(TypeWithNoMappableProperties).GetProperties();
|
var properties = typeof(TypeWithNoMappableProperties).GetProperties();
|
||||||
properties.Should().NotBeEmpty();
|
properties.Should().NotBeEmpty();
|
||||||
properties.Should().NotContain(c => MappingExtensions.IsMappableProperty(c));
|
properties.Should().NotContain(c => 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 System.Linq;
|
||||||
using FizzWare.NBuilder;
|
using FizzWare.NBuilder;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Marr.Data;
|
|
||||||
using Moq;
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.DecisionEngine;
|
using NzbDrone.Core.DecisionEngine;
|
||||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using FizzWare.NBuilder;
|
using FizzWare.NBuilder;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Marr.Data;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||||
using NzbDrone.Core.Music;
|
using NzbDrone.Core.Music;
|
||||||
|
|
|
@ -2,10 +2,10 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FizzWare.NBuilder;
|
using FizzWare.NBuilder;
|
||||||
using Marr.Data;
|
|
||||||
using Moq;
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.DecisionEngine;
|
using NzbDrone.Core.DecisionEngine;
|
||||||
using NzbDrone.Core.Download.Pending;
|
using NzbDrone.Core.Download.Pending;
|
||||||
using NzbDrone.Core.Music;
|
using NzbDrone.Core.Music;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FizzWare.NBuilder;
|
using FizzWare.NBuilder;
|
||||||
using Marr.Data;
|
|
||||||
using Moq;
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.DecisionEngine;
|
using NzbDrone.Core.DecisionEngine;
|
||||||
using NzbDrone.Core.Download;
|
using NzbDrone.Core.Download;
|
||||||
using NzbDrone.Core.Download.Pending;
|
using NzbDrone.Core.Download.Pending;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using FizzWare.NBuilder;
|
using FizzWare.NBuilder;
|
||||||
using Marr.Data;
|
|
||||||
using Moq;
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.DecisionEngine;
|
using NzbDrone.Core.DecisionEngine;
|
||||||
using NzbDrone.Core.Download;
|
using NzbDrone.Core.Download;
|
||||||
using NzbDrone.Core.Download.Pending;
|
using NzbDrone.Core.Download.Pending;
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data.SQLite;
|
using System.Data.SQLite;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Marr.Data;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
@ -112,7 +111,7 @@ namespace NzbDrone.Core.Test.Framework
|
||||||
Mocker.SetConstant<IConnectionStringFactory>(Mocker.Resolve<ConnectionStringFactory>());
|
Mocker.SetConstant<IConnectionStringFactory>(Mocker.Resolve<ConnectionStringFactory>());
|
||||||
Mocker.SetConstant<IMigrationController>(Mocker.Resolve<MigrationController>());
|
Mocker.SetConstant<IMigrationController>(Mocker.Resolve<MigrationController>());
|
||||||
|
|
||||||
MapRepository.Instance.EnableTraceLogging = true;
|
SqlBuilderExtensions.LogSql = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Data.Common;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NzbDrone.Common.Serializer;
|
using NzbDrone.Common.Serializer;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
|
@ -13,32 +12,20 @@ namespace NzbDrone.Core.Test.Framework
|
||||||
List<Dictionary<string, object>> Query(string sql);
|
List<Dictionary<string, object>> Query(string sql);
|
||||||
List<T> Query<T>(string sql)
|
List<T> Query<T>(string sql)
|
||||||
where T : new();
|
where T : new();
|
||||||
T QueryScalar<T>(string sql);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DirectDataMapper : IDirectDataMapper
|
public class DirectDataMapper : IDirectDataMapper
|
||||||
{
|
{
|
||||||
private readonly DbProviderFactory _providerFactory;
|
private readonly IDatabase _database;
|
||||||
private readonly string _connectionString;
|
|
||||||
|
|
||||||
public DirectDataMapper(IDatabase database)
|
public DirectDataMapper(IDatabase database)
|
||||||
{
|
{
|
||||||
var dataMapper = database.GetDataMapper();
|
_database = database;
|
||||||
_providerFactory = dataMapper.ProviderFactory;
|
|
||||||
_connectionString = dataMapper.ConnectionString;
|
|
||||||
}
|
|
||||||
|
|
||||||
private DbConnection OpenConnection()
|
|
||||||
{
|
|
||||||
var connection = _providerFactory.CreateConnection();
|
|
||||||
connection.ConnectionString = _connectionString;
|
|
||||||
connection.Open();
|
|
||||||
return connection;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataTable GetDataTable(string sql)
|
public DataTable GetDataTable(string sql)
|
||||||
{
|
{
|
||||||
using (var connection = OpenConnection())
|
using (var connection = _database.OpenConnection())
|
||||||
{
|
{
|
||||||
using (var cmd = connection.CreateCommand())
|
using (var cmd = connection.CreateCommand())
|
||||||
{
|
{
|
||||||
|
@ -65,13 +52,6 @@ namespace NzbDrone.Core.Test.Framework
|
||||||
return dataTable.Rows.Cast<DataRow>().Select(MapToObject<T>).ToList();
|
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)
|
protected Dictionary<string, object> MapToDictionary(DataRow dataRow)
|
||||||
{
|
{
|
||||||
var item = new Dictionary<string, object>();
|
var item = new Dictionary<string, object>();
|
||||||
|
@ -118,28 +98,24 @@ namespace NzbDrone.Core.Test.Framework
|
||||||
propertyType = propertyType.GetGenericArguments()[0];
|
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);
|
propertyInfo.SetValue(item, value, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return item;
|
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.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Moq;
|
using Moq;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
|
@ -21,6 +22,7 @@ namespace NzbDrone.Core.Test.Framework
|
||||||
void Delete<T>(T childModel)
|
void Delete<T>(T childModel)
|
||||||
where T : ModelBase, new();
|
where T : ModelBase, new();
|
||||||
IDirectDataMapper GetDirectDataMapper();
|
IDirectDataMapper GetDirectDataMapper();
|
||||||
|
IDbConnection OpenConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TestDatabase : ITestDatabase
|
public class TestDatabase : ITestDatabase
|
||||||
|
@ -74,5 +76,10 @@ namespace NzbDrone.Core.Test.Framework
|
||||||
{
|
{
|
||||||
return new DirectDataMapper(_dbConnection);
|
return new DirectDataMapper(_dbConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IDbConnection OpenConnection()
|
||||||
|
{
|
||||||
|
return _dbConnection.OpenConnection();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,11 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
|
||||||
[Test]
|
[Test]
|
||||||
public void should_delete_unused_tags()
|
public void should_delete_unused_tags()
|
||||||
{
|
{
|
||||||
var tags = Builder<Tag>.CreateListOfSize(2).BuildList();
|
var tags = Builder<Tag>
|
||||||
|
.CreateListOfSize(2)
|
||||||
|
.All()
|
||||||
|
.With(x => x.Id = 0)
|
||||||
|
.BuildList();
|
||||||
|
|
||||||
Db.InsertMany(tags);
|
Db.InsertMany(tags);
|
||||||
Subject.Clean();
|
Subject.Clean();
|
||||||
|
@ -24,11 +28,17 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
|
||||||
[Test]
|
[Test]
|
||||||
public void should_not_delete_used_tags()
|
public void should_not_delete_used_tags()
|
||||||
{
|
{
|
||||||
var tags = Builder<Tag>.CreateListOfSize(2).BuildList();
|
var tags = Builder<Tag>
|
||||||
|
.CreateListOfSize(2)
|
||||||
|
.All()
|
||||||
|
.With(x => x.Id = 0)
|
||||||
|
.BuildList();
|
||||||
|
|
||||||
Db.InsertMany(tags);
|
Db.InsertMany(tags);
|
||||||
|
|
||||||
var restrictions = Builder<ReleaseProfile>.CreateListOfSize(2)
|
var restrictions = Builder<ReleaseProfile>.CreateListOfSize(2)
|
||||||
.All()
|
.All()
|
||||||
|
.With(x => x.Id = 0)
|
||||||
.With(v => v.Tags.Add(tags[0].Id))
|
.With(v => v.Tags.Add(tags[0].Id))
|
||||||
.BuildList();
|
.BuildList();
|
||||||
Db.InsertMany(restrictions);
|
Db.InsertMany(restrictions);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Marr.Data;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Common.Instrumentation;
|
using NzbDrone.Common.Instrumentation;
|
||||||
|
@ -64,22 +63,6 @@ namespace NzbDrone.Core.Test.Instrumentation
|
||||||
VerifyLog(StoredModel, LogLevel.Info);
|
VerifyLog(StoredModel, LogLevel.Info);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
[Explicit]
|
|
||||||
[ManualTest]
|
|
||||||
public void perf_test()
|
|
||||||
{
|
|
||||||
MapRepository.Instance.EnableTraceLogging = false;
|
|
||||||
for (int i = 0; i < 1000; i++)
|
|
||||||
{
|
|
||||||
_logger.Info(Guid.NewGuid());
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread.Sleep(1000);
|
|
||||||
|
|
||||||
MapRepository.Instance.EnableTraceLogging = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void write_log_exception()
|
public void write_log_exception()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FizzWare.NBuilder;
|
using FizzWare.NBuilder;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Marr.Data;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.MediaFiles.TrackImport.Specifications;
|
using NzbDrone.Core.MediaFiles.TrackImport.Specifications;
|
||||||
using NzbDrone.Core.Music;
|
using NzbDrone.Core.Music;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FizzWare.NBuilder;
|
using FizzWare.NBuilder;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Marr.Data;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.MediaFiles.TrackImport.Specifications;
|
using NzbDrone.Core.MediaFiles.TrackImport.Specifications;
|
||||||
using NzbDrone.Core.Music;
|
using NzbDrone.Core.Music;
|
||||||
|
|
|
@ -2,10 +2,10 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FizzWare.NBuilder;
|
using FizzWare.NBuilder;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Marr.Data;
|
|
||||||
using Moq;
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.Music;
|
using NzbDrone.Core.Music;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
|
|
@ -29,10 +29,7 @@ namespace NzbDrone.Core.Test.MusicTests.AlbumRepositoryTests
|
||||||
Monitored = true,
|
Monitored = true,
|
||||||
ForeignArtistId = "this is a fake id",
|
ForeignArtistId = "this is a fake id",
|
||||||
Id = 1,
|
Id = 1,
|
||||||
Metadata = new ArtistMetadata
|
ArtistMetadataId = 1
|
||||||
{
|
|
||||||
Id = 1
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
_albumRepo = Mocker.Resolve<AlbumRepository>();
|
_albumRepo = Mocker.Resolve<AlbumRepository>();
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace NzbDrone.Core.Test.MusicTests.ArtistRepositoryTests
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
_artistMetadataRepo = Mocker.Resolve<ArtistMetadataRepository>();
|
_artistMetadataRepo = Mocker.Resolve<ArtistMetadataRepository>();
|
||||||
_metadataList = Builder<ArtistMetadata>.CreateListOfSize(10).BuildList();
|
_metadataList = Builder<ArtistMetadata>.CreateListOfSize(10).All().With(x => x.Id = 0).BuildList();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|
|
@ -4,9 +4,9 @@ using System.Reflection;
|
||||||
using AutoFixture;
|
using AutoFixture;
|
||||||
using Equ;
|
using Equ;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Marr.Data;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.Music;
|
using NzbDrone.Core.Music;
|
||||||
using NzbDrone.Test.Common;
|
using NzbDrone.Test.Common;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Linq;
|
||||||
|
using Dapper;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
using NzbDrone.Core.Music;
|
||||||
|
|
||||||
namespace NzbDrone.Core.ArtistStats
|
namespace NzbDrone.Core.ArtistStats
|
||||||
{
|
{
|
||||||
|
@ -13,6 +16,8 @@ namespace NzbDrone.Core.ArtistStats
|
||||||
|
|
||||||
public class ArtistStatisticsRepository : IArtistStatisticsRepository
|
public class ArtistStatisticsRepository : IArtistStatisticsRepository
|
||||||
{
|
{
|
||||||
|
private const string _selectTemplate = "SELECT /**select**/ FROM Tracks /**join**/ /**innerjoin**/ /**leftjoin**/ /**where**/ /**groupby**/ /**having**/ /**orderby**/";
|
||||||
|
|
||||||
private readonly IMainDatabase _database;
|
private readonly IMainDatabase _database;
|
||||||
|
|
||||||
public ArtistStatisticsRepository(IMainDatabase database)
|
public ArtistStatisticsRepository(IMainDatabase database)
|
||||||
|
@ -22,57 +27,41 @@ namespace NzbDrone.Core.ArtistStats
|
||||||
|
|
||||||
public List<AlbumStatistics> ArtistStatistics()
|
public List<AlbumStatistics> ArtistStatistics()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
var time = DateTime.UtcNow;
|
||||||
|
return Query(Builder().Where<Album>(x => x.ReleaseDate < time));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<AlbumStatistics> ArtistStatistics(int artistId)
|
public List<AlbumStatistics> ArtistStatistics(int artistId)
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
var time = DateTime.UtcNow;
|
||||||
|
return Query(Builder().Where<Album>(x => x.ReleaseDate < time)
|
||||||
mapper.AddParameter("currentDate", DateTime.UtcNow);
|
.Where<Artist>(x => x.Id == artistId));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetSelectClause()
|
private List<AlbumStatistics> Query(SqlBuilder builder)
|
||||||
{
|
{
|
||||||
return @"SELECT
|
var sql = builder.AddTemplate(_selectTemplate).LogQuery();
|
||||||
Artists.Id AS ArtistId,
|
|
||||||
|
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,
|
Albums.Id AS AlbumId,
|
||||||
SUM(COALESCE(TrackFiles.Size, 0)) AS SizeOnDisk,
|
SUM(COALESCE(TrackFiles.Size, 0)) AS SizeOnDisk,
|
||||||
COUNT(Tracks.Id) AS TotalTrackCount,
|
COUNT(Tracks.Id) AS TotalTrackCount,
|
||||||
SUM(CASE WHEN Tracks.TrackFileId > 0 THEN 1 ELSE 0 END) AS AvailableTrackCount,
|
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 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
|
SUM(CASE WHEN TrackFiles.Id IS NULL THEN 0 ELSE 1 END) AS TrackFileCount")
|
||||||
FROM Tracks
|
.Join<Track, AlbumRelease>((t, r) => t.AlbumReleaseId == r.Id)
|
||||||
JOIN AlbumReleases ON Tracks.AlbumReleaseId = AlbumReleases.Id
|
.Join<AlbumRelease, Album>((r, a) => r.AlbumId == a.Id)
|
||||||
JOIN Albums ON AlbumReleases.AlbumId = Albums.Id
|
.Join<Album, Artist>((album, artist) => album.ArtistMetadataId == artist.ArtistMetadataId)
|
||||||
JOIN Artists on Albums.ArtistMetadataId = Artists.ArtistMetadataId
|
.LeftJoin<Track, TrackFile>((t, f) => t.TrackFileId == f.Id)
|
||||||
LEFT OUTER JOIN TrackFiles ON Tracks.TrackFileId = TrackFiles.Id
|
.Where<AlbumRelease>(x => x.Monitored == true)
|
||||||
WHERE AlbumReleases.Monitored = 1";
|
.GroupBy<Artist>(x => x.Id)
|
||||||
}
|
.GroupBy<Album>(x => x.Id);
|
||||||
|
|
||||||
private string GetGroupByClause()
|
|
||||||
{
|
|
||||||
return "GROUP BY Artists.Id, Albums.Id";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,12 @@ namespace NzbDrone.Core.Authentication
|
||||||
|
|
||||||
public User FindUser(string username)
|
public User FindUser(string username)
|
||||||
{
|
{
|
||||||
return Query.Where(u => u.Username == username).SingleOrDefault();
|
return Query(x => x.Username == username).SingleOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
public User FindUser(Guid identifier)
|
public User FindUser(Guid identifier)
|
||||||
{
|
{
|
||||||
return Query.Where(u => u.Identifier == identifier).SingleOrDefault();
|
return Query(x => x.Identifier == identifier).SingleOrDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,12 @@ namespace NzbDrone.Core.Backup
|
||||||
|
|
||||||
public void BackupDatabase(IDatabase database, string targetDirectory)
|
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);
|
var backupConnectionStringBuilder = new SQLiteConnectionStringBuilder(sourceConnectionString);
|
||||||
|
|
||||||
backupConnectionStringBuilder.DataSource = Path.Combine(targetDirectory, Path.GetFileName(backupConnectionStringBuilder.DataSource));
|
backupConnectionStringBuilder.DataSource = Path.Combine(targetDirectory, Path.GetFileName(backupConnectionStringBuilder.DataSource));
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Marr.Data.QGen;
|
using Dapper;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Core.Music;
|
using NzbDrone.Core.Music;
|
||||||
|
@ -22,26 +22,24 @@ namespace NzbDrone.Core.Blacklisting
|
||||||
|
|
||||||
public List<Blacklist> BlacklistedByTitle(int artistId, string sourceTitle)
|
public List<Blacklist> BlacklistedByTitle(int artistId, string sourceTitle)
|
||||||
{
|
{
|
||||||
return Query.Where(e => e.ArtistId == artistId)
|
return Query(e => e.ArtistId == artistId && e.SourceTitle.Contains(sourceTitle));
|
||||||
.AndWhere(e => e.SourceTitle.Contains(sourceTitle));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Blacklist> BlacklistedByTorrentInfoHash(int artistId, string torrentInfoHash)
|
public List<Blacklist> BlacklistedByTorrentInfoHash(int artistId, string torrentInfoHash)
|
||||||
{
|
{
|
||||||
return Query.Where(e => e.ArtistId == artistId)
|
return Query(e => e.ArtistId == artistId && e.TorrentInfoHash.Contains(torrentInfoHash));
|
||||||
.AndWhere(e => e.TorrentInfoHash.Contains(torrentInfoHash));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Blacklist> BlacklistedByArtist(int artistId)
|
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)
|
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) =>
|
||||||
var baseQuery = query.Join<Blacklist, Artist>(JoinType.Inner, h => h.Artist, (h, s) => h.ArtistId == s.Id);
|
{
|
||||||
|
bl.Artist = artist;
|
||||||
return base.GetPagedQuery(baseQuery, pagingSpec);
|
return bl;
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ namespace NzbDrone.Core.Configuration
|
||||||
bool AnalyticsEnabled { get; }
|
bool AnalyticsEnabled { get; }
|
||||||
string LogLevel { get; }
|
string LogLevel { get; }
|
||||||
string ConsoleLogLevel { get; }
|
string ConsoleLogLevel { get; }
|
||||||
|
bool LogSql { get; }
|
||||||
|
int LogRotate { get; }
|
||||||
bool FilterSentryEvents { get; }
|
bool FilterSentryEvents { get; }
|
||||||
string Branch { get; }
|
string Branch { get; }
|
||||||
string ApiKey { get; }
|
string ApiKey { get; }
|
||||||
|
@ -177,6 +179,8 @@ namespace NzbDrone.Core.Configuration
|
||||||
|
|
||||||
public string LogLevel => GetValue("LogLevel", "info");
|
public string LogLevel => GetValue("LogLevel", "info");
|
||||||
public string ConsoleLogLevel => GetValue("ConsoleLogLevel", string.Empty, persist: false);
|
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 bool FilterSentryEvents => GetValueBoolean("FilterSentryEvents", true, persist: false);
|
||||||
public string SslCertPath => GetValue("SslCertPath", "");
|
public string SslCertPath => GetValue("SslCertPath", "");
|
||||||
public string SslCertPassword => GetValue("SslCertPassword", "");
|
public string SslCertPassword => GetValue("SslCertPassword", "");
|
||||||
|
@ -204,9 +208,9 @@ namespace NzbDrone.Core.Configuration
|
||||||
|
|
||||||
public string UpdateScriptPath => GetValue("UpdateScriptPath", "", false);
|
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)
|
public bool GetValueBoolean(string key, bool defaultValue, bool persist = true)
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace NzbDrone.Core.Configuration
|
||||||
|
|
||||||
public Config Get(string key)
|
public Config Get(string key)
|
||||||
{
|
{
|
||||||
return Query.Where(c => c.Key == key).SingleOrDefault();
|
return Query(c => c.Key == key).SingleOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Config Upsert(string key, string value)
|
public Config Upsert(string key, string value)
|
||||||
|
|
|
@ -3,10 +3,10 @@ using System.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using Marr.Data;
|
using System.Reflection;
|
||||||
using Marr.Data.QGen;
|
using System.Text;
|
||||||
|
using Dapper;
|
||||||
using NzbDrone.Core.Datastore.Events;
|
using NzbDrone.Core.Datastore.Events;
|
||||||
using NzbDrone.Core.Datastore.Extensions;
|
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore
|
namespace NzbDrone.Core.Datastore
|
||||||
|
@ -17,59 +17,79 @@ namespace NzbDrone.Core.Datastore
|
||||||
IEnumerable<TModel> All();
|
IEnumerable<TModel> All();
|
||||||
int Count();
|
int Count();
|
||||||
TModel Get(int id);
|
TModel Get(int id);
|
||||||
IEnumerable<TModel> Get(IEnumerable<int> ids);
|
|
||||||
TModel SingleOrDefault();
|
|
||||||
TModel Insert(TModel model);
|
TModel Insert(TModel model);
|
||||||
TModel Update(TModel model);
|
TModel Update(TModel model);
|
||||||
TModel Upsert(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(TModel model);
|
||||||
|
void Delete(int id);
|
||||||
|
IEnumerable<TModel> Get(IEnumerable<int> ids);
|
||||||
void InsertMany(IList<TModel> model);
|
void InsertMany(IList<TModel> model);
|
||||||
void UpdateMany(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(List<TModel> model);
|
||||||
|
void DeleteMany(IEnumerable<int> ids);
|
||||||
void Purge(bool vacuum = false);
|
void Purge(bool vacuum = false);
|
||||||
bool HasItems();
|
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 Single();
|
||||||
|
TModel SingleOrDefault();
|
||||||
PagingSpec<TModel> GetPaged(PagingSpec<TModel> pagingSpec);
|
PagingSpec<TModel> GetPaged(PagingSpec<TModel> pagingSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BasicRepository<TModel> : IBasicRepository<TModel>
|
public class BasicRepository<TModel> : IBasicRepository<TModel>
|
||||||
where TModel : ModelBase, new()
|
where TModel : ModelBase, new()
|
||||||
{
|
{
|
||||||
private readonly IDatabase _database;
|
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
|
private readonly PropertyInfo _keyProperty;
|
||||||
|
private readonly List<PropertyInfo> _properties;
|
||||||
|
private readonly string _updateSql;
|
||||||
|
private readonly string _insertSql;
|
||||||
|
|
||||||
protected IDataMapper DataMapper => _database.GetDataMapper();
|
protected readonly IDatabase _database;
|
||||||
|
protected readonly string _table;
|
||||||
|
|
||||||
public BasicRepository(IDatabase database, IEventAggregator eventAggregator)
|
public BasicRepository(IDatabase database, IEventAggregator eventAggregator)
|
||||||
{
|
{
|
||||||
_database = database;
|
_database = database;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
|
|
||||||
|
var type = typeof(TModel);
|
||||||
|
|
||||||
|
_table = TableMapping.Mapper.TableNameMapping(type);
|
||||||
|
_keyProperty = type.GetProperty(nameof(ModelBase.Id));
|
||||||
|
|
||||||
|
var excluded = TableMapping.Mapper.ExcludeProperties(type).Select(x => x.Name).ToList();
|
||||||
|
excluded.Add(_keyProperty.Name);
|
||||||
|
_properties = type.GetProperties().Where(x => 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)
|
protected virtual List<TModel> Query(SqlBuilder builder) => _database.Query<TModel>(builder).ToList();
|
||||||
{
|
|
||||||
DataMapper.Delete(filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<TModel> All()
|
protected List<TModel> Query(Expression<Func<TModel, bool>> where) => Query(Builder().Where(where));
|
||||||
{
|
|
||||||
return Query.ToList();
|
protected virtual List<TModel> QueryDistinct(SqlBuilder builder) => _database.QueryDistinct<TModel>(builder).ToList();
|
||||||
}
|
|
||||||
|
|
||||||
public int Count()
|
public int Count()
|
||||||
{
|
{
|
||||||
return DataMapper.Query<TModel>().GetRowCount();
|
using (var conn = _database.OpenConnection())
|
||||||
|
{
|
||||||
|
return conn.ExecuteScalar<int>($"SELECT COUNT(*) FROM {_table}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual IEnumerable<TModel> All()
|
||||||
|
{
|
||||||
|
return Query(Builder());
|
||||||
}
|
}
|
||||||
|
|
||||||
public TModel Get(int id)
|
public TModel Get(int id)
|
||||||
{
|
{
|
||||||
var model = Query.Where(c => c.Id == id).SingleOrDefault();
|
var model = Query(x => x.Id == id).FirstOrDefault();
|
||||||
|
|
||||||
if (model == null)
|
if (model == null)
|
||||||
{
|
{
|
||||||
|
@ -81,13 +101,16 @@ namespace NzbDrone.Core.Datastore
|
||||||
|
|
||||||
public IEnumerable<TModel> Get(IEnumerable<int> ids)
|
public IEnumerable<TModel> Get(IEnumerable<int> ids)
|
||||||
{
|
{
|
||||||
var idList = ids.ToList();
|
if (!ids.Any())
|
||||||
var query = string.Format("[t0].[Id] IN ({0})", string.Join(",", idList));
|
|
||||||
var result = Query.Where(query).ToList();
|
|
||||||
|
|
||||||
if (result.Count != idList.Count())
|
|
||||||
{
|
{
|
||||||
throw new ApplicationException($"Expected query to return {idList.Count} rows but returned {result.Count}");
|
return new List<TModel>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = Query(x => ids.Contains(x.Id));
|
||||||
|
|
||||||
|
if (result.Count != ids.Count())
|
||||||
|
{
|
||||||
|
throw new ApplicationException($"Expected query to return {ids.Count()} rows but returned {result.Count}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -110,13 +133,74 @@ namespace NzbDrone.Core.Datastore
|
||||||
throw new InvalidOperationException("Can't insert model with existing ID " + model.Id);
|
throw new InvalidOperationException("Can't insert model with existing ID " + model.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
DataMapper.Insert(model);
|
using (var conn = _database.OpenConnection())
|
||||||
|
{
|
||||||
|
model = Insert(conn, null, model);
|
||||||
|
}
|
||||||
|
|
||||||
ModelCreated(model);
|
ModelCreated(model);
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetInsertSql()
|
||||||
|
{
|
||||||
|
var sbColumnList = new StringBuilder(null);
|
||||||
|
for (var i = 0; i < _properties.Count; i++)
|
||||||
|
{
|
||||||
|
var property = _properties[i];
|
||||||
|
sbColumnList.AppendFormat("\"{0}\"", property.Name);
|
||||||
|
if (i < _properties.Count - 1)
|
||||||
|
{
|
||||||
|
sbColumnList.Append(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sbParameterList = new StringBuilder(null);
|
||||||
|
for (var i = 0; i < _properties.Count; i++)
|
||||||
|
{
|
||||||
|
var property = _properties[i];
|
||||||
|
sbParameterList.AppendFormat("@{0}", property.Name);
|
||||||
|
if (i < _properties.Count - 1)
|
||||||
|
{
|
||||||
|
sbParameterList.Append(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"INSERT INTO {_table} ({sbColumnList.ToString()}) VALUES ({sbParameterList.ToString()}); SELECT last_insert_rowid() id";
|
||||||
|
}
|
||||||
|
|
||||||
|
private TModel Insert(IDbConnection connection, IDbTransaction transaction, TModel model)
|
||||||
|
{
|
||||||
|
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)
|
public TModel Update(TModel model)
|
||||||
{
|
{
|
||||||
if (model.Id == 0)
|
if (model.Id == 0)
|
||||||
|
@ -124,52 +208,59 @@ namespace NzbDrone.Core.Datastore
|
||||||
throw new InvalidOperationException("Can't update model with ID 0");
|
throw new InvalidOperationException("Can't update model with ID 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
DataMapper.Update(model, c => c.Id == model.Id);
|
using (var conn = _database.OpenConnection())
|
||||||
|
{
|
||||||
|
UpdateFields(conn, null, model, _properties);
|
||||||
|
}
|
||||||
|
|
||||||
ModelUpdated(model);
|
ModelUpdated(model);
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void 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)
|
public void Delete(TModel model)
|
||||||
{
|
{
|
||||||
Delete(model.Id);
|
Delete(model.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InsertMany(IList<TModel> models)
|
public void Delete(int id)
|
||||||
{
|
{
|
||||||
using (var unitOfWork = new UnitOfWork(() => DataMapper))
|
Delete(x => x.Id == id);
|
||||||
{
|
|
||||||
unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted);
|
|
||||||
|
|
||||||
foreach (var model in models)
|
|
||||||
{
|
|
||||||
unitOfWork.DB.Insert(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
unitOfWork.Commit();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
Delete(x => ids.Contains(x.Id));
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,31 +281,13 @@ namespace NzbDrone.Core.Datastore
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Delete(int id)
|
|
||||||
{
|
|
||||||
DataMapper.Delete<TModel>(c => c.Id == id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DeleteMany(IEnumerable<int> ids)
|
|
||||||
{
|
|
||||||
using (var unitOfWork = new UnitOfWork(() => DataMapper))
|
|
||||||
{
|
|
||||||
unitOfWork.BeginTransaction(IsolationLevel.ReadCommitted);
|
|
||||||
|
|
||||||
foreach (var id in ids)
|
|
||||||
{
|
|
||||||
var localId = id;
|
|
||||||
|
|
||||||
unitOfWork.DB.Delete<TModel>(c => c.Id == localId);
|
|
||||||
}
|
|
||||||
|
|
||||||
unitOfWork.Commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Purge(bool vacuum = false)
|
public void Purge(bool vacuum = false)
|
||||||
{
|
{
|
||||||
DataMapper.Delete<TModel>(c => c.Id > -1);
|
using (var conn = _database.OpenConnection())
|
||||||
|
{
|
||||||
|
conn.Execute($"DELETE FROM [{_table}]");
|
||||||
|
}
|
||||||
|
|
||||||
if (vacuum)
|
if (vacuum)
|
||||||
{
|
{
|
||||||
Vacuum();
|
Vacuum();
|
||||||
|
@ -235,67 +308,130 @@ namespace NzbDrone.Core.Datastore
|
||||||
{
|
{
|
||||||
if (model.Id == 0)
|
if (model.Id == 0)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Attempted to updated model without ID");
|
throw new InvalidOperationException("Attempted to update model without ID");
|
||||||
}
|
}
|
||||||
|
|
||||||
DataMapper.Update<TModel>()
|
var propertiesToUpdate = properties.Select(x => x.GetMemberName()).ToList();
|
||||||
.Where(c => c.Id == model.Id)
|
|
||||||
.ColumnsIncluding(properties)
|
using (var conn = _database.OpenConnection())
|
||||||
.Entity(model)
|
{
|
||||||
.Execute();
|
UpdateFields(conn, null, model, propertiesToUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
ModelUpdated(model);
|
ModelUpdated(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetFields(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)
|
var propertiesToUpdate = properties.Select(x => x.GetMemberName()).ToList();
|
||||||
{
|
|
||||||
if (model.Id == 0)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Can't update model with ID 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
unitOfWork.DB.Update<TModel>()
|
using (var conn = _database.OpenConnection())
|
||||||
.Where(c => c.Id == model.Id)
|
{
|
||||||
.ColumnsIncluding(properties)
|
UpdateFields(conn, null, models, propertiesToUpdate);
|
||||||
.Entity(model)
|
}
|
||||||
.Execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
public virtual PagingSpec<TModel> GetPaged(PagingSpec<TModel> pagingSpec)
|
||||||
{
|
{
|
||||||
pagingSpec.Records = GetPagedQuery(Query, pagingSpec).ToList();
|
pagingSpec.Records = GetPagedRecords(PagedBuilder(), pagingSpec, PagedQuery);
|
||||||
pagingSpec.TotalRecords = GetPagedQuery(Query, pagingSpec).GetRowCount();
|
pagingSpec.TotalRecords = GetPagedRecordCount(PagedBuilder().SelectCount(), pagingSpec);
|
||||||
|
|
||||||
return pagingSpec;
|
return pagingSpec;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual SortBuilder<TModel> GetPagedQuery(QueryBuilder<TModel> query, PagingSpec<TModel> pagingSpec)
|
private void AddFilters(SqlBuilder builder, PagingSpec<TModel> pagingSpec)
|
||||||
{
|
{
|
||||||
var filterExpressions = pagingSpec.FilterExpressions;
|
var filters = pagingSpec.FilterExpressions;
|
||||||
var sortQuery = query.Where(filterExpressions.FirstOrDefault());
|
|
||||||
|
|
||||||
if (filterExpressions.Count > 1)
|
foreach (var filter in filters)
|
||||||
{
|
{
|
||||||
// Start at the second item for the AndWhere clauses
|
builder.Where<TModel>(filter);
|
||||||
for (var i = 1; i < filterExpressions.Count; i++)
|
}
|
||||||
{
|
}
|
||||||
sortQuery.AndWhere(filterExpressions[i]);
|
|
||||||
}
|
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())
|
using (var conn = _database.OpenConnection())
|
||||||
.Skip(pagingSpec.PagingOffset())
|
{
|
||||||
.Take(pagingSpec.PageSize);
|
return conn.ExecuteScalar<int>(sql.RawSql, sql.Parameters);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void ModelCreated(TModel model)
|
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 System.Data;
|
||||||
using Marr.Data.Converters;
|
using System.Text.Json;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Common.Reflection;
|
using NzbDrone.Common.Reflection;
|
||||||
using NzbDrone.Common.Serializer;
|
|
||||||
using NzbDrone.Core.Messaging.Commands;
|
using NzbDrone.Core.Messaging.Commands;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore.Converters
|
namespace NzbDrone.Core.Datastore.Converters
|
||||||
{
|
{
|
||||||
public class CommandConverter : EmbeddedDocumentConverter
|
public class CommandConverter : EmbeddedDocumentConverter<Command>
|
||||||
{
|
{
|
||||||
public override object FromDB(ConverterContext context)
|
public override Command Parse(object value)
|
||||||
{
|
{
|
||||||
if (context.DbValue == DBNull.Value)
|
var stringValue = (string)value;
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var stringValue = (string)context.DbValue;
|
|
||||||
|
|
||||||
if (stringValue.IsNullOrWhiteSpace())
|
if (stringValue.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ordinal = context.DataRecord.GetOrdinal("Name");
|
string contract;
|
||||||
var contract = context.DataRecord.GetString(ordinal);
|
using (JsonDocument body = JsonDocument.Parse(stringValue))
|
||||||
|
{
|
||||||
|
contract = body.RootElement.GetProperty("name").GetString();
|
||||||
|
}
|
||||||
|
|
||||||
var impType = typeof(Command).Assembly.FindTypeByName(contract + "Command");
|
var impType = typeof(Command).Assembly.FindTypeByName(contract + "Command");
|
||||||
|
|
||||||
if (impType == null)
|
if (impType == null)
|
||||||
{
|
{
|
||||||
var result = Json.Deserialize<UnknownCommand>(stringValue);
|
var result = JsonSerializer.Deserialize<UnknownCommand>(stringValue, SerializerSettings);
|
||||||
|
|
||||||
result.ContractName = contract;
|
result.ContractName = contract;
|
||||||
|
|
||||||
return result;
|
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 System.Data;
|
||||||
using Marr.Data.Converters;
|
using System.Text.Json;
|
||||||
using Marr.Data.Mapping;
|
using System.Text.Json.Serialization;
|
||||||
using Newtonsoft.Json;
|
using Dapper;
|
||||||
using Newtonsoft.Json.Converters;
|
|
||||||
using Newtonsoft.Json.Serialization;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore.Converters
|
namespace NzbDrone.Core.Datastore.Converters
|
||||||
{
|
{
|
||||||
public class EmbeddedDocumentConverter : IConverter
|
public class EmbeddedDocumentConverter<T> : SqlMapper.TypeHandler<T>
|
||||||
{
|
{
|
||||||
private readonly JsonSerializerSettings _serializerSetting;
|
protected readonly JsonSerializerOptions SerializerSettings;
|
||||||
|
|
||||||
public EmbeddedDocumentConverter(params JsonConverter[] converters)
|
public EmbeddedDocumentConverter()
|
||||||
{
|
{
|
||||||
_serializerSetting = new JsonSerializerSettings
|
var serializerSettings = new JsonSerializerOptions
|
||||||
{
|
{
|
||||||
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
|
AllowTrailingCommas = true,
|
||||||
NullValueHandling = NullValueHandling.Ignore,
|
IgnoreNullValues = true,
|
||||||
Formatting = Formatting.Indented,
|
PropertyNameCaseInsensitive = true,
|
||||||
DefaultValueHandling = DefaultValueHandling.Include,
|
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
|
||||||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
|
WriteIndented = true
|
||||||
};
|
};
|
||||||
|
|
||||||
_serializerSetting.Converters.Add(new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy() });
|
serializerSettings.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, true));
|
||||||
_serializerSetting.Converters.Add(new VersionConverter());
|
serializerSettings.Converters.Add(new TimeSpanConverter());
|
||||||
|
serializerSettings.Converters.Add(new UtcConverter());
|
||||||
|
|
||||||
|
SerializerSettings = serializerSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmbeddedDocumentConverter(params JsonConverter[] converters)
|
||||||
|
: this()
|
||||||
|
{
|
||||||
foreach (var converter in converters)
|
foreach (var converter in converters)
|
||||||
{
|
{
|
||||||
_serializerSetting.Converters.Add(converter);
|
SerializerSettings.Converters.Add(converter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual object FromDB(ConverterContext context)
|
public override void SetValue(IDbDataParameter parameter, T value)
|
||||||
{
|
{
|
||||||
if (context.DbValue == DBNull.Value)
|
// Cast to object to get all properties written out
|
||||||
{
|
// https://github.com/dotnet/corefx/issues/38650
|
||||||
return DBNull.Value;
|
parameter.Value = JsonSerializer.Serialize((object)value, SerializerSettings);
|
||||||
}
|
|
||||||
|
|
||||||
var stringValue = (string)context.DbValue;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(stringValue))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return JsonConvert.DeserializeObject(stringValue, context.ColumnMap.FieldType, _serializerSetting);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public object FromDB(ColumnMap map, object dbValue)
|
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 System;
|
||||||
using Marr.Data.Converters;
|
using System.Data;
|
||||||
using Marr.Data.Mapping;
|
using Dapper;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore.Converters
|
namespace NzbDrone.Core.Datastore.Converters
|
||||||
{
|
{
|
||||||
public class GuidConverter : IConverter
|
public class GuidConverter : SqlMapper.TypeHandler<Guid>
|
||||||
{
|
{
|
||||||
public object FromDB(ConverterContext context)
|
public override Guid Parse(object value)
|
||||||
{
|
{
|
||||||
if (context.DbValue == DBNull.Value)
|
if (value == null)
|
||||||
{
|
{
|
||||||
return Guid.Empty;
|
return Guid.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
var value = (string)context.DbValue;
|
return new Guid((string)value);
|
||||||
|
|
||||||
return new Guid(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public object FromDB(ColumnMap map, object dbValue)
|
public override void SetValue(IDbDataParameter parameter, Guid value)
|
||||||
{
|
{
|
||||||
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
|
parameter.Value = value.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public object ToDB(object clrValue)
|
|
||||||
{
|
|
||||||
if (clrValue == null)
|
|
||||||
{
|
|
||||||
return DBNull.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
var value = clrValue;
|
|
||||||
|
|
||||||
return value.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Type DbType => typeof(string);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 System;
|
||||||
using Marr.Data.Converters;
|
using System.Data;
|
||||||
using Marr.Data.Mapping;
|
using Dapper;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore.Converters
|
namespace NzbDrone.Core.Datastore.Converters
|
||||||
{
|
{
|
||||||
public class OsPathConverter : IConverter
|
public class OsPathConverter : SqlMapper.TypeHandler<OsPath>
|
||||||
{
|
{
|
||||||
public object FromDB(ConverterContext context)
|
public override void SetValue(IDbDataParameter parameter, OsPath value)
|
||||||
{
|
{
|
||||||
if (context.DbValue == DBNull.Value)
|
parameter.Value = value.FullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override OsPath Parse(object value)
|
||||||
|
{
|
||||||
|
if (value == null || value is DBNull)
|
||||||
{
|
{
|
||||||
return DBNull.Value;
|
return new OsPath(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
var value = (string)context.DbValue;
|
return new OsPath((string)value);
|
||||||
|
|
||||||
return new OsPath(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public object FromDB(ColumnMap map, object dbValue)
|
|
||||||
{
|
|
||||||
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
|
|
||||||
}
|
|
||||||
|
|
||||||
public object ToDB(object clrValue)
|
|
||||||
{
|
|
||||||
var value = (OsPath)clrValue;
|
|
||||||
|
|
||||||
return value.FullPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Type DbType => typeof(string);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,62 +1,21 @@
|
||||||
using System;
|
using System;
|
||||||
using Marr.Data.Converters;
|
using System.Text.Json;
|
||||||
using Marr.Data.Mapping;
|
using System.Text.Json.Serialization;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using NzbDrone.Core.Music;
|
using NzbDrone.Core.Music;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore.Converters
|
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)
|
var item = reader.GetInt32();
|
||||||
{
|
return (PrimaryAlbumType)item;
|
||||||
return PrimaryAlbumType.Album;
|
|
||||||
}
|
|
||||||
|
|
||||||
var val = Convert.ToInt32(context.DbValue);
|
|
||||||
|
|
||||||
return (PrimaryAlbumType)val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 });
|
writer.WriteNumberValue((int)value);
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +1,22 @@
|
||||||
using System;
|
using System.Data;
|
||||||
using Marr.Data.Converters;
|
using System.Text.Json;
|
||||||
using NzbDrone.Common.Reflection;
|
|
||||||
using NzbDrone.Common.Serializer;
|
|
||||||
using NzbDrone.Core.ThingiProvider;
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore.Converters
|
namespace NzbDrone.Core.Datastore.Converters
|
||||||
{
|
{
|
||||||
public class ProviderSettingConverter : EmbeddedDocumentConverter
|
public class ProviderSettingConverter : EmbeddedDocumentConverter<IProviderConfig>
|
||||||
{
|
{
|
||||||
public override object FromDB(ConverterContext context)
|
public override IProviderConfig Parse(object value)
|
||||||
{
|
{
|
||||||
if (context.DbValue == DBNull.Value)
|
// We can't deserialize based on another column, happens in ProviderRepository instead
|
||||||
{
|
return null;
|
||||||
return NullConfig.Instance;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var stringValue = (string)context.DbValue;
|
public override void SetValue(IDbDataParameter parameter, IProviderConfig value)
|
||||||
|
{
|
||||||
if (string.IsNullOrWhiteSpace(stringValue))
|
// Cast to object to get all properties written out
|
||||||
{
|
// https://github.com/dotnet/corefx/issues/38650
|
||||||
return NullConfig.Instance;
|
parameter.Value = JsonSerializer.Serialize((object)value, SerializerSettings);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,62 +1,36 @@
|
||||||
using System;
|
using System;
|
||||||
using Marr.Data.Converters;
|
using System.Data;
|
||||||
using Marr.Data.Mapping;
|
using System.Text.Json;
|
||||||
using Newtonsoft.Json;
|
using System.Text.Json.Serialization;
|
||||||
|
using Dapper;
|
||||||
using NzbDrone.Core.Qualities;
|
using NzbDrone.Core.Qualities;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore.Converters
|
namespace NzbDrone.Core.Datastore.Converters
|
||||||
{
|
{
|
||||||
public class QualityIntConverter : JsonConverter, IConverter
|
public class QualityIntConverter : JsonConverter<Quality>
|
||||||
{
|
{
|
||||||
public object FromDB(ConverterContext context)
|
public override Quality Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
if (context.DbValue == DBNull.Value)
|
var item = reader.GetInt32();
|
||||||
{
|
return (Quality)item;
|
||||||
return Quality.Unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
var val = Convert.ToInt32(context.DbValue);
|
|
||||||
|
|
||||||
return (Quality)val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (Quality)Convert.ToInt32(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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,62 +1,21 @@
|
||||||
using System;
|
using System;
|
||||||
using Marr.Data.Converters;
|
using System.Text.Json;
|
||||||
using Marr.Data.Mapping;
|
using System.Text.Json.Serialization;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using NzbDrone.Core.Music;
|
using NzbDrone.Core.Music;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore.Converters
|
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)
|
var item = reader.GetInt32();
|
||||||
{
|
return (ReleaseStatus)item;
|
||||||
return ReleaseStatus.Official;
|
|
||||||
}
|
|
||||||
|
|
||||||
var val = Convert.ToInt32(context.DbValue);
|
|
||||||
|
|
||||||
return (ReleaseStatus)val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 });
|
writer.WriteNumberValue((int)value);
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,65 +1,21 @@
|
||||||
using System;
|
using System;
|
||||||
using Marr.Data.Converters;
|
using System.Text.Json;
|
||||||
using Marr.Data.Mapping;
|
using System.Text.Json.Serialization;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using NzbDrone.Core.Music;
|
using NzbDrone.Core.Music;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore.Converters
|
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)
|
var item = reader.GetInt32();
|
||||||
{
|
return (SecondaryAlbumType)item;
|
||||||
return SecondaryAlbumType.Studio;
|
|
||||||
}
|
|
||||||
|
|
||||||
var val = Convert.ToInt32(context.DbValue);
|
|
||||||
|
|
||||||
return (SecondaryAlbumType)val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 });
|
writer.WriteNumberValue((int)value);
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +1,19 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Text.Json;
|
||||||
using Marr.Data.Converters;
|
using System.Text.Json.Serialization;
|
||||||
using Marr.Data.Mapping;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore.Converters
|
namespace NzbDrone.Core.Datastore.Converters
|
||||||
{
|
{
|
||||||
public class TimeSpanConverter : IConverter
|
public class TimeSpanConverter : JsonConverter<TimeSpan>
|
||||||
{
|
{
|
||||||
public object FromDB(ConverterContext context)
|
public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
if (context.DbValue == DBNull.Value)
|
return TimeSpan.Parse(reader.GetString());
|
||||||
{
|
|
||||||
return TimeSpan.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.DbValue is TimeSpan)
|
|
||||||
{
|
|
||||||
return context.DbValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TimeSpan.Parse(context.DbValue.ToString(), CultureInfo.InvariantCulture);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 System;
|
||||||
using Marr.Data.Converters;
|
using System.Data;
|
||||||
using Marr.Data.Mapping;
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Dapper;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore.Converters
|
namespace NzbDrone.Core.Datastore.Converters
|
||||||
{
|
{
|
||||||
public class UtcConverter : IConverter
|
public class DapperUtcConverter : SqlMapper.TypeHandler<DateTime>
|
||||||
{
|
{
|
||||||
public object FromDB(ConverterContext context)
|
public override void SetValue(IDbDataParameter parameter, DateTime value)
|
||||||
{
|
{
|
||||||
return context.DbValue;
|
parameter.Value = value.ToUniversalTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
public object FromDB(ColumnMap map, object dbValue)
|
public override DateTime Parse(object value)
|
||||||
{
|
{
|
||||||
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
|
return DateTime.SpecifyKind((DateTime)value, DateTimeKind.Utc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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)
|
writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"));
|
||||||
{
|
|
||||||
return clrValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var dateTime = (DateTime)clrValue;
|
|
||||||
return dateTime.ToUniversalTime();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Type DbType => typeof(DateTime);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using Marr.Data;
|
using System.Data;
|
||||||
|
using Dapper;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Instrumentation;
|
using NzbDrone.Common.Instrumentation;
|
||||||
|
|
||||||
|
@ -7,7 +8,7 @@ namespace NzbDrone.Core.Datastore
|
||||||
{
|
{
|
||||||
public interface IDatabase
|
public interface IDatabase
|
||||||
{
|
{
|
||||||
IDataMapper GetDataMapper();
|
IDbConnection OpenConnection();
|
||||||
Version Version { get; }
|
Version Version { get; }
|
||||||
int Migration { get; }
|
int Migration { get; }
|
||||||
void Vacuum();
|
void Vacuum();
|
||||||
|
@ -16,17 +17,17 @@ namespace NzbDrone.Core.Datastore
|
||||||
public class Database : IDatabase
|
public class Database : IDatabase
|
||||||
{
|
{
|
||||||
private readonly string _databaseName;
|
private readonly string _databaseName;
|
||||||
private readonly Func<IDataMapper> _datamapperFactory;
|
private readonly Func<IDbConnection> _datamapperFactory;
|
||||||
|
|
||||||
private readonly Logger _logger = NzbDroneLogger.GetLogger(typeof(Database));
|
private readonly Logger _logger = NzbDroneLogger.GetLogger(typeof(Database));
|
||||||
|
|
||||||
public Database(string databaseName, Func<IDataMapper> datamapperFactory)
|
public Database(string databaseName, Func<IDbConnection> datamapperFactory)
|
||||||
{
|
{
|
||||||
_databaseName = databaseName;
|
_databaseName = databaseName;
|
||||||
_datamapperFactory = datamapperFactory;
|
_datamapperFactory = datamapperFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDataMapper GetDataMapper()
|
public IDbConnection OpenConnection()
|
||||||
{
|
{
|
||||||
return _datamapperFactory();
|
return _datamapperFactory();
|
||||||
}
|
}
|
||||||
|
@ -35,8 +36,11 @@ namespace NzbDrone.Core.Datastore
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var version = _datamapperFactory().ExecuteScalar("SELECT sqlite_version()").ToString();
|
using (var db = _datamapperFactory())
|
||||||
return new Version(version);
|
{
|
||||||
|
var version = db.QueryFirstOrDefault<string>("SELECT sqlite_version()");
|
||||||
|
return new Version(version);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,9 +48,10 @@ namespace NzbDrone.Core.Datastore
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var migration = _datamapperFactory()
|
using (var db = _datamapperFactory())
|
||||||
.ExecuteScalar("SELECT version from VersionInfo ORDER BY version DESC LIMIT 1").ToString();
|
{
|
||||||
return Convert.ToInt32(migration);
|
return db.QueryFirstOrDefault<int>("SELECT version from VersionInfo ORDER BY version DESC LIMIT 1");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +60,11 @@ namespace NzbDrone.Core.Datastore
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_logger.Info("Vacuuming {0} database", _databaseName);
|
_logger.Info("Vacuuming {0} database", _databaseName);
|
||||||
_datamapperFactory().ExecuteNonQuery("Vacuum;");
|
using (var db = _datamapperFactory())
|
||||||
|
{
|
||||||
|
db.Execute("Vacuum;");
|
||||||
|
}
|
||||||
|
|
||||||
_logger.Info("{0} database compressed", _databaseName);
|
_logger.Info("{0} database compressed", _databaseName);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Data.SQLite;
|
using System.Data.SQLite;
|
||||||
using Marr.Data;
|
|
||||||
using Marr.Data.Reflection;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Composition;
|
using NzbDrone.Common.Composition;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
|
@ -30,7 +28,6 @@ namespace NzbDrone.Core.Datastore
|
||||||
{
|
{
|
||||||
InitializeEnvironment();
|
InitializeEnvironment();
|
||||||
|
|
||||||
MapRepository.Instance.ReflectionStrategy = new SimpleReflectionStrategy();
|
|
||||||
TableMapping.Map();
|
TableMapping.Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,12 +96,11 @@ namespace NzbDrone.Core.Datastore
|
||||||
|
|
||||||
var db = new Database(migrationContext.MigrationType.ToString(), () =>
|
var db = new Database(migrationContext.MigrationType.ToString(), () =>
|
||||||
{
|
{
|
||||||
var dataMapper = new DataMapper(SQLiteFactory.Instance, connectionString)
|
var conn = SQLiteFactory.Instance.CreateConnection();
|
||||||
{
|
conn.ConnectionString = connectionString;
|
||||||
SqlMode = SqlModes.Text,
|
conn.Open();
|
||||||
};
|
|
||||||
|
|
||||||
return dataMapper;
|
return conn;
|
||||||
});
|
});
|
||||||
|
|
||||||
return db;
|
return db;
|
||||||
|
@ -123,7 +119,7 @@ namespace NzbDrone.Core.Datastore
|
||||||
|
|
||||||
if (OsInfo.IsOsx)
|
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);
|
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 System;
|
||||||
using Marr.Data;
|
using System.Linq.Expressions;
|
||||||
using Marr.Data.Mapping;
|
using System.Reflection;
|
||||||
|
using Dapper;
|
||||||
using NzbDrone.Common.Reflection;
|
using NzbDrone.Common.Reflection;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore.Extensions
|
namespace NzbDrone.Core.Datastore
|
||||||
{
|
{
|
||||||
public static class MappingExtensions
|
public static class MappingExtensions
|
||||||
{
|
{
|
||||||
public static ColumnMapBuilder<T> MapResultSet<T>(this FluentMappings.MappingsFluentEntity<T> mapBuilder)
|
public static PropertyInfo GetMemberName<T, TChild>(this Expression<Func<T, TChild>> member)
|
||||||
where T : ResultSet, new()
|
|
||||||
{
|
{
|
||||||
return mapBuilder
|
if (!(member.Body is MemberExpression memberExpression))
|
||||||
.Columns
|
{
|
||||||
.AutoMapPropertiesWhere(IsMappableProperty);
|
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)
|
public static bool IsMappableProperty(this MemberInfo memberInfo)
|
||||||
where T : ProviderDefinition, new()
|
|
||||||
{
|
|
||||||
return RegisterModel(mapBuilder, tableName).Ignore(c => c.ImplementationName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ColumnMapBuilder<T> RegisterModel<T>(this FluentMappings.MappingsFluentEntity<T> mapBuilder, string tableName = null)
|
|
||||||
where T : ModelBase, new()
|
|
||||||
{
|
|
||||||
return mapBuilder.Table.MapTable(tableName)
|
|
||||||
.Columns
|
|
||||||
.AutoMapPropertiesWhere(IsMappableProperty)
|
|
||||||
.PrefixAltNames(string.Format("{0}_", typeof(T).Name))
|
|
||||||
.For(c => c.Id)
|
|
||||||
.SetPrimaryKey()
|
|
||||||
.SetReturnValue()
|
|
||||||
.SetAutoIncrement();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RelationshipBuilder<T> AutoMapChildModels<T>(this ColumnMapBuilder<T> mapBuilder)
|
|
||||||
{
|
|
||||||
return mapBuilder.Relationships.AutoMapPropertiesWhere(m =>
|
|
||||||
m.MemberType == MemberTypes.Property &&
|
|
||||||
typeof(ModelBase).IsAssignableFrom(((PropertyInfo)m).PropertyType));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsMappableProperty(MemberInfo memberInfo)
|
|
||||||
{
|
{
|
||||||
var propertyInfo = memberInfo as PropertyInfo;
|
var propertyInfo = memberInfo as PropertyInfo;
|
||||||
|
|
||||||
|
@ -56,7 +32,11 @@ namespace NzbDrone.Core.Datastore.Extensions
|
||||||
return false;
|
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;
|
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 System;
|
||||||
using Marr.Data;
|
using System.Data;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore
|
namespace NzbDrone.Core.Datastore
|
||||||
{
|
{
|
||||||
|
@ -16,9 +16,9 @@ namespace NzbDrone.Core.Datastore
|
||||||
_database = database;
|
_database = database;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDataMapper GetDataMapper()
|
public IDbConnection OpenConnection()
|
||||||
{
|
{
|
||||||
return _database.GetDataMapper();
|
return _database.OpenConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Version Version => _database.Version;
|
public Version Version => _database.Version;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using Marr.Data;
|
using System.Data;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore
|
namespace NzbDrone.Core.Datastore
|
||||||
{
|
{
|
||||||
|
@ -16,9 +16,9 @@ namespace NzbDrone.Core.Datastore
|
||||||
_database = database;
|
_database = database;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDataMapper GetDataMapper()
|
public IDbConnection OpenConnection()
|
||||||
{
|
{
|
||||||
return _database.GetDataMapper();
|
return _database.OpenConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Version Version => _database.Version;
|
public Version Version => _database.Version;
|
||||||
|
|
|
@ -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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Marr.Data;
|
using Dapper;
|
||||||
using Marr.Data.Mapping;
|
|
||||||
using Marr.Data.QGen;
|
|
||||||
using NzbDrone.Common.Disk;
|
|
||||||
using NzbDrone.Common.Reflection;
|
using NzbDrone.Common.Reflection;
|
||||||
using NzbDrone.Core.ArtistStats;
|
|
||||||
using NzbDrone.Core.Authentication;
|
using NzbDrone.Core.Authentication;
|
||||||
using NzbDrone.Core.Blacklisting;
|
using NzbDrone.Core.Blacklisting;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.CustomFilters;
|
using NzbDrone.Core.CustomFilters;
|
||||||
using NzbDrone.Core.Datastore.Converters;
|
using NzbDrone.Core.Datastore.Converters;
|
||||||
using NzbDrone.Core.Datastore.Extensions;
|
|
||||||
using NzbDrone.Core.Download;
|
using NzbDrone.Core.Download;
|
||||||
using NzbDrone.Core.Download.Pending;
|
using NzbDrone.Core.Download.Pending;
|
||||||
using NzbDrone.Core.Extras.Lyrics;
|
using NzbDrone.Core.Extras.Lyrics;
|
||||||
|
@ -39,38 +34,47 @@ using NzbDrone.Core.RemotePathMappings;
|
||||||
using NzbDrone.Core.RootFolders;
|
using NzbDrone.Core.RootFolders;
|
||||||
using NzbDrone.Core.Tags;
|
using NzbDrone.Core.Tags;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
using static Dapper.SqlMapper;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore
|
namespace NzbDrone.Core.Datastore
|
||||||
{
|
{
|
||||||
public static class TableMapping
|
public static class TableMapping
|
||||||
{
|
{
|
||||||
private static readonly FluentMappings Mapper = new FluentMappings(true);
|
static TableMapping()
|
||||||
|
{
|
||||||
|
Mapper = new TableMapper();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TableMapper Mapper { get; private set; }
|
||||||
|
|
||||||
public static void Map()
|
public static void Map()
|
||||||
{
|
{
|
||||||
RegisterMappers();
|
RegisterMappers();
|
||||||
|
|
||||||
Mapper.Entity<Config>().RegisterModel("Config");
|
Mapper.Entity<Config>("Config").RegisterModel();
|
||||||
|
|
||||||
Mapper.Entity<RootFolder>().RegisterModel("RootFolders")
|
Mapper.Entity<RootFolder>("RootFolders").RegisterModel()
|
||||||
.Ignore(r => r.Accessible)
|
.Ignore(r => r.Accessible)
|
||||||
.Ignore(r => r.FreeSpace)
|
.Ignore(r => r.FreeSpace)
|
||||||
.Ignore(r => r.TotalSpace);
|
.Ignore(r => r.TotalSpace);
|
||||||
|
|
||||||
Mapper.Entity<ScheduledTask>().RegisterModel("ScheduledTasks");
|
Mapper.Entity<ScheduledTask>("ScheduledTasks").RegisterModel();
|
||||||
|
|
||||||
Mapper.Entity<IndexerDefinition>().RegisterDefinition("Indexers")
|
Mapper.Entity<IndexerDefinition>("Indexers").RegisterModel()
|
||||||
|
.Ignore(x => x.ImplementationName)
|
||||||
.Ignore(i => i.Enable)
|
.Ignore(i => i.Enable)
|
||||||
.Ignore(i => i.Protocol)
|
.Ignore(i => i.Protocol)
|
||||||
.Ignore(i => i.SupportsRss)
|
.Ignore(i => i.SupportsRss)
|
||||||
.Ignore(i => i.SupportsSearch)
|
.Ignore(i => i.SupportsSearch)
|
||||||
.Ignore(d => d.Tags);
|
.Ignore(d => d.Tags);
|
||||||
|
|
||||||
Mapper.Entity<ImportListDefinition>().RegisterDefinition("ImportLists")
|
Mapper.Entity<ImportListDefinition>("ImportLists").RegisterModel()
|
||||||
|
.Ignore(x => x.ImplementationName)
|
||||||
.Ignore(i => i.Enable)
|
.Ignore(i => i.Enable)
|
||||||
.Ignore(i => i.ListType);
|
.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.SupportsOnGrab)
|
||||||
.Ignore(i => i.SupportsOnReleaseImport)
|
.Ignore(i => i.SupportsOnReleaseImport)
|
||||||
.Ignore(i => i.SupportsOnUpgrade)
|
.Ignore(i => i.SupportsOnUpgrade)
|
||||||
|
@ -80,118 +84,110 @@ namespace NzbDrone.Core.Datastore
|
||||||
.Ignore(i => i.SupportsOnImportFailure)
|
.Ignore(i => i.SupportsOnImportFailure)
|
||||||
.Ignore(i => i.SupportsOnTrackRetag);
|
.Ignore(i => i.SupportsOnTrackRetag);
|
||||||
|
|
||||||
Mapper.Entity<MetadataDefinition>().RegisterDefinition("Metadata")
|
Mapper.Entity<MetadataDefinition>("Metadata").RegisterModel()
|
||||||
|
.Ignore(x => x.ImplementationName)
|
||||||
.Ignore(d => d.Tags);
|
.Ignore(d => d.Tags);
|
||||||
|
|
||||||
Mapper.Entity<DownloadClientDefinition>().RegisterDefinition("DownloadClients")
|
Mapper.Entity<DownloadClientDefinition>("DownloadClients").RegisterModel()
|
||||||
|
.Ignore(x => x.ImplementationName)
|
||||||
.Ignore(d => d.Protocol)
|
.Ignore(d => d.Protocol)
|
||||||
.Ignore(d => d.Tags);
|
.Ignore(d => d.Tags);
|
||||||
|
|
||||||
Mapper.Entity<History.History>().RegisterModel("History")
|
Mapper.Entity<History.History>("History").RegisterModel();
|
||||||
.AutoMapChildModels();
|
|
||||||
|
|
||||||
Mapper.Entity<Artist>().RegisterModel("Artists")
|
Mapper.Entity<Artist>("Artists")
|
||||||
.Ignore(s => s.RootFolderPath)
|
.Ignore(s => s.RootFolderPath)
|
||||||
.Ignore(s => s.Name)
|
.Ignore(s => s.Name)
|
||||||
.Ignore(s => s.ForeignArtistId)
|
.Ignore(s => s.ForeignArtistId)
|
||||||
.Relationship()
|
|
||||||
.HasOne(a => a.Metadata, a => a.ArtistMetadataId)
|
.HasOne(a => a.Metadata, a => a.ArtistMetadataId)
|
||||||
.HasOne(a => a.QualityProfile, a => a.QualityProfileId)
|
.HasOne(a => a.QualityProfile, a => a.QualityProfileId)
|
||||||
.HasOne(s => s.MetadataProfile, s => s.MetadataProfileId)
|
.HasOne(s => s.MetadataProfile, s => s.MetadataProfileId)
|
||||||
.For(a => a.Albums)
|
.LazyLoad(a => a.Albums, (db, a) => db.Query<Album>(new SqlBuilder().Where<Album>(rg => rg.ArtistMetadataId == a.Id)).ToList(), a => a.Id > 0);
|
||||||
.LazyLoad(condition: a => a.Id > 0, query: (db, a) => db.Query<Album>().Where(rg => rg.ArtistMetadataId == a.Id).ToList());
|
|
||||||
|
|
||||||
Mapper.Entity<ArtistMetadata>().RegisterModel("ArtistMetadata");
|
Mapper.Entity<ArtistMetadata>("ArtistMetadata").RegisterModel();
|
||||||
|
|
||||||
Mapper.Entity<Album>().RegisterModel("Albums")
|
Mapper.Entity<Album>("Albums").RegisterModel()
|
||||||
.Ignore(r => r.ArtistId)
|
.Ignore(x => x.ArtistId)
|
||||||
.Relationship()
|
|
||||||
.HasOne(r => r.ArtistMetadata, r => r.ArtistMetadataId)
|
.HasOne(r => r.ArtistMetadata, r => r.ArtistMetadataId)
|
||||||
.For(rg => rg.AlbumReleases)
|
.LazyLoad(a => a.AlbumReleases, (db, album) => db.Query<AlbumRelease>(new SqlBuilder().Where<AlbumRelease>(r => r.AlbumId == album.Id)).ToList(), a => a.Id > 0)
|
||||||
.LazyLoad(condition: rg => rg.Id > 0, query: (db, rg) => db.Query<AlbumRelease>().Where(r => r.AlbumId == rg.Id).ToList())
|
.LazyLoad(a => a.Artist,
|
||||||
.For(rg => rg.Artist)
|
(db, album) => ArtistRepository.Query(db,
|
||||||
.LazyLoad(condition: rg => rg.ArtistMetadataId > 0,
|
new SqlBuilder()
|
||||||
query: (db, rg) => db.Query<Artist>()
|
.Join<Artist, ArtistMetadata>((a, m) => a.ArtistMetadataId == m.Id)
|
||||||
.Join<Artist, ArtistMetadata>(JoinType.Inner, a => a.Metadata, (a, m) => a.ArtistMetadataId == m.Id)
|
.Where<Artist>(a => a.ArtistMetadataId == album.ArtistMetadataId)).SingleOrDefault(),
|
||||||
.Where(a => a.ArtistMetadataId == rg.ArtistMetadataId).SingleOrDefault());
|
a => a.ArtistMetadataId > 0);
|
||||||
|
|
||||||
Mapper.Entity<AlbumRelease>().RegisterModel("AlbumReleases")
|
Mapper.Entity<AlbumRelease>("AlbumReleases").RegisterModel()
|
||||||
.Relationship()
|
|
||||||
.HasOne(r => r.Album, r => r.AlbumId)
|
.HasOne(r => r.Album, r => r.AlbumId)
|
||||||
.For(r => r.Tracks)
|
.LazyLoad(x => x.Tracks, (db, release) => db.Query<Track>(new SqlBuilder().Where<Track>(t => t.AlbumReleaseId == release.Id)).ToList(), r => r.Id > 0);
|
||||||
.LazyLoad(condition: r => r.Id > 0, query: (db, r) => db.Query<Track>().Where(t => t.AlbumReleaseId == r.Id).ToList());
|
|
||||||
|
|
||||||
Mapper.Entity<Track>().RegisterModel("Tracks")
|
Mapper.Entity<Track>("Tracks").RegisterModel()
|
||||||
.Ignore(t => t.HasFile)
|
.Ignore(t => t.HasFile)
|
||||||
.Ignore(t => t.AlbumId)
|
.Ignore(t => t.AlbumId)
|
||||||
.Ignore(t => t.Album)
|
|
||||||
.Relationship()
|
|
||||||
.HasOne(track => track.AlbumRelease, track => track.AlbumReleaseId)
|
.HasOne(track => track.AlbumRelease, track => track.AlbumReleaseId)
|
||||||
.HasOne(track => track.ArtistMetadata, track => track.ArtistMetadataId)
|
.HasOne(track => track.ArtistMetadata, track => track.ArtistMetadataId)
|
||||||
.For(track => track.TrackFile)
|
.LazyLoad(t => t.TrackFile,
|
||||||
.LazyLoad(condition: track => track.TrackFileId > 0,
|
(db, track) => MediaFileRepository.Query(db,
|
||||||
query: (db, track) => db.Query<TrackFile>()
|
new SqlBuilder()
|
||||||
.Join<TrackFile, Track>(JoinType.Inner, t => t.Tracks, (t, x) => t.Id == x.TrackFileId)
|
.Join<TrackFile, Track>((l, r) => l.Id == r.TrackFileId)
|
||||||
.Join<TrackFile, Album>(JoinType.Inner, t => t.Album, (t, a) => t.AlbumId == a.Id)
|
.Join<TrackFile, Album>((l, r) => l.AlbumId == r.Id)
|
||||||
.Join<TrackFile, Artist>(JoinType.Inner, t => t.Artist, (t, a) => t.Album.Value.ArtistMetadataId == a.ArtistMetadataId)
|
.Join<Album, Artist>((l, r) => l.ArtistMetadataId == r.ArtistMetadataId)
|
||||||
.Join<Artist, ArtistMetadata>(JoinType.Inner, a => a.Metadata, (a, m) => a.ArtistMetadataId == m.Id)
|
.Join<Artist, ArtistMetadata>((l, r) => l.ArtistMetadataId == r.Id)
|
||||||
.Where(t => t.Id == track.TrackFileId)
|
.Where<TrackFile>(t => t.Id == track.TrackFileId)).SingleOrDefault(),
|
||||||
.SingleOrDefault())
|
t => t.TrackFileId > 0)
|
||||||
.For(t => t.Artist)
|
.LazyLoad(x => x.Artist,
|
||||||
.LazyLoad(condition: t => t.AlbumReleaseId > 0, query: (db, t) => db.Query<Artist>()
|
(db, t) => ArtistRepository.Query(db,
|
||||||
.Join<Artist, ArtistMetadata>(JoinType.Inner, a => a.Metadata, (a, m) => a.ArtistMetadataId == m.Id)
|
new SqlBuilder()
|
||||||
.Join<Artist, Album>(JoinType.Inner, a => a.Albums, (l, r) => l.ArtistMetadataId == r.ArtistMetadataId)
|
.Join<Artist, ArtistMetadata>((a, m) => a.ArtistMetadataId == m.Id)
|
||||||
.Join<Album, AlbumRelease>(JoinType.Inner, a => a.AlbumReleases, (l, r) => l.Id == r.AlbumId)
|
.Join<Artist, Album>((l, r) => l.ArtistMetadataId == r.ArtistMetadataId)
|
||||||
.Where<AlbumRelease>(r => r.Id == t.AlbumReleaseId)
|
.Join<Album, AlbumRelease>((l, r) => l.Id == r.AlbumId)
|
||||||
.SingleOrDefault());
|
.Where<AlbumRelease>(r => r.Id == t.AlbumReleaseId)).SingleOrDefault(),
|
||||||
|
t => t.Id > 0);
|
||||||
|
|
||||||
Mapper.Entity<TrackFile>().RegisterModel("TrackFiles")
|
Mapper.Entity<TrackFile>("TrackFiles").RegisterModel()
|
||||||
.Relationship()
|
|
||||||
.HasOne(f => f.Album, f => f.AlbumId)
|
.HasOne(f => f.Album, f => f.AlbumId)
|
||||||
.For(f => f.Tracks)
|
.LazyLoad(x => x.Tracks, (db, file) => db.Query<Track>(new SqlBuilder().Where<Track>(t => t.TrackFileId == file.Id)).ToList(), x => x.Id > 0)
|
||||||
.LazyLoad(condition: f => f.Id > 0, query: (db, f) => db.Query<Track>()
|
.LazyLoad(x => x.Artist,
|
||||||
.Where(x => x.TrackFileId == f.Id)
|
(db, f) => ArtistRepository.Query(db,
|
||||||
.ToList())
|
new SqlBuilder()
|
||||||
.For(t => t.Artist)
|
.Join<Artist, ArtistMetadata>((a, m) => a.ArtistMetadataId == m.Id)
|
||||||
.LazyLoad(condition: f => f.Id > 0, query: (db, f) => db.Query<Artist>()
|
.Join<Artist, Album>((l, r) => l.ArtistMetadataId == r.ArtistMetadataId)
|
||||||
.Join<Artist, ArtistMetadata>(JoinType.Inner, a => a.Metadata, (a, m) => a.ArtistMetadataId == m.Id)
|
.Where<Album>(a => a.Id == f.AlbumId)).SingleOrDefault(),
|
||||||
.Join<Artist, Album>(JoinType.Inner, a => a.Albums, (l, r) => l.ArtistMetadataId == r.ArtistMetadataId)
|
t => t.Id > 0);
|
||||||
.Where<Album>(r => r.Id == f.AlbumId)
|
|
||||||
.SingleOrDefault());
|
|
||||||
|
|
||||||
Mapper.Entity<QualityDefinition>().RegisterModel("QualityDefinitions")
|
Mapper.Entity<QualityDefinition>("QualityDefinitions").RegisterModel()
|
||||||
.Ignore(d => d.GroupName)
|
.Ignore(d => d.GroupName)
|
||||||
.Ignore(d => d.GroupWeight)
|
.Ignore(d => d.GroupWeight)
|
||||||
.Ignore(d => d.Weight);
|
.Ignore(d => d.Weight);
|
||||||
|
|
||||||
Mapper.Entity<QualityProfile>().RegisterModel("QualityProfiles");
|
Mapper.Entity<QualityProfile>("QualityProfiles").RegisterModel();
|
||||||
Mapper.Entity<MetadataProfile>().RegisterModel("MetadataProfiles");
|
Mapper.Entity<MetadataProfile>("MetadataProfiles").RegisterModel();
|
||||||
Mapper.Entity<Log>().RegisterModel("Logs");
|
Mapper.Entity<Log>("Logs").RegisterModel();
|
||||||
Mapper.Entity<NamingConfig>().RegisterModel("NamingConfig");
|
Mapper.Entity<NamingConfig>("NamingConfig").RegisterModel();
|
||||||
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<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);
|
.Ignore(e => e.RemoteAlbum);
|
||||||
|
|
||||||
Mapper.Entity<RemotePathMapping>().RegisterModel("RemotePathMappings");
|
Mapper.Entity<RemotePathMapping>("RemotePathMappings").RegisterModel();
|
||||||
Mapper.Entity<Tag>().RegisterModel("Tags");
|
Mapper.Entity<Tag>("Tags").RegisterModel();
|
||||||
Mapper.Entity<ReleaseProfile>().RegisterModel("ReleaseProfiles");
|
Mapper.Entity<ReleaseProfile>("ReleaseProfiles").RegisterModel();
|
||||||
|
|
||||||
Mapper.Entity<DelayProfile>().RegisterModel("DelayProfiles");
|
Mapper.Entity<DelayProfile>("DelayProfiles").RegisterModel();
|
||||||
Mapper.Entity<User>().RegisterModel("Users");
|
Mapper.Entity<User>("Users").RegisterModel();
|
||||||
Mapper.Entity<CommandModel>().RegisterModel("Commands")
|
Mapper.Entity<CommandModel>("Commands").RegisterModel()
|
||||||
.Ignore(c => c.Message);
|
.Ignore(c => c.Message);
|
||||||
|
|
||||||
Mapper.Entity<IndexerStatus>().RegisterModel("IndexerStatus");
|
Mapper.Entity<IndexerStatus>("IndexerStatus").RegisterModel();
|
||||||
Mapper.Entity<DownloadClientStatus>().RegisterModel("DownloadClientStatus");
|
Mapper.Entity<DownloadClientStatus>("DownloadClientStatus").RegisterModel();
|
||||||
Mapper.Entity<ImportListStatus>().RegisterModel("ImportListStatus");
|
Mapper.Entity<ImportListStatus>("ImportListStatus").RegisterModel();
|
||||||
|
|
||||||
Mapper.Entity<CustomFilter>().RegisterModel("CustomFilters");
|
Mapper.Entity<CustomFilter>("CustomFilters").RegisterModel();
|
||||||
Mapper.Entity<ImportListExclusion>().RegisterModel("ImportListExclusions");
|
Mapper.Entity<ImportListExclusion>("ImportListExclusions").RegisterModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RegisterMappers()
|
private static void RegisterMappers()
|
||||||
|
@ -199,40 +195,40 @@ namespace NzbDrone.Core.Datastore
|
||||||
RegisterEmbeddedConverter();
|
RegisterEmbeddedConverter();
|
||||||
RegisterProviderSettingConverter();
|
RegisterProviderSettingConverter();
|
||||||
|
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(int), new Int32Converter());
|
SqlMapper.RemoveTypeMap(typeof(DateTime));
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(double), new DoubleConverter());
|
SqlMapper.AddTypeHandler(new DapperUtcConverter());
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(DateTime), new UtcConverter());
|
SqlMapper.AddTypeHandler(new DapperQualityIntConverter());
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(bool), new BooleanIntConverter());
|
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<QualityProfileQualityItem>>(new QualityIntConverter()));
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(Enum), new EnumIntConverter());
|
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<QualityModel>(new QualityIntConverter()));
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(Quality), new QualityIntConverter());
|
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<Dictionary<string, string>>());
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(List<QualityProfileQualityItem>), new EmbeddedDocumentConverter(new QualityIntConverter()));
|
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<IDictionary<string, string>>());
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(QualityModel), new EmbeddedDocumentConverter(new QualityIntConverter()));
|
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<int>>());
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(Dictionary<string, string>), new EmbeddedDocumentConverter());
|
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<KeyValuePair<string, int>>>());
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(List<int>), new EmbeddedDocumentConverter());
|
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<KeyValuePair<string, int>>());
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(List<KeyValuePair<string, int>>), new EmbeddedDocumentConverter());
|
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<string>>());
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(List<string>), new EmbeddedDocumentConverter());
|
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<ProfilePrimaryAlbumTypeItem>>(new PrimaryAlbumTypeIntConverter()));
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(List<ProfilePrimaryAlbumTypeItem>), new EmbeddedDocumentConverter(new PrimaryAlbumTypeIntConverter()));
|
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<ProfileSecondaryAlbumTypeItem>>(new SecondaryAlbumTypeIntConverter()));
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(List<ProfileSecondaryAlbumTypeItem>), new EmbeddedDocumentConverter(new SecondaryAlbumTypeIntConverter()));
|
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<ProfileReleaseStatusItem>>(new ReleaseStatusIntConverter()));
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(List<ProfileReleaseStatusItem>), new EmbeddedDocumentConverter(new ReleaseStatusIntConverter()));
|
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<ParsedAlbumInfo>());
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(ParsedAlbumInfo), new EmbeddedDocumentConverter());
|
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<ParsedTrackInfo>());
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(ParsedTrackInfo), new EmbeddedDocumentConverter());
|
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<ReleaseInfo>());
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(ReleaseInfo), new EmbeddedDocumentConverter());
|
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<HashSet<int>>());
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(HashSet<int>), new EmbeddedDocumentConverter());
|
SqlMapper.AddTypeHandler(new OsPathConverter());
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(OsPath), new OsPathConverter());
|
SqlMapper.RemoveTypeMap(typeof(Guid));
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(Guid), new GuidConverter());
|
SqlMapper.RemoveTypeMap(typeof(Guid?));
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(Command), new CommandConverter());
|
SqlMapper.AddTypeHandler(new GuidConverter());
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(TimeSpan), new TimeSpanConverter());
|
SqlMapper.AddTypeHandler(new CommandConverter());
|
||||||
MapRepository.Instance.RegisterTypeConverter(typeof(TimeSpan?), new TimeSpanConverter());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RegisterProviderSettingConverter()
|
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();
|
var providerSettingConverter = new ProviderSettingConverter();
|
||||||
foreach (var embeddedType in settingTypes)
|
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 embeddedTypes = typeof(IEmbeddedDocument).Assembly.ImplementationsOf<IEmbeddedDocument>();
|
||||||
|
|
||||||
var embeddedConvertor = new EmbeddedDocumentConverter();
|
var embeddedConverterDefinition = typeof(EmbeddedDocumentConverter<>).GetGenericTypeDefinition();
|
||||||
var genericListDefinition = typeof(List<>).GetGenericTypeDefinition();
|
var genericListDefinition = typeof(List<>).GetGenericTypeDefinition();
|
||||||
|
|
||||||
foreach (var embeddedType in embeddedTypes)
|
foreach (var embeddedType in embeddedTypes)
|
||||||
{
|
{
|
||||||
var embeddedListType = genericListDefinition.MakeGenericType(embeddedType);
|
var embeddedListType = genericListDefinition.MakeGenericType(embeddedType);
|
||||||
|
|
||||||
MapRepository.Instance.RegisterTypeConverter(embeddedType, embeddedConvertor);
|
RegisterEmbeddedConverter(embeddedType, embeddedConverterDefinition);
|
||||||
MapRepository.Instance.RegisterTypeConverter(embeddedListType, embeddedConvertor);
|
RegisterEmbeddedConverter(embeddedListType, embeddedConverterDefinition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void RegisterEmbeddedConverter(Type embeddedType, Type embeddedConverterDefinition)
|
||||||
|
{
|
||||||
|
var embeddedConverterType = embeddedConverterDefinition.MakeGenericType(embeddedType);
|
||||||
|
var converter = (ITypeHandler)Activator.CreateInstance(embeddedConverterType);
|
||||||
|
|
||||||
|
SqlMapper.AddTypeHandler(embeddedType, converter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
public void DeleteByArtistId(int artistId)
|
||||||
{
|
{
|
||||||
Delete(r => r.ArtistId == artistId);
|
Delete(artistId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PendingRelease> AllByArtistId(int artistId)
|
public List<PendingRelease> AllByArtistId(int artistId)
|
||||||
{
|
{
|
||||||
return Query.Where(p => p.ArtistId == artistId);
|
return Query(p => p.ArtistId == artistId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PendingRelease> WithoutFallback()
|
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)
|
foreach (var trackFile in trackFiles)
|
||||||
{
|
{
|
||||||
var localTrackFile = trackFile;
|
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;
|
return trackFiles;
|
||||||
|
|
|
@ -42,22 +42,22 @@ namespace NzbDrone.Core.Extras.Files
|
||||||
|
|
||||||
public List<TExtraFile> GetFilesByArtist(int artistId)
|
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)
|
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)
|
public List<TExtraFile> GetFilesByTrackFile(int trackFileId)
|
||||||
{
|
{
|
||||||
return Query.Where(c => c.TrackFileId == trackFileId);
|
return Query(c => c.TrackFileId == trackFileId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TExtraFile FindByPath(string path)
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Marr.Data.QGen;
|
using Dapper;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Core.Music;
|
using NzbDrone.Core.Music;
|
||||||
|
@ -30,63 +30,72 @@ namespace NzbDrone.Core.History
|
||||||
|
|
||||||
public History MostRecentForAlbum(int albumId)
|
public History MostRecentForAlbum(int albumId)
|
||||||
{
|
{
|
||||||
return Query.Where(h => h.AlbumId == albumId)
|
return Query(h => h.AlbumId == albumId)
|
||||||
.OrderByDescending(h => h.Date)
|
.OrderByDescending(h => h.Date)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
public History MostRecentForDownloadId(string downloadId)
|
public History MostRecentForDownloadId(string downloadId)
|
||||||
{
|
{
|
||||||
return Query.Where(h => h.DownloadId == downloadId)
|
return Query(h => h.DownloadId == downloadId)
|
||||||
.OrderByDescending(h => h.Date)
|
.OrderByDescending(h => h.Date)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<History> FindByDownloadId(string downloadId)
|
public List<History> FindByDownloadId(string downloadId)
|
||||||
{
|
{
|
||||||
return Query.Join<History, Artist>(JoinType.Left, h => h.Artist, (h, a) => h.ArtistId == a.Id)
|
return _database.QueryJoined<History, Artist, Album>(
|
||||||
.Join<History, Album>(JoinType.Left, h => h.Album, (h, r) => h.AlbumId == r.Id)
|
Builder()
|
||||||
.Where(h => h.DownloadId == downloadId);
|
.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)
|
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)
|
if (eventType.HasValue)
|
||||||
{
|
{
|
||||||
query.AndWhere(h => h.EventType == eventType);
|
builder.Where<History>(h => h.EventType == eventType);
|
||||||
}
|
}
|
||||||
|
|
||||||
query.OrderByDescending(h => h.Date);
|
return Query(builder).OrderByDescending(h => h.Date).ToList();
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<History> GetByAlbum(int albumId, HistoryEventType? eventType)
|
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)
|
var builder = Builder()
|
||||||
.Where(h => h.AlbumId == albumId);
|
.Join<History, Album>((h, a) => h.AlbumId == a.Id)
|
||||||
|
.Where<History>(h => h.AlbumId == albumId);
|
||||||
|
|
||||||
if (eventType.HasValue)
|
if (eventType.HasValue)
|
||||||
{
|
{
|
||||||
query.AndWhere(h => h.EventType == eventType);
|
builder.Where<History>(h => h.EventType == eventType);
|
||||||
}
|
}
|
||||||
|
|
||||||
query.OrderByDescending(h => h.Date);
|
return _database.QueryJoined<History, Album>(
|
||||||
|
builder,
|
||||||
return query;
|
(history, album) =>
|
||||||
|
{
|
||||||
|
history.Album = album;
|
||||||
|
return history;
|
||||||
|
}).OrderByDescending(h => h.Date).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<History> FindDownloadHistory(int idArtistId, QualityModel quality)
|
public List<History> FindDownloadHistory(int idArtistId, QualityModel quality)
|
||||||
{
|
{
|
||||||
return Query.Where(h =>
|
var allowed = new[] { HistoryEventType.Grabbed, HistoryEventType.DownloadFailed, HistoryEventType.TrackFileImported };
|
||||||
h.ArtistId == idArtistId &&
|
|
||||||
h.Quality == quality &&
|
return Query(h => h.ArtistId == idArtistId &&
|
||||||
(h.EventType == HistoryEventType.Grabbed ||
|
h.Quality == quality &&
|
||||||
h.EventType == HistoryEventType.DownloadFailed ||
|
allowed.Contains(h.EventType));
|
||||||
h.EventType == HistoryEventType.TrackFileImported))
|
|
||||||
.ToList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteForArtist(int artistId)
|
public void DeleteForArtist(int artistId)
|
||||||
|
@ -94,27 +103,29 @@ namespace NzbDrone.Core.History
|
||||||
Delete(c => c.ArtistId == artistId);
|
Delete(c => c.ArtistId == artistId);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override SortBuilder<History> GetPagedQuery(QueryBuilder<History> query, PagingSpec<History> pagingSpec)
|
protected override SqlBuilder PagedBuilder() => new SqlBuilder()
|
||||||
{
|
.Join<History, Artist>((h, a) => h.ArtistId == a.Id)
|
||||||
var baseQuery = query.Join<History, Artist>(JoinType.Inner, h => h.Artist, (h, a) => h.ArtistId == a.Id)
|
.Join<History, Album>((h, a) => h.AlbumId == a.Id)
|
||||||
.Join<History, Album>(JoinType.Inner, h => h.Album, (h, r) => h.AlbumId == r.Id)
|
.LeftJoin<History, Track>((h, t) => h.TrackId == t.Id);
|
||||||
.Join<History, Track>(JoinType.Left, h => h.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) =>
|
||||||
return base.GetPagedQuery(baseQuery, pagingSpec);
|
{
|
||||||
}
|
history.Artist = artist;
|
||||||
|
history.Album = album;
|
||||||
|
history.Track = track;
|
||||||
|
return history;
|
||||||
|
});
|
||||||
|
|
||||||
public List<History> Since(DateTime date, HistoryEventType? eventType)
|
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)
|
if (eventType.HasValue)
|
||||||
{
|
{
|
||||||
query.AndWhere(h => h.EventType == eventType);
|
builder.Where<History>(h => h.EventType == eventType);
|
||||||
}
|
}
|
||||||
|
|
||||||
query.OrderBy(h => h.Date);
|
return Query(builder).OrderBy(h => h.Date).ToList();
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using NzbDrone.Core.Datastore;
|
using Dapper;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
{
|
{
|
||||||
|
@ -13,9 +14,9 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
|
||||||
public void Clean()
|
public void Clean()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT Id FROM MetadataFiles
|
SELECT Id FROM MetadataFiles
|
||||||
WHERE RelativePath
|
WHERE RelativePath
|
||||||
|
@ -25,6 +26,7 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
OR RelativePath
|
OR RelativePath
|
||||||
LIKE '/%'
|
LIKE '/%'
|
||||||
)");
|
)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using NzbDrone.Core.Datastore;
|
using Dapper;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
{
|
{
|
||||||
|
@ -13,12 +14,13 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
|
||||||
public void Clean()
|
public void Clean()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM NamingConfig
|
mapper.Execute(@"DELETE FROM NamingConfig
|
||||||
WHERE ID NOT IN (
|
WHERE ID NOT IN (
|
||||||
SELECT ID FROM NamingConfig
|
SELECT ID FROM NamingConfig
|
||||||
LIMIT 1)");
|
LIMIT 1)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using NzbDrone.Core.Datastore;
|
using Dapper;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
{
|
{
|
||||||
|
@ -13,12 +14,13 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
|
||||||
public void Clean()
|
public void Clean()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM Users
|
mapper.Execute(@"DELETE FROM Users
|
||||||
WHERE ID NOT IN (
|
WHERE ID NOT IN (
|
||||||
SELECT ID FROM Users
|
SELECT ID FROM Users
|
||||||
LIMIT 1)");
|
LIMIT 1)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using Dapper;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.Download.Pending;
|
using NzbDrone.Core.Download.Pending;
|
||||||
|
|
||||||
|
@ -15,18 +16,17 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
|
||||||
public void Clean()
|
public void Clean()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
var twoWeeksAgo = DateTime.UtcNow.AddDays(-14);
|
{
|
||||||
|
mapper.Execute(@"DELETE FROM PendingReleases
|
||||||
mapper.Delete<PendingRelease>(p => p.Added < twoWeeksAgo &&
|
WHERE Added < @TwoWeeksAgo
|
||||||
(p.Reason == PendingReleaseReason.DownloadClientUnavailable ||
|
AND REASON IN @Reasons",
|
||||||
p.Reason == PendingReleaseReason.Fallback));
|
new
|
||||||
|
{
|
||||||
// mapper.AddParameter("twoWeeksAgo", $"{DateTime.UtcNow.AddDays(-14).ToString("s")}Z");
|
TwoWeeksAgo = DateTime.UtcNow.AddDays(-14),
|
||||||
|
Reasons = new[] { (int)PendingReleaseReason.DownloadClientUnavailable, (int)PendingReleaseReason.Fallback }
|
||||||
// mapper.ExecuteNonQuery(@"DELETE FROM PendingReleases
|
});
|
||||||
// WHERE Added < @twoWeeksAgo
|
}
|
||||||
// AND (Reason = 'DownloadClientUnavailable' OR Reason = 'Fallback')");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Dapper;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
@ -21,54 +22,58 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
|
||||||
private void DeleteDuplicateArtistMetadata()
|
private void DeleteDuplicateArtistMetadata()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT Id FROM MetadataFiles
|
SELECT Id FROM MetadataFiles
|
||||||
WHERE Type = 1
|
WHERE Type = 1
|
||||||
GROUP BY ArtistId, Consumer
|
GROUP BY ArtistId, Consumer
|
||||||
HAVING COUNT(ArtistId) > 1
|
HAVING COUNT(ArtistId) > 1
|
||||||
)");
|
)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeleteDuplicateAlbumMetadata()
|
private void DeleteDuplicateAlbumMetadata()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT Id FROM MetadataFiles
|
SELECT Id FROM MetadataFiles
|
||||||
WHERE Type = 6
|
WHERE Type = 6
|
||||||
GROUP BY AlbumId, Consumer
|
GROUP BY AlbumId, Consumer
|
||||||
HAVING COUNT(AlbumId) > 1
|
HAVING COUNT(AlbumId) > 1
|
||||||
)");
|
)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeleteDuplicateTrackMetadata()
|
private void DeleteDuplicateTrackMetadata()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT Id FROM MetadataFiles
|
SELECT Id FROM MetadataFiles
|
||||||
WHERE Type = 2
|
WHERE Type = 2
|
||||||
GROUP BY TrackFileId, Consumer
|
GROUP BY TrackFileId, Consumer
|
||||||
HAVING COUNT(TrackFileId) > 1
|
HAVING COUNT(TrackFileId) > 1
|
||||||
)");
|
)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeleteDuplicateTrackImages()
|
private void DeleteDuplicateTrackImages()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT Id FROM MetadataFiles
|
SELECT Id FROM MetadataFiles
|
||||||
WHERE Type = 5
|
WHERE Type = 5
|
||||||
GROUP BY TrackFileId, Consumer
|
GROUP BY TrackFileId, Consumer
|
||||||
HAVING COUNT(TrackFileId) > 1
|
HAVING COUNT(TrackFileId) > 1
|
||||||
)");
|
)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Dapper;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
@ -13,14 +14,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
|
||||||
public void Clean()
|
public void Clean()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM Albums
|
mapper.Execute(@"DELETE FROM Albums
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT Albums.Id FROM Albums
|
SELECT Albums.Id FROM Albums
|
||||||
LEFT OUTER JOIN Artists
|
LEFT OUTER JOIN Artists
|
||||||
ON Albums.ArtistMetadataId = Artists.ArtistMetadataId
|
ON Albums.ArtistMetadataId = Artists.ArtistMetadataId
|
||||||
WHERE Artists.Id IS NULL)");
|
WHERE Artists.Id IS NULL)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Dapper;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
@ -13,15 +14,16 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
|
||||||
public void Clean()
|
public void Clean()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM ArtistMetadata
|
mapper.Execute(@"DELETE FROM ArtistMetadata
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT ArtistMetadata.Id FROM ArtistMetadata
|
SELECT ArtistMetadata.Id FROM ArtistMetadata
|
||||||
LEFT OUTER JOIN Albums ON Albums.ArtistMetadataId = ArtistMetadata.Id
|
LEFT OUTER JOIN Albums ON Albums.ArtistMetadataId = ArtistMetadata.Id
|
||||||
LEFT OUTER JOIN Tracks ON Tracks.ArtistMetadataId = ArtistMetadata.Id
|
LEFT OUTER JOIN Tracks ON Tracks.ArtistMetadataId = ArtistMetadata.Id
|
||||||
LEFT OUTER JOIN Artists ON Artists.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)");
|
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
|
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
{
|
{
|
||||||
|
@ -13,14 +14,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
|
||||||
public void Clean()
|
public void Clean()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM Blacklist
|
mapper.Execute(@"DELETE FROM Blacklist
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT Blacklist.Id FROM Blacklist
|
SELECT Blacklist.Id FROM Blacklist
|
||||||
LEFT OUTER JOIN Artists
|
LEFT OUTER JOIN Artists
|
||||||
ON Blacklist.ArtistId = Artists.Id
|
ON Blacklist.ArtistId = Artists.Id
|
||||||
WHERE Artists.Id IS NULL)");
|
WHERE Artists.Id IS NULL)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Dapper;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
@ -13,14 +14,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
|
||||||
public void Clean()
|
public void Clean()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM DownloadClientStatus
|
mapper.Execute(@"DELETE FROM DownloadClientStatus
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT DownloadClientStatus.Id FROM DownloadClientStatus
|
SELECT DownloadClientStatus.Id FROM DownloadClientStatus
|
||||||
LEFT OUTER JOIN DownloadClients
|
LEFT OUTER JOIN DownloadClients
|
||||||
ON DownloadClientStatus.ProviderId = DownloadClients.Id
|
ON DownloadClientStatus.ProviderId = DownloadClients.Id
|
||||||
WHERE DownloadClients.Id IS NULL)");
|
WHERE DownloadClients.Id IS NULL)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using NzbDrone.Core.Datastore;
|
using Dapper;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
{
|
{
|
||||||
|
@ -19,26 +20,28 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
|
||||||
private void CleanupOrphanedByArtist()
|
private void CleanupOrphanedByArtist()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM History
|
mapper.Execute(@"DELETE FROM History
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT History.Id FROM History
|
SELECT History.Id FROM History
|
||||||
LEFT OUTER JOIN Artists
|
LEFT OUTER JOIN Artists
|
||||||
ON History.ArtistId = Artists.Id
|
ON History.ArtistId = Artists.Id
|
||||||
WHERE Artists.Id IS NULL)");
|
WHERE Artists.Id IS NULL)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CleanupOrphanedByAlbum()
|
private void CleanupOrphanedByAlbum()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM History
|
mapper.Execute(@"DELETE FROM History
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT History.Id FROM History
|
SELECT History.Id FROM History
|
||||||
LEFT OUTER JOIN Albums
|
LEFT OUTER JOIN Albums
|
||||||
ON History.AlbumId = Albums.Id
|
ON History.AlbumId = Albums.Id
|
||||||
WHERE Albums.Id IS NULL)");
|
WHERE Albums.Id IS NULL)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Dapper;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
@ -13,14 +14,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
|
||||||
public void Clean()
|
public void Clean()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM ImportListStatus
|
mapper.Execute(@"DELETE FROM ImportListStatus
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT ImportListStatus.Id FROM ImportListStatus
|
SELECT ImportListStatus.Id FROM ImportListStatus
|
||||||
LEFT OUTER JOIN ImportLists
|
LEFT OUTER JOIN ImportLists
|
||||||
ON ImportListStatus.ProviderId = ImportLists.Id
|
ON ImportListStatus.ProviderId = ImportLists.Id
|
||||||
WHERE ImportLists.Id IS NULL)");
|
WHERE ImportLists.Id IS NULL)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Dapper;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
@ -13,14 +14,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
|
||||||
public void Clean()
|
public void Clean()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM IndexerStatus
|
mapper.Execute(@"DELETE FROM IndexerStatus
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT IndexerStatus.Id FROM IndexerStatus
|
SELECT IndexerStatus.Id FROM IndexerStatus
|
||||||
LEFT OUTER JOIN Indexers
|
LEFT OUTER JOIN Indexers
|
||||||
ON IndexerStatus.ProviderId = Indexers.Id
|
ON IndexerStatus.ProviderId = Indexers.Id
|
||||||
WHERE Indexers.Id IS NULL)");
|
WHERE Indexers.Id IS NULL)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Dapper;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
@ -22,62 +23,67 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
|
||||||
private void DeleteOrphanedByArtist()
|
private void DeleteOrphanedByArtist()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT MetadataFiles.Id FROM MetadataFiles
|
SELECT MetadataFiles.Id FROM MetadataFiles
|
||||||
LEFT OUTER JOIN Artists
|
LEFT OUTER JOIN Artists
|
||||||
ON MetadataFiles.ArtistId = Artists.Id
|
ON MetadataFiles.ArtistId = Artists.Id
|
||||||
WHERE Artists.Id IS NULL)");
|
WHERE Artists.Id IS NULL)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeleteOrphanedByAlbum()
|
private void DeleteOrphanedByAlbum()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT MetadataFiles.Id FROM MetadataFiles
|
SELECT MetadataFiles.Id FROM MetadataFiles
|
||||||
LEFT OUTER JOIN Albums
|
LEFT OUTER JOIN Albums
|
||||||
ON MetadataFiles.AlbumId = Albums.Id
|
ON MetadataFiles.AlbumId = Albums.Id
|
||||||
WHERE MetadataFiles.AlbumId > 0
|
WHERE MetadataFiles.AlbumId > 0
|
||||||
AND Albums.Id IS NULL)");
|
AND Albums.Id IS NULL)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeleteOrphanedByTrackFile()
|
private void DeleteOrphanedByTrackFile()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT MetadataFiles.Id FROM MetadataFiles
|
SELECT MetadataFiles.Id FROM MetadataFiles
|
||||||
LEFT OUTER JOIN TrackFiles
|
LEFT OUTER JOIN TrackFiles
|
||||||
ON MetadataFiles.TrackFileId = TrackFiles.Id
|
ON MetadataFiles.TrackFileId = TrackFiles.Id
|
||||||
WHERE MetadataFiles.TrackFileId > 0
|
WHERE MetadataFiles.TrackFileId > 0
|
||||||
AND TrackFiles.Id IS NULL)");
|
AND TrackFiles.Id IS NULL)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeleteWhereAlbumIdIsZero()
|
private void DeleteWhereAlbumIdIsZero()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT Id FROM MetadataFiles
|
SELECT Id FROM MetadataFiles
|
||||||
WHERE Type IN (4, 6)
|
WHERE Type IN (4, 6)
|
||||||
AND AlbumId = 0)");
|
AND AlbumId = 0)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeleteWhereTrackFileIsZero()
|
private void DeleteWhereTrackFileIsZero()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
|
mapper.Execute(@"DELETE FROM MetadataFiles
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT Id FROM MetadataFiles
|
SELECT Id FROM MetadataFiles
|
||||||
WHERE Type IN (2, 5)
|
WHERE Type IN (2, 5)
|
||||||
AND TrackFileId = 0)");
|
AND TrackFileId = 0)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using NzbDrone.Core.Datastore;
|
using Dapper;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
{
|
{
|
||||||
|
@ -13,14 +14,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
|
||||||
public void Clean()
|
public void Clean()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM PendingReleases
|
mapper.Execute(@"DELETE FROM PendingReleases
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT PendingReleases.Id FROM PendingReleases
|
SELECT PendingReleases.Id FROM PendingReleases
|
||||||
LEFT OUTER JOIN Artists
|
LEFT OUTER JOIN Artists
|
||||||
ON PendingReleases.ArtistId = Artists.Id
|
ON PendingReleases.ArtistId = Artists.Id
|
||||||
WHERE Artists.Id IS NULL)");
|
WHERE Artists.Id IS NULL)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Dapper;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
@ -13,14 +14,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
|
||||||
public void Clean()
|
public void Clean()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM AlbumReleases
|
mapper.Execute(@"DELETE FROM AlbumReleases
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT AlbumReleases.Id FROM AlbumReleases
|
SELECT AlbumReleases.Id FROM AlbumReleases
|
||||||
LEFT OUTER JOIN Albums
|
LEFT OUTER JOIN Albums
|
||||||
ON AlbumReleases.AlbumId = Albums.Id
|
ON AlbumReleases.AlbumId = Albums.Id
|
||||||
WHERE Albums.Id IS NULL)");
|
WHERE Albums.Id IS NULL)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Dapper;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
@ -13,10 +14,10 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
|
||||||
public void Clean()
|
public void Clean()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
// Unlink where track no longer exists
|
// Unlink where track no longer exists
|
||||||
mapper.ExecuteNonQuery(@"UPDATE TrackFiles
|
mapper.Execute(@"UPDATE TrackFiles
|
||||||
SET AlbumId = 0
|
SET AlbumId = 0
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT TrackFiles.Id FROM TrackFiles
|
SELECT TrackFiles.Id FROM TrackFiles
|
||||||
|
@ -24,14 +25,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
ON TrackFiles.Id = Tracks.TrackFileId
|
ON TrackFiles.Id = Tracks.TrackFileId
|
||||||
WHERE Tracks.Id IS NULL)");
|
WHERE Tracks.Id IS NULL)");
|
||||||
|
|
||||||
// Unlink Tracks where the Trackfiles entry no longer exists
|
// Unlink Tracks where the Trackfiles entry no longer exists
|
||||||
mapper.ExecuteNonQuery(@"UPDATE Tracks
|
mapper.Execute(@"UPDATE Tracks
|
||||||
SET TrackFileId = 0
|
SET TrackFileId = 0
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT Tracks.Id FROM Tracks
|
SELECT Tracks.Id FROM Tracks
|
||||||
LEFT OUTER JOIN TrackFiles
|
LEFT OUTER JOIN TrackFiles
|
||||||
ON Tracks.TrackFileId = TrackFiles.Id
|
ON Tracks.TrackFileId = TrackFiles.Id
|
||||||
WHERE TrackFiles.Id IS NULL)");
|
WHERE TrackFiles.Id IS NULL)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Dapper;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
@ -13,14 +14,15 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
|
||||||
public void Clean()
|
public void Clean()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
mapper.ExecuteNonQuery(@"DELETE FROM Tracks
|
mapper.Execute(@"DELETE FROM Tracks
|
||||||
WHERE Id IN (
|
WHERE Id IN (
|
||||||
SELECT Tracks.Id FROM Tracks
|
SELECT Tracks.Id FROM Tracks
|
||||||
LEFT OUTER JOIN AlbumReleases
|
LEFT OUTER JOIN AlbumReleases
|
||||||
ON Tracks.AlbumReleaseId = AlbumReleases.Id
|
ON Tracks.AlbumReleaseId = AlbumReleases.Id
|
||||||
WHERE AlbumReleases.Id IS NULL)");
|
WHERE AlbumReleases.Id IS NULL)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Marr.Data;
|
using Dapper;
|
||||||
using NzbDrone.Common.Serializer;
|
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
@ -17,24 +17,25 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
|
|
||||||
public void Clean()
|
public void Clean()
|
||||||
{
|
{
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
|
{
|
||||||
var usedTags = new[] { "Artists", "Notifications", "DelayProfiles", "ReleaseProfiles" }
|
var usedTags = new[] { "Artists", "Notifications", "DelayProfiles", "ReleaseProfiles" }
|
||||||
.SelectMany(v => GetUsedTags(v, mapper))
|
.SelectMany(v => GetUsedTags(v, mapper))
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToArray();
|
.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))
|
return mapper.Query<List<int>>($"SELECT DISTINCT Tags FROM {table} WHERE NOT Tags = '[]'")
|
||||||
.SelectMany(Json.Deserialize<List<int>>)
|
.SelectMany(x => x)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using Dapper;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
|
@ -23,12 +24,13 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||||
_logger.Debug("Not running scheduled task last execution cleanup during debug");
|
_logger.Debug("Not running scheduled task last execution cleanup during debug");
|
||||||
}
|
}
|
||||||
|
|
||||||
var mapper = _database.GetDataMapper();
|
using (var mapper = _database.OpenConnection())
|
||||||
mapper.AddParameter("time", DateTime.UtcNow);
|
{
|
||||||
|
mapper.Execute(@"UPDATE ScheduledTasks
|
||||||
mapper.ExecuteNonQuery(@"UPDATE ScheduledTasks
|
SET LastExecution = @time
|
||||||
SET LastExecution = @time
|
WHERE LastExecution > @time",
|
||||||
WHERE LastExecution > @time");
|
new { time = DateTime.UtcNow });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,14 @@ namespace NzbDrone.Core.ImportLists.Exclusions
|
||||||
|
|
||||||
public ImportListExclusion FindByForeignId(string foreignId)
|
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)
|
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; }
|
public int MinimumSeeders { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(4)]
|
[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)]
|
[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; }
|
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