diff --git a/Sieve/Services/SieveProcessor.cs b/Sieve/Services/SieveProcessor.cs index 792355a..c542a38 100644 --- a/Sieve/Services/SieveProcessor.cs +++ b/Sieve/Services/SieveProcessor.cs @@ -142,7 +142,7 @@ namespace Sieve.Services ? converter.ConvertFrom(filterTerm.Value) : Convert.ChangeType(filterTerm.Value, property.PropertyType); - Expression filterValue = GetClosureOverConstant(constantVal); + Expression filterValue = GetClosureOverConstant(constantVal, property.PropertyType); dynamic propertyValue = Expression.PropertyOrField(parameterExpression, property.Name); @@ -219,12 +219,13 @@ namespace Sieve.Services } } - //Workaround to ensure that the filter value gets passed as a parameter in generated SQL from EF Core - //See https://github.com/aspnet/EntityFrameworkCore/issues/3361 - private Expression GetClosureOverConstant(T constant) + // Workaround to ensure that the filter value gets passed as a parameter in generated SQL from EF Core + // See https://github.com/aspnet/EntityFrameworkCore/issues/3361 + // Expression.Constant passed the target type to allow Nullable comparison + // See http://bradwilson.typepad.com/blog/2008/07/creating-nullab.html + private Expression GetClosureOverConstant(T constant, Type targetType) { - Expression> closure = () => constant; - return closure.Body; + return Expression.Constant(constant, targetType); } private IQueryable ApplySorting( diff --git a/SieveTests/Entities/Post.cs b/SieveTests/Entities/Post.cs index 4bd4518..23d2cb4 100644 --- a/SieveTests/Entities/Post.cs +++ b/SieveTests/Entities/Post.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel.DataAnnotations.Schema; using Sieve.Attributes; @@ -14,6 +14,7 @@ namespace SieveTests.Entities [Sieve(CanFilter = true, CanSort = true)] public int LikeCount { get; set; } = new Random().Next(0, 1000); + [Sieve(CanFilter = true, CanSort = true)] public int CommentCount { get; set; } = new Random().Next(0, 1000); @@ -23,5 +24,8 @@ namespace SieveTests.Entities [Sieve(CanFilter = true, CanSort = true)] [Column(TypeName = "datetime")] public DateTime DateLastViewed { get; set; } = DateTime.UtcNow; + + [Sieve(CanFilter = true, CanSort = true)] + public int? CategoryId { get; set; } = new Random().Next(0, 4); } } diff --git a/SieveTests/Migrations/20180127005347_Init.cs b/SieveTests/Migrations/20180127005347_Init.cs index 9ef971a..c69e330 100644 --- a/SieveTests/Migrations/20180127005347_Init.cs +++ b/SieveTests/Migrations/20180127005347_Init.cs @@ -17,7 +17,8 @@ namespace SieveTests.Migrations CommentCount = table.Column(nullable: false), DateCreated = table.Column(nullable: false), LikeCount = table.Column(nullable: false), - Title = table.Column(nullable: true) + Title = table.Column(nullable: true), + CategoryId = table.Column(nullable: true) }, constraints: table => table.PrimaryKey("PK_Posts", x => x.Id)); } diff --git a/SieveTests/Migrations/ApplicationDbContextModelSnapshot.cs b/SieveTests/Migrations/ApplicationDbContextModelSnapshot.cs index 4455496..f2a7904 100644 --- a/SieveTests/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/SieveTests/Migrations/ApplicationDbContextModelSnapshot.cs @@ -34,6 +34,8 @@ namespace SieveTests.Migrations b.Property("LikeCount"); + b.Property("CategoryId"); + b.Property("Title"); b.HasKey("Id"); diff --git a/SieveUnitTests/Entities/Post.cs b/SieveUnitTests/Entities/Post.cs index c4b44d3..23a118d 100644 --- a/SieveUnitTests/Entities/Post.cs +++ b/SieveUnitTests/Entities/Post.cs @@ -19,6 +19,9 @@ namespace SieveUnitTests.Entities [Sieve(CanFilter = true, CanSort = true)] public DateTimeOffset DateCreated { get; set; } = DateTimeOffset.UtcNow; + [Sieve(CanFilter = true, CanSort = true)] + public int? CategoryId { get; set; } = new Random().Next(0, 4); + [Sieve(CanFilter = true, CanSort = true)] public bool IsDraft { get; set; } diff --git a/SieveUnitTests/General.cs b/SieveUnitTests/General.cs index 2642bcd..ee9459b 100644 --- a/SieveUnitTests/General.cs +++ b/SieveUnitTests/General.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -28,24 +28,28 @@ namespace SieveUnitTests Id = 0, Title = "A", LikeCount = 100, - IsDraft = true + IsDraft = true, + CategoryId = null, }, new Post() { Id = 1, Title = "B", LikeCount = 50, - IsDraft = false + IsDraft = false, + CategoryId = 1, }, new Post() { Id = 2, Title = "C", - LikeCount = 0 + LikeCount = 0, + CategoryId = 1, }, new Post() { Id = 3, Title = "D", LikeCount = 3, - IsDraft = true + IsDraft = true, + CategoryId = 2, }, }.AsQueryable(); } @@ -103,6 +107,19 @@ namespace SieveUnitTests Assert.AreEqual(result.First().Id, 0); } + [TestMethod] + public void CanFilterNullableInts() + { + var model = new SieveModel() + { + Filters = "CategoryId==1" + }; + + var result = _processor.Apply(model, _posts); + + Assert.IsTrue(result.Count() == 2); + } + [TestMethod] public void EqualsDoesntFailWithNonStringTypes() { @@ -174,4 +191,4 @@ namespace SieveUnitTests Assert.AreEqual(3, entry.Id); } } -} \ No newline at end of file +}