Merge pull request #26 from zolrath/Compare-Nullable

Allow comparing Nullable via Expression.Constant
This commit is contained in:
Biarity 2018-05-28 17:59:34 +10:00 committed by GitHub
commit 4fea535586
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 42 additions and 14 deletions

View File

@ -142,7 +142,7 @@ namespace Sieve.Services
? converter.ConvertFrom(filterTerm.Value) ? converter.ConvertFrom(filterTerm.Value)
: Convert.ChangeType(filterTerm.Value, property.PropertyType); : Convert.ChangeType(filterTerm.Value, property.PropertyType);
Expression filterValue = GetClosureOverConstant(constantVal); Expression filterValue = GetClosureOverConstant(constantVal, property.PropertyType);
dynamic propertyValue = Expression.PropertyOrField(parameterExpression, property.Name); 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 // 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 // See https://github.com/aspnet/EntityFrameworkCore/issues/3361
private Expression GetClosureOverConstant<T>(T constant) // 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>(T constant, Type targetType)
{ {
Expression<Func<T>> closure = () => constant; return Expression.Constant(constant, targetType);
return closure.Body;
} }
private IQueryable<TEntity> ApplySorting<TEntity>( private IQueryable<TEntity> ApplySorting<TEntity>(

View File

@ -1,4 +1,4 @@
using System; using System;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using Sieve.Attributes; using Sieve.Attributes;
@ -14,6 +14,7 @@ namespace SieveTests.Entities
[Sieve(CanFilter = true, CanSort = true)] [Sieve(CanFilter = true, CanSort = true)]
public int LikeCount { get; set; } = new Random().Next(0, 1000); public int LikeCount { get; set; } = new Random().Next(0, 1000);
[Sieve(CanFilter = true, CanSort = true)] [Sieve(CanFilter = true, CanSort = true)]
public int CommentCount { get; set; } = new Random().Next(0, 1000); public int CommentCount { get; set; } = new Random().Next(0, 1000);
@ -23,5 +24,8 @@ namespace SieveTests.Entities
[Sieve(CanFilter = true, CanSort = true)] [Sieve(CanFilter = true, CanSort = true)]
[Column(TypeName = "datetime")] [Column(TypeName = "datetime")]
public DateTime DateLastViewed { get; set; } = DateTime.UtcNow; public DateTime DateLastViewed { get; set; } = DateTime.UtcNow;
[Sieve(CanFilter = true, CanSort = true)]
public int? CategoryId { get; set; } = new Random().Next(0, 4);
} }
} }

View File

@ -17,7 +17,8 @@ namespace SieveTests.Migrations
CommentCount = table.Column<int>(nullable: false), CommentCount = table.Column<int>(nullable: false),
DateCreated = table.Column<DateTimeOffset>(nullable: false), DateCreated = table.Column<DateTimeOffset>(nullable: false),
LikeCount = table.Column<int>(nullable: false), LikeCount = table.Column<int>(nullable: false),
Title = table.Column<string>(nullable: true) Title = table.Column<string>(nullable: true),
CategoryId = table.Column<int>(nullable: true)
}, },
constraints: table => table.PrimaryKey("PK_Posts", x => x.Id)); constraints: table => table.PrimaryKey("PK_Posts", x => x.Id));
} }

View File

@ -34,6 +34,8 @@ namespace SieveTests.Migrations
b.Property<int>("LikeCount"); b.Property<int>("LikeCount");
b.Property<int?>("CategoryId");
b.Property<string>("Title"); b.Property<string>("Title");
b.HasKey("Id"); b.HasKey("Id");

View File

@ -19,6 +19,9 @@ namespace SieveUnitTests.Entities
[Sieve(CanFilter = true, CanSort = true)] [Sieve(CanFilter = true, CanSort = true)]
public DateTimeOffset DateCreated { get; set; } = DateTimeOffset.UtcNow; 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)] [Sieve(CanFilter = true, CanSort = true)]
public bool IsDraft { get; set; } public bool IsDraft { get; set; }

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
@ -28,24 +28,28 @@ namespace SieveUnitTests
Id = 0, Id = 0,
Title = "A", Title = "A",
LikeCount = 100, LikeCount = 100,
IsDraft = true IsDraft = true,
CategoryId = null,
}, },
new Post() { new Post() {
Id = 1, Id = 1,
Title = "B", Title = "B",
LikeCount = 50, LikeCount = 50,
IsDraft = false IsDraft = false,
CategoryId = 1,
}, },
new Post() { new Post() {
Id = 2, Id = 2,
Title = "C", Title = "C",
LikeCount = 0 LikeCount = 0,
CategoryId = 1,
}, },
new Post() { new Post() {
Id = 3, Id = 3,
Title = "D", Title = "D",
LikeCount = 3, LikeCount = 3,
IsDraft = true IsDraft = true,
CategoryId = 2,
}, },
}.AsQueryable(); }.AsQueryable();
} }
@ -103,6 +107,19 @@ namespace SieveUnitTests
Assert.AreEqual(result.First().Id, 0); 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] [TestMethod]
public void EqualsDoesntFailWithNonStringTypes() public void EqualsDoesntFailWithNonStringTypes()
{ {
@ -174,4 +191,4 @@ namespace SieveUnitTests
Assert.AreEqual(3, entry.Id); Assert.AreEqual(3, entry.Id);
} }
} }
} }