Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for parsing Guid and string in the same condition #200

Merged
merged 23 commits into from
Aug 27, 2018

Conversation

OlegNadymov
Copy link
Contributor

@OlegNadymov OlegNadymov commented Aug 23, 2018

Plus fix for parsing expression with custom types with implicit convertions

@codecov
Copy link

codecov bot commented Aug 23, 2018

Codecov Report

Merging #200 into master will increase coverage by 0.02%.
The diff coverage is 100%.

Impacted file tree graph

@@            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
Impacted Files Coverage Δ
...ystem.Linq.Dynamic.Core/Parser/ExpressionParser.cs 78.22% <100%> (+0.16%) ⬆️
...ystem.Linq.Dynamic.Core/Parser/ExpressionHelper.cs 77.69% <100%> (+0.89%) ⬆️
...namic.Core/Parser/SupportedMethods/MethodFinder.cs 89.86% <0%> (-2.71%) ⬇️
src/System.Linq.Dynamic.Core/Parser/TypeHelper.cs 87.26% <0%> (-0.48%) ⬇️
...c/System.Linq.Dynamic.Core/Tokenizer/TextParser.cs 98.03% <0%> (+0.98%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b31d36f...dc9e24d. Read the comment docs.

@OlegNadymov
Copy link
Contributor Author

Hi @StefH !
Could you show me more information about failing unit test and code issues.
All unit test were passed on my local machine.

@StefH StefH self-requested a review August 23, 2018 16:21
@StefH
Copy link
Collaborator

StefH commented Aug 23, 2018

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)

Total tests: 143. Passed: 143. Failed: 0. Skipped: 0.
Test Run Successful.
Test execution time: 57.6756 Seconds
Build success

2] CodeFactor complains, therefore you see a red cross there.
Review and fix the issues from CodeFactor, see this page: https://www.codefactor.io/repository/github/stefh/system.linq.dynamic.core/pull/200

@@ -452,5 +454,39 @@ public void ParseLambda_With_InnerStringLiteral()
object result = del.DynamicInvoke(String.Empty);
Check.That(result).IsEqualTo(originalTrueValue);
}

[Fact]
public void ParseLambda_With_If_Guid_Null()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use // Arrange , // Act and // Assert flow (take a look at other tests).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was done!

[Fact]
public void ParseLambda_With_If_Guid_Null()
{
var objContext = new User();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

user

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was done!

public void ParseLambda_With_If_Guid_Null()
{
var objContext = new User();
var guidEmpty = Guid.Empty;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For clarity, I mostly / always use real definitions for simple types. So use Guid here.
Only for complex types, I use var.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was done!

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)})";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

string

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was done!

@@ -452,5 +454,39 @@ public void ParseLambda_With_InnerStringLiteral()
object result = del.DynamicInvoke(String.Empty);
Check.That(result).IsEqualTo(originalTrueValue);
}

[Fact]
public void ParseLambda_With_If_Guid_Null()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be Iif ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was done!

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)})";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)";

Copy link
Contributor Author

@OlegNadymov OlegNadymov Aug 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's correct expression:
string expressionText = $"iif(@0.Id == null, @0.Id == Guid.Parse(\"{someId}\"), Id == Id)";

@@ -452,5 +454,39 @@ public void ParseLambda_With_InnerStringLiteral()
object result = del.DynamicInvoke(String.Empty);
Check.That(result).IsEqualTo(originalTrueValue);
}

[Fact]
public void ParseLambda_With_If_Guid_Null()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better text like: Guid_Equals_Null ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was done!

}

[Fact]
public void ParseLambda_With_If_Null_Guid()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comments as defined on other method also apply here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was done!

//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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would think that removing this code would actually break unit-tests? Or do you say that this logic was wrong from the beginning?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

@OlegNadymov
Copy link
Contributor Author

@StefH,
I can't open the link https://www.codefactor.io/repository/github/stefh/system.linq.dynamic.core/pull/200.
I see the error:
"Access is denied. You do not have permission to access /repository/github//pull/200."

@OlegNadymov
Copy link
Contributor Author

@StefH all changes according to your review were done!

@StefH
Copy link
Collaborator

StefH commented Aug 24, 2018

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

@StefH
Copy link
Collaborator

StefH commented Aug 24, 2018

Small issues:

string expressionText = $"iif(@0.Id == \"{someId}\", Guid.Parse(\"{guidEmpty}\"), Guid.Parse(\"{anotherId}\"))";

var lambda = DynamicExpressionParser.ParseLambda(typeof(User), null, expressionText, user);

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not also check here on:

var boolLambda = lambda as Expression<Func<User, bool>>;
Assert.NotNull(boolLambda);

?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I've added it:

var guidLambda = lambda as Expression<Func<User, Guid>>;
Assert.NotNull(guidLambda);

@OlegNadymov
Copy link
Contributor Author

OlegNadymov commented Aug 24, 2018

Ok! Now I got that you mean about // Arrange , // Act and // Assert.
I've added these comments. But I'm not sure about something like this:

            // 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 Assert.NotNull is located inside Act. Is it ok?

@OlegNadymov
Copy link
Contributor Author

@StefH Do I need mention you in my each comment? Or you get notifications about new comments anyway.

@StefH
Copy link
Collaborator

StefH commented Aug 24, 2018

@OlegNadymov No need to mention me in the comments.

@StefH
Copy link
Collaborator

StefH commented Aug 24, 2018

As you can see here the first Assert.NotNull is located inside Act. Is it ok?

for now it's ok.
What I sometimes do is use an // Act 1 and // Assert 1 and // Act 2 and // Assert 2
Or just move all acts and asserts together.

@OlegNadymov
Copy link
Contributor Author

OlegNadymov commented Aug 24, 2018

Ok! I'll do it like you said with several Act1, Act2 in next PRs

@OlegNadymov
Copy link
Contributor Author

@StefH, what about this PR?

@StefH
Copy link
Collaborator

StefH commented Aug 25, 2018

I will merge this weekend and create new nuget.

@OlegNadymov
Copy link
Contributor Author

Ok! Then I'll add one more change for this PR.

@OlegNadymov
Copy link
Contributor Author

And one more change for LessGreater parsing.

@StefH
Copy link
Collaborator

StefH commented Aug 25, 2018

Maybe it's best to revert the last changes you did for LessGreater (unit test fail?). And create a new PR for this.

@OlegNadymov
Copy link
Contributor Author

I've fixed unit test. It was problem with custom static type

@OlegNadymov
Copy link
Contributor Author

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?

@StefH
Copy link
Collaborator

StefH commented Aug 25, 2018

OK, keep in same branch. I'll check the new code in some time...

@OlegNadymov
Copy link
Contributor Author

OK! Thank you! I'm looking forward to reviewing my changes. :)

Copy link
Collaborator

@StefH StefH left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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}\"))";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why introduce the StaticHelper class here if you only want to test the <> operator?

Copy link
Contributor Author

@OlegNadymov OlegNadymov Aug 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 !=.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 + \")\"";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 + \")\"";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to use $ here I think ? It's just a normal string.

}

[Fact]
public void ParseLambda_With_Less_Greater()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to ParseLambda_Operator_Less_Greater_With_Guids ?

@OlegNadymov
Copy link
Contributor Author

OlegNadymov commented Aug 26, 2018

All review's changes were done.

Copy link
Collaborator

@StefH StefH left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

@StefH StefH merged commit b78629f into zzzprojects:master Aug 27, 2018
@OlegNadymov
Copy link
Contributor Author

Thank you for merge! And please nuget.

@StefH
Copy link
Collaborator

StefH commented Aug 27, 2018

1.0.8.17 is added to NuGet

@OlegNadymov
Copy link
Contributor Author

@StefH Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants