parent
20269aba9b
commit
53c0ffd129
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
@ -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,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,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,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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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)");
|
||||
}
|
||||
}
|
||||
}
|
@ -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,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,62 +1,21 @@
|
||||
using System;
|
||||
using Marr.Data.Converters;
|
||||
using Marr.Data.Mapping;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using NzbDrone.Core.Music;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Converters
|
||||
{
|
||||
public class PrimaryAlbumTypeIntConverter : JsonConverter, IConverter
|
||||
public class PrimaryAlbumTypeIntConverter : JsonConverter<PrimaryAlbumType>
|
||||
{
|
||||
public object FromDB(ConverterContext context)
|
||||
public override PrimaryAlbumType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (context.DbValue == DBNull.Value)
|
||||
{
|
||||
return PrimaryAlbumType.Album;
|
||||
}
|
||||
|
||||
var val = Convert.ToInt32(context.DbValue);
|
||||
|
||||
return (PrimaryAlbumType)val;
|
||||
}
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
{
|
||||
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
|
||||
}
|
||||
|
||||
public object ToDB(object clrValue)
|
||||
{
|
||||
if (clrValue == DBNull.Value)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (clrValue as 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);
|
||||
var item = reader.GetInt32();
|
||||
return (PrimaryAlbumType)item;
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
public override void Write(Utf8JsonWriter writer, PrimaryAlbumType value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteValue(ToDB(value));
|
||||
writer.WriteNumberValue((int)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,62 +1,21 @@
|
||||
using System;
|
||||
using Marr.Data.Converters;
|
||||
using Marr.Data.Mapping;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using NzbDrone.Core.Music;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Converters
|
||||
{
|
||||
public class ReleaseStatusIntConverter : JsonConverter, IConverter
|
||||
public class ReleaseStatusIntConverter : JsonConverter<ReleaseStatus>
|
||||
{
|
||||
public object FromDB(ConverterContext context)
|
||||
public override ReleaseStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (context.DbValue == DBNull.Value)
|
||||
{
|
||||
return ReleaseStatus.Official;
|
||||
}
|
||||
|
||||
var val = Convert.ToInt32(context.DbValue);
|
||||
|
||||
return (ReleaseStatus)val;
|
||||
}
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
{
|
||||
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
|
||||
}
|
||||
|
||||
public object ToDB(object clrValue)
|
||||
{
|
||||
if (clrValue == DBNull.Value)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (clrValue as 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);
|
||||
var item = reader.GetInt32();
|
||||
return (ReleaseStatus)item;
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
public override void Write(Utf8JsonWriter writer, ReleaseStatus value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteValue(ToDB(value));
|
||||
writer.WriteNumberValue((int)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,65 +1,21 @@
|
||||
using System;
|
||||
using Marr.Data.Converters;
|
||||
using Marr.Data.Mapping;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using NzbDrone.Core.Music;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Converters
|
||||
{
|
||||
public class SecondaryAlbumTypeIntConverter : JsonConverter, IConverter
|
||||
public class SecondaryAlbumTypeIntConverter : JsonConverter<SecondaryAlbumType>
|
||||
{
|
||||
public object FromDB(ConverterContext context)
|
||||
public override SecondaryAlbumType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (context.DbValue == DBNull.Value)
|
||||
{
|
||||
return SecondaryAlbumType.Studio;
|
||||
}
|
||||
|
||||
var val = Convert.ToInt32(context.DbValue);
|
||||
|
||||
return (SecondaryAlbumType)val;
|
||||
}
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
{
|
||||
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
|
||||
}
|
||||
|
||||
public object ToDB(object clrValue)
|
||||
{
|
||||
if (clrValue == DBNull.Value)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (clrValue as 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);
|
||||
var item = reader.GetInt32();
|
||||
return (SecondaryAlbumType)item;
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
public override void Write(Utf8JsonWriter writer, SecondaryAlbumType value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteValue(ToDB(value));
|
||||
writer.WriteNumberValue((int)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue