Skip to content

Commit 4275edd

Browse files
committed
NotEqual filter not working with DateTime #19
1 parent fb92c89 commit 4275edd

File tree

3 files changed

+99
-43
lines changed

3 files changed

+99
-43
lines changed

src/System.Linq.Dynamic.Core/ExpressionParser.cs

+59-41
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,13 @@ interface IEqualitySignatures : IRelationalSignatures
9696
{
9797
void F(bool x, bool y);
9898
void F(bool? x, bool? y);
99-
void F(DateTime x, string y);
100-
void F(DateTime? x, string y);
101-
void F(string x, DateTime y);
102-
void F(string x, DateTime? y);
99+
100+
// Disabled 4 lines below because of : https://github.com/StefH/System.Linq.Dynamic.Core/issues/19
101+
//void F(DateTime x, string y);
102+
//void F(DateTime? x, string y);
103+
//void F(string x, DateTime y);
104+
//void F(string x, DateTime? y);
105+
103106
void F(Guid x, Guid y);
104107
void F(Guid? x, Guid? y);
105108
void F(Guid x, string y);
@@ -614,8 +617,7 @@ Expression ParseComparison()
614617
Token op = _token;
615618
NextToken();
616619
Expression right = ParseShift();
617-
bool isEquality = op.id == TokenId.Equal || op.id == TokenId.DoubleEqual ||
618-
op.id == TokenId.ExclamationEqual || op.id == TokenId.LessGreater;
620+
bool isEquality = op.id == TokenId.Equal || op.id == TokenId.DoubleEqual || op.id == TokenId.ExclamationEqual;
619621
if (isEquality && ((!left.Type.GetTypeInfo().IsValueType && !right.Type.GetTypeInfo().IsValueType) || (left.Type == typeof(Guid) && right.Type == typeof(Guid))))
620622
{
621623
if (left.Type != right.Type)
@@ -1114,6 +1116,7 @@ Expression GenerateConditional(Expression test, Expression expr1, Expression exp
11141116
throw ParseError(errorPos, Res.NeitherTypeConvertsToOther, type1, type2);
11151117
}
11161118
}
1119+
11171120
return Expression.Condition(test, expr1, expr2);
11181121
}
11191122

@@ -1587,17 +1590,17 @@ static bool IsEnumType(Type type)
15871590

15881591
void CheckAndPromoteOperand(Type signatures, string opName, ref Expression expr, int errorPos)
15891592
{
1590-
Expression[] args = new[] { expr };
1593+
Expression[] args = { expr };
15911594
MethodBase method;
15921595
if (FindMethod(signatures, "F", false, args, out method) != 1)
1593-
throw ParseError(errorPos, Res.IncompatibleOperand,
1594-
opName, GetTypeName(args[0].Type));
1596+
throw ParseError(errorPos, Res.IncompatibleOperand, opName, GetTypeName(args[0].Type));
15951597
expr = args[0];
15961598
}
15971599

15981600
void CheckAndPromoteOperands(Type signatures, string opName, ref Expression left, ref Expression right, int errorPos)
15991601
{
1600-
Expression[] args = new[] { left, right };
1602+
Expression[] args = { left, right };
1603+
16011604
MethodBase method;
16021605
if (FindMethod(signatures, "F", false, args, out method) != 1)
16031606
throw IncompatibleOperandsError(opName, left, right, errorPos);
@@ -1636,6 +1639,22 @@ static MemberInfo FindPropertyOrField(Type type, string memberName, bool staticA
16361639
#endif
16371640
}
16381641

1642+
/*
1643+
*
1644+
BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly |
1645+
(staticAccess ? BindingFlags.Static : BindingFlags.Instance);
1646+
foreach (Type t in SelfAndBaseTypes(type))
1647+
{
1648+
MemberInfo[] members = t.FindMembers(MemberTypes.Method,
1649+
flags, Type.FilterNameIgnoreCase, methodName);
1650+
int count = FindBestMethod(members.Cast<MethodBase>(), args, out method);
1651+
if (count != 0) return count;
1652+
}
1653+
method = null;
1654+
return 0;
1655+
1656+
*/
1657+
16391658
int FindMethod(Type type, string methodName, bool staticAccess, Expression[] args, out MethodBase method)
16401659
{
16411660
#if !(NETFX_CORE || DNXCORE50 || DOTNET5_4 || WINDOWS_APP || DOTNET5_1 || UAP10_0 || NETSTANDARD)
@@ -1651,13 +1670,14 @@ int FindMethod(Type type, string methodName, bool staticAccess, Expression[] arg
16511670
method = null;
16521671
return 0;
16531672
#else
1654-
method = null;
16551673
foreach (Type t in SelfAndBaseTypes(type))
16561674
{
1657-
var methods = t.GetTypeInfo().DeclaredMethods.Where(x => (x.IsStatic || !staticAccess) && x.Name.ToLowerInvariant() == methodName.ToLowerInvariant());
1675+
MethodInfo[] methods = t.GetTypeInfo().DeclaredMethods.Where(x => (x.IsStatic || !staticAccess) && x.Name.ToLowerInvariant() == methodName.ToLowerInvariant()).ToArray();
16581676
int count = FindBestMethod(methods, args, out method);
16591677
if (count != 0) return count;
16601678
}
1679+
1680+
method = null;
16611681
return 0;
16621682
#endif
16631683
}
@@ -1738,6 +1758,7 @@ int FindBestMethod(IEnumerable<MethodBase> methods, Expression[] args, out Metho
17381758
Where(m => applicable.All(n => m == n || IsBetterThan(args, m, n))).
17391759
ToArray();
17401760
}
1761+
17411762
if (applicable.Length == 1)
17421763
{
17431764
MethodData md = applicable[0];
@@ -1748,6 +1769,7 @@ int FindBestMethod(IEnumerable<MethodBase> methods, Expression[] args, out Metho
17481769
{
17491770
method = null;
17501771
}
1772+
17511773
return applicable.Length;
17521774
}
17531775

@@ -2195,67 +2217,64 @@ static Expression GenerateGreaterThan(Expression left, Expression right)
21952217
{
21962218
if (left.Type == typeof(string))
21972219
{
2198-
return Expression.GreaterThan(
2199-
GenerateStaticMethodCall("Compare", left, right),
2200-
Expression.Constant(0)
2201-
);
2220+
return Expression.GreaterThan(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0));
22022221
}
2203-
else if (left.Type.GetTypeInfo().IsEnum || right.Type.GetTypeInfo().IsEnum)
2222+
2223+
if (left.Type.GetTypeInfo().IsEnum || right.Type.GetTypeInfo().IsEnum)
22042224
{
2205-
return Expression.GreaterThan(left.Type.GetTypeInfo().IsEnum ? Expression.Convert(left, Enum.GetUnderlyingType(left.Type)) : left,
2206-
right.Type.GetTypeInfo().IsEnum ? Expression.Convert(right, Enum.GetUnderlyingType(right.Type)) : right);
2225+
var leftPart = left.Type.GetTypeInfo().IsEnum ? Expression.Convert(left, Enum.GetUnderlyingType(left.Type)) : left;
2226+
var rightPart = right.Type.GetTypeInfo().IsEnum ? Expression.Convert(right, Enum.GetUnderlyingType(right.Type)) : right;
2227+
return Expression.GreaterThan(leftPart, rightPart);
22072228
}
2229+
22082230
return Expression.GreaterThan(left, right);
22092231
}
22102232

22112233
static Expression GenerateGreaterThanEqual(Expression left, Expression right)
22122234
{
22132235
if (left.Type == typeof(string))
22142236
{
2215-
return Expression.GreaterThanOrEqual(
2216-
GenerateStaticMethodCall("Compare", left, right),
2217-
Expression.Constant(0)
2218-
);
2237+
return Expression.GreaterThanOrEqual(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0));
22192238
}
2220-
else if (left.Type.GetTypeInfo().IsEnum || right.Type.GetTypeInfo().IsEnum)
2239+
2240+
if (left.Type.GetTypeInfo().IsEnum || right.Type.GetTypeInfo().IsEnum)
22212241
{
22222242
return Expression.GreaterThanOrEqual(left.Type.GetTypeInfo().IsEnum ? Expression.Convert(left, Enum.GetUnderlyingType(left.Type)) : left,
22232243
right.Type.GetTypeInfo().IsEnum ? Expression.Convert(right, Enum.GetUnderlyingType(right.Type)) : right);
22242244
}
2245+
22252246
return Expression.GreaterThanOrEqual(left, right);
22262247
}
22272248

22282249
static Expression GenerateLessThan(Expression left, Expression right)
22292250
{
22302251
if (left.Type == typeof(string))
22312252
{
2232-
return Expression.LessThan(
2233-
GenerateStaticMethodCall("Compare", left, right),
2234-
Expression.Constant(0)
2235-
);
2253+
return Expression.LessThan(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0));
22362254
}
2237-
else if (left.Type.GetTypeInfo().IsEnum || right.Type.GetTypeInfo().IsEnum)
2255+
2256+
if (left.Type.GetTypeInfo().IsEnum || right.Type.GetTypeInfo().IsEnum)
22382257
{
22392258
return Expression.LessThan(left.Type.GetTypeInfo().IsEnum ? Expression.Convert(left, Enum.GetUnderlyingType(left.Type)) : left,
22402259
right.Type.GetTypeInfo().IsEnum ? Expression.Convert(right, Enum.GetUnderlyingType(right.Type)) : right);
22412260
}
2261+
22422262
return Expression.LessThan(left, right);
22432263
}
22442264

22452265
static Expression GenerateLessThanEqual(Expression left, Expression right)
22462266
{
22472267
if (left.Type == typeof(string))
22482268
{
2249-
return Expression.LessThanOrEqual(
2250-
GenerateStaticMethodCall("Compare", left, right),
2251-
Expression.Constant(0)
2252-
);
2269+
return Expression.LessThanOrEqual(GenerateStaticMethodCall("Compare", left, right), Expression.Constant(0));
22532270
}
2254-
else if (left.Type.GetTypeInfo().IsEnum || right.Type.GetTypeInfo().IsEnum)
2271+
2272+
if (left.Type.GetTypeInfo().IsEnum || right.Type.GetTypeInfo().IsEnum)
22552273
{
22562274
return Expression.LessThanOrEqual(left.Type.GetTypeInfo().IsEnum ? Expression.Convert(left, Enum.GetUnderlyingType(left.Type)) : left,
22572275
right.Type.GetTypeInfo().IsEnum ? Expression.Convert(right, Enum.GetUnderlyingType(right.Type)) : right);
22582276
}
2277+
22592278
return Expression.LessThanOrEqual(left, right);
22602279
}
22612280

@@ -2276,10 +2295,7 @@ static Expression GenerateSubtract(Expression left, Expression right)
22762295
static Expression GenerateStringConcat(Expression left, Expression right)
22772296
{
22782297
// Allow concat String with something else
2279-
return Expression.Call(
2280-
null,
2281-
typeof(string).GetMethod("Concat", new[] { left.Type, right.Type }),
2282-
new[] { left, right });
2298+
return Expression.Call(null, typeof(string).GetMethod("Concat", new[] { left.Type, right.Type }), new[] { left, right });
22832299
}
22842300

22852301
static MethodInfo GetStaticMethod(string methodName, Expression left, Expression right)
@@ -2292,10 +2308,9 @@ static Expression GenerateStaticMethodCall(string methodName, Expression left, E
22922308
return Expression.Call(null, GetStaticMethod(methodName, left, right), new[] { left, right });
22932309
}
22942310

2295-
22962311
static void OptimizeForEqualityIfPossible(ref Expression left, ref Expression right)
22972312
{
2298-
// The goal here is to provide the way to convert some types from the string form in a way that is compatible with Linq-to-Entities.
2313+
// The goal here is to provide the way to convert some types from the string form in a way that is compatible with Linq to Entities.
22992314
//
23002315
// The Expression.Call(typeof(Guid).GetMethod("Parse"), right); does the job only for Linq to Object but Linq to Entities.
23012316
//
@@ -2304,14 +2319,17 @@ static void OptimizeForEqualityIfPossible(ref Expression left, ref Expression ri
23042319
{
23052320
right = OptimizeStringForEqualityIfPossible((string)((ConstantExpression)right).Value, leftType) ?? right;
23062321
}
2322+
23072323
if (leftType == typeof(string) && left.NodeType == ExpressionType.Constant)
23082324
{
23092325
left = OptimizeStringForEqualityIfPossible((string)((ConstantExpression)left).Value, rightType) ?? left;
23102326
}
23112327
}
2328+
23122329
static Expression OptimizeStringForEqualityIfPossible(string text, Type type)
23132330
{
23142331
DateTime dateTime;
2332+
23152333
if (type == typeof(DateTime) &&
23162334
DateTime.TryParse(text, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
23172335
return Expression.Constant(dateTime, typeof(DateTime));
@@ -2681,4 +2699,4 @@ internal static void ResetDynamicLinqTypes()
26812699
_keywords = null;
26822700
}
26832701
}
2684-
}
2702+
}

test/System.Linq.Dynamic.Core.Tests/DynamicTests.cs

+34-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Collections;
22
using System.Collections.Generic;
33
using System.Linq.Dynamic.Core.Exceptions;
4+
using System.Linq.Dynamic.Core.Tests.Helpers.Entities;
45
using System.Linq.Dynamic.Core.Tests.Helpers.Models;
56
using Linq.PropertyTranslator.Core;
67
using QueryInterceptor.Core;
@@ -10,21 +11,52 @@ namespace System.Linq.Dynamic.Core.Tests
1011
{
1112
public class DynamicTests
1213
{
14+
[Fact]
15+
// https://github.com/StefH/System.Linq.Dynamic.Core/issues/19
16+
public void Where_DateTime_NotEquals_Null()
17+
{
18+
//Arrange
19+
IQueryable<Post> queryable = new[] { new Post() }.AsQueryable();
20+
21+
//Act
22+
var expected = queryable.Where(p => p.PostDate != null).ToArray();
23+
var result1 = queryable.Where("PostDate != null").ToArray();
24+
var result2 = queryable.Where("null != PostDate").ToArray();
25+
26+
//Assert
27+
Assert.Equal(expected, result1);
28+
Assert.Equal(expected, result2);
29+
}
30+
31+
[Fact]
32+
public void Where_DateTime_Equals_Null()
33+
{
34+
//Arrange
35+
IQueryable<Post> queryable = new[] { new Post() }.AsQueryable();
36+
37+
//Act
38+
var expected = queryable.Where(p => p.PostDate == null).ToArray();
39+
var result1 = queryable.Where("PostDate == null").ToArray();
40+
var result2 = queryable.Where("null == PostDate").ToArray();
41+
42+
//Assert
43+
Assert.Equal(expected, result1);
44+
Assert.Equal(expected, result2);
45+
}
46+
1347
[Fact]
1448
public void Where()
1549
{
1650
//Arrange
1751
var testList = User.GenerateSampleModels(100, allowNullableProfiles: true);
1852
var qry = testList.AsQueryable();
1953

20-
2154
//Act
2255
var userById = qry.Where("Id=@0", testList[10].Id);
2356
var userByUserName = qry.Where("UserName=\"User5\"");
2457
var nullProfileCount = qry.Where("Profile=null");
2558
var userByFirstName = qry.Where("Profile!=null && Profile.FirstName=@0", testList[1].Profile.FirstName);
2659

27-
2860
//Assert
2961
Assert.Equal(testList[10], userById.Single());
3062
Assert.Equal(testList[5], userByUserName.Single());

test/System.Linq.Dynamic.Core.Tests/project.json

+6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@
2626
"System.Linq.Parallel": "4.0.1-rc2-*",
2727
"System.Threading.Tasks": "4.0.11-rc2-*",
2828

29+
30+
31+
"Microsoft.EntityFrameworkCore.Commands": "1.0.0-rc2-*",
32+
33+
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-rc2-*",
34+
2935
"Microsoft.EntityFrameworkCore": "1.0.0-rc2-*",
3036
"Microsoft.EntityFrameworkCore.Sqlite": "1.0.0-rc2-*",
3137
"Microsoft.EntityFrameworkCore.SqlServer": "1.0.0-rc2-*"

0 commit comments

Comments
 (0)