Fix for parsing Guid and string in the same condition#200
Fix for parsing Guid and string in the same condition#200StefH merged 23 commits intozzzprojects:masterfrom
Conversation
Codecov Report
@@ Coverage Diff @@
## master #200 +/- ##
==========================================
+ Coverage 82.53% 82.55% +0.02%
==========================================
Files 37 37
Lines 3676 3681 +5
Branches 503 504 +1
==========================================
+ Hits 3034 3039 +5
- Misses 498 502 +4
+ Partials 144 140 -4
Continue to review full report at Codecov.
|
|
Hi @StefH ! |
|
Hello @OlegNadymov , Thanks for this PR, I'll check and review in some time. 1] Unit tests are fine (see https://ci.appveyor.com/project/StefH/system-linq-dynamic-core/build/1.0.8.643)
2] CodeFactor complains, therefore you see a red cross there. |
| } | ||
|
|
||
| [Fact] | ||
| public void ParseLambda_With_If_Guid_Null() |
There was a problem hiding this comment.
Please use // Arrange , // Act and // Assert flow (take a look at other tests).
| [Fact] | ||
| public void ParseLambda_With_If_Guid_Null() | ||
| { | ||
| var objContext = new User(); |
| public void ParseLambda_With_If_Guid_Null() | ||
| { | ||
| var objContext = new User(); | ||
| var guidEmpty = Guid.Empty; |
There was a problem hiding this comment.
For clarity, I mostly / always use real definitions for simple types. So use Guid here.
Only for complex types, I use var.
| var objContext = new User(); | ||
| var guidEmpty = Guid.Empty; | ||
| var someId = Guid.NewGuid(); | ||
| var expressionText = $"iif(@0.{nameof(objContext.Id)} == null, @0.{nameof(objContext.Id)} == Guid.Parse(\"{someId}\"), {nameof(objContext.Id)}={nameof(objContext.Id)})"; |
| } | ||
|
|
||
| [Fact] | ||
| public void ParseLambda_With_If_Guid_Null() |
| var objContext = new User(); | ||
| var guidEmpty = Guid.Empty; | ||
| var someId = Guid.NewGuid(); | ||
| var expressionText = $"iif(@0.{nameof(objContext.Id)} == null, @0.{nameof(objContext.Id)} == Guid.Parse(\"{someId}\"), {nameof(objContext.Id)}={nameof(objContext.Id)})"; |
There was a problem hiding this comment.
For readability I would just type the expressionText string as simple as possible, so just:
$"iif(@0.Id == null, @0.Id == Guid.Parse(\"{someId}\"), Id=Id)";And actually I wonder if this is ok?
Wouldn't the last statement set a value?
Like:
$"iif(@0.Id == null, @0.Id == Guid.Parse(\"{someId}\"), Id)";There was a problem hiding this comment.
No, it would not. This expression should have boolean result. So "Id == Id" is kind of true result. It's part of query for our ORM.
There was a problem hiding this comment.
It's correct expression:
string expressionText = $"iif(@0.Id == null, @0.Id == Guid.Parse(\"{someId}\"), Id == Id)";
| } | ||
|
|
||
| [Fact] | ||
| public void ParseLambda_With_If_Guid_Null() |
There was a problem hiding this comment.
Better text like: Guid_Equals_Null ?
| } | ||
|
|
||
| [Fact] | ||
| public void ParseLambda_With_If_Null_Guid() |
There was a problem hiding this comment.
Same comments as defined on other method also apply here.
| //void F(Guid x, string y); | ||
| //void F(Guid? x, string y); | ||
|
|
||
| // Disabled 2 lines below according to unit test ParseLambda_With_If_Null_Guid |
There was a problem hiding this comment.
I would think that removing this code would actually break unit-tests? Or do you say that this logic was wrong from the beginning?
There was a problem hiding this comment.
In my opinion logic was wrong from the start.
It's like the issue #19
I've added unit test ParseLambda_With_Guid_Equals_String for demonstrate that Guid plus String works without these lines (at least in condition part).
|
@StefH, |
|
@StefH all changes according to your review were done! |
|
CodeFactor found multiple issues last seen at 6213fd1: An opening curly bracket must not be followed by a blank line.test\System.Linq.Dynamic.Core.Tests\DynamicExpressionParserTests.cs:13 The code must not contain multiple blank lines in a row.test\System.Linq.Dynamic.Core.Tests\DynamicExpressionParserTests.cs:15 |
|
Small issues:
|
| string expressionText = $"iif(@0.Id == \"{someId}\", Guid.Parse(\"{guidEmpty}\"), Guid.Parse(\"{anotherId}\"))"; | ||
|
|
||
| var lambda = DynamicExpressionParser.ParseLambda(typeof(User), null, expressionText, user); | ||
|
|
There was a problem hiding this comment.
Why not also check here on:
var boolLambda = lambda as Expression<Func<User, bool>>;
Assert.NotNull(boolLambda);?
There was a problem hiding this comment.
Yes. I've added it:
var guidLambda = lambda as Expression<Func<User, Guid>>;
Assert.NotNull(guidLambda);
|
Ok! Now I got that you mean about // Arrange , // Act and // Assert. // Act
var lambda = DynamicExpressionParser.ParseLambda(typeof(User), null, expressionText, user);
var guidLambda = lambda as Expression<Func<User, Guid>>;
Assert.NotNull(guidLambda);
var del = lambda.Compile();
Guid result = (Guid) del.DynamicInvoke(user);
// Assert
Assert.Equal(guidEmpty, result);As you can see here the first |
|
@StefH Do I need mention you in my each comment? Or you get notifications about new comments anyway. |
|
@OlegNadymov No need to mention me in the comments. |
for now it's ok. |
|
Ok! I'll do it like you said with several Act1, Act2 in next PRs |
|
@StefH, what about this PR? |
|
I will merge this weekend and create new nuget. |
|
Ok! Then I'll add one more change for this PR. |
|
And one more change for LessGreater parsing. |
|
Maybe it's best to revert the last changes you did for LessGreater (unit test fail?). And create a new PR for this. |
|
I've fixed unit test. It was problem with custom static type |
|
Do you want that I create new PR? Or I can leave it here. In case with new PR, I need to create new branch for new PR because the current branch is using for this open PR. Do I understand correctly? |
|
OK, keep in same branch. I'll check the new code in some time... |
|
OK! Thank you! I'm looking forward to reviewing my changes. :) |
StefH
left a comment
There was a problem hiding this comment.
I've added some comments, please take a look.
And I think I need to restructure the test class a bit in the future to make clear what's tested.
| var user = new User(); | ||
| user.Id = someId; | ||
| Guid guidEmpty = Guid.Empty; | ||
| string expressionText = $"iif(@0.Id == StaticHelper.GetGuid(\"name\"), Guid.Parse(\"{guidEmpty}\"), Guid.Parse(\"{anotherId}\"))"; |
There was a problem hiding this comment.
Why introduce the StaticHelper class here if you only want to test the <> operator?
There was a problem hiding this comment.
This expression with Guid constant instead of Guid method was worked correctly before these changes. Therefore for represent bug with method I've introduced StaticHelper with GetGuid method. Or do you mean why I didn't create this class like as local class?
There was a problem hiding this comment.
I thought that the <> was not working at all. But I understand from your comment that this was only not working in specific flows?
In that case your test looks good.
Maybe you could add some more comments to explain this?
There was a problem hiding this comment.
The <> was working with constant only.
Your ExpressionParser has this condition (line 494):
else if ((constantExpr = right as ConstantExpression) != null && constantExpr.Value is string && (typeConverter = TypeConverterFactory.GetConverter(left.Type)) != null)
{
right = Expression.Constant(typeConverter.ConvertFromInvariantString((string)constantExpr.Value), left.Type);
}
Therefore if right epxression is ConstantExpression then the <> will be working.
But for non-constant and for different types (e.g. Guid and Guid?) will be executed this code (line 531):
CheckAndPromoteOperands(isEquality ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures), op.Text, ref left, ref right, op.Pos);
And before my changes the variable isEquality was false. Therefore into method CheckAndPromoteOperands passed argument type as typeof(IRelationalSignatures). But type typeof(IRelationalSignatures) doesn't have conversion between Guid and Guid?.
Moreover for the != this case was working because for the != the variable isEquality was true:
bool isEquality = op.Id == TokenId.Equal || op.Id == TokenId.DoubleEqual || op.Id == TokenId.ExclamationEqual;
TokenId.ExclamationEqual is equal to the !=.
There was a problem hiding this comment.
OK, thanks for the detailed explanation.
| void F(string x, Guid y); | ||
| void F(string x, Guid? y); | ||
|
|
||
| // Disabled 2 lines below according to unit test ParseLambda_With_Guid_Equals_Null |
There was a problem hiding this comment.
Change comment to:
// Disabled 4 lines below because of : https://github.com/StefH/System.Linq.Dynamic.Core/pull/200
//void F(Guid x, string y);
//void F(Guid? x, string y);
//void F(string x, Guid y);
//void F(string x, Guid? y);| string name = "name1"; | ||
| string note = "note1"; | ||
| var textHolder = new TextHolder(name, note); | ||
| string expressionText = $"Name + \" (\" + Note + \")\""; |
There was a problem hiding this comment.
No need to use $ here I think ? It's just a normal string.
| string name = "name1"; | ||
| string note = "note1"; | ||
| var textHolder = new TextHolder(name, note); | ||
| string expressionText = $"Note + \" (\" + Name + \")\""; |
There was a problem hiding this comment.
No need to use $ here I think ? It's just a normal string.
| } | ||
|
|
||
| [Fact] | ||
| public void ParseLambda_With_Less_Greater() |
There was a problem hiding this comment.
rename to ParseLambda_Operator_Less_Greater_With_Guids ?
|
All review's changes were done. |
|
Thank you for merge! And please nuget. |
|
1.0.8.17 is added to NuGet |
|
@StefH Thanks! |
Plus fix for parsing expression with custom types with implicit convertions