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

Feature: How to keep parameter input name of query #119

Closed
frankiDotNet opened this issue Nov 14, 2017 · 16 comments
Closed

Feature: How to keep parameter input name of query #119

frankiDotNet opened this issue Nov 14, 2017 · 16 comments
Labels

Comments

@frankiDotNet
Copy link

I want to parse a string to a LambdaExpression.
Following classes:

      public class Person
      {
         public String Name { get; set; }
         public String FirstName { get; set; }
         public Int32 Status { get; set; }
         public IList<Student> StudentList { get; set; }
      }
      public class Student
      {
         public String LastName { get; set; }
         public String FirstName { get; set; }
         public Int32 Age { get; set; }
      }

And the query:

var expressionString = "pe => (( pe.Name == \"Hallo\" ))";

Now if I parse the expression

var expression = DynamicExpressionParser.ParseLambda(typeof(Person), null, expressionString);

The 'pe' name for my input parameter is lost or replaced.

Is there a way to preserve the given name?

@frankiDotNet
Copy link
Author

This is a workaround, but it only helps if you know the input parameter name..

      /// <summary> Replace an input parameter in an expression with a predefined name.</summary>
      /// <exception cref="ArgumentNullException"> Thrown when one or more required arguments are null. </exception>
      /// <param name="itType">                    The main type from the dynamic class expression. </param>
      /// <param name="expression">                The expression. </param>
      /// <param name="parameterExpressionString"> The name of the input parameter to use for the expression parameters. </param>
      /// <returns> The dynamic expression with replaced input parameter name /></returns>
      public static string ReplaceInputParameter(Type itType, string expression, string parameterExpressionString)
      {
         if (String.IsNullOrEmpty(parameterExpressionString))
         {
            throw new ArgumentNullException("parameterExpressionString");
         }
         var parameters = new[] { Expression.Parameter(itType, parameterExpressionString) };

         var newExpression = DynamicExpressionParser.ParseLambda(parameters, null, expression, null);

         return newExpression.Body.ToString();
      }

Is there a way to extract only the input parameter name from a expression string?

@StefH
Copy link
Collaborator

StefH commented Nov 14, 2017

I see your point. I've to check if this can be fixed.

This issue is maybe a bit related to #110 ?

@frankiDotNet
Copy link
Author

Yes, this looks like to have the same solution.
Anyway is there a way to get the input parameter name of a string expression?

@StefH StefH added the feature label Nov 14, 2017
@wertzui
Copy link
Contributor

wertzui commented Oct 18, 2018

I have the same Problem and as far as I can see, it comes from the calls to
ParameterExpressionHelper.CreateParameterExpression(itType, string.Empty)
in DynamicExpressionParser.cs.

string.Empty effectively removes the name of the parameter.

var serialized = "s => s.Length == 7"
var exp = DynamicExpressionParser.ParseLambda<string, bool>(new ParsingConfig {}, true, serialized);
Console.WriteLine(exp.ToString()); // will output Param_0 => (Param_0.Length == 7)

Instead of giving an arbitrary name to the Parser, it should be the other way around, so the parser determines the name given the type of the parameter.

@StefH
Copy link
Collaborator

StefH commented Oct 19, 2018

The main question is : Why do you need this?

@wertzui
Copy link
Contributor

wertzui commented Oct 19, 2018

I'm getting the string as an MVC Controller parameter.
Then I'll parse the Expression.
Now if something goes wrong, I want to give the the user a nice error message with his original query.

When debugging it is way more readable if you have the original parameter name.

If you want to go from string to expression and back to string, you should be able to get your original string back. Currently this is not possible.

@StefH
Copy link
Collaborator

StefH commented Oct 19, 2018

@wertzui and @Franki1986
I've found a simple solution based on https://stackoverflow.com/questions/8540954/changing-parameter-name-in-a-lambdaexpression-just-for-display/8541464

Code is like:

public class Renamer : ExpressionVisitor
{
	private readonly string _newName;

	public Renamer(string newName)
	{
		_newName = newName;
	}
	
	public Expression Rename(Expression expression)
	{
		return Visit(expression);
	}

	protected override Expression VisitParameter(ParameterExpression node)
	{
		if (string.IsNullOrEmpty(node.Name))
		{
			return Expression.Parameter(node.Type, _newName);
		}
		else
		{
			return node;
		}
	}
}

And can be used like:

void Main()
{
	var expression = DynamicExpressionParser.ParseLambda<string, bool>(new ParsingConfig { }, true, "s => s.Length == 8");
	expression.ToString().Dump();
	var newExp = new Renamer("something").Rename(expression);
	newExp.ToString().Dump();

	"---".Dump();

	var expression1 = DynamicExpressionParser.ParseLambda<TestObject, bool>(new ParsingConfig { }, true, "dto => dto.X == 8");
	expression1.ToString().Dump();
	var newExp1 = new Renamer("dto").Rename(expression1);
	newExp1.ToString().Dump();
}

public class TestObject
{
	public int X { get; set; }

	public string Name { get; set; }
}

Output is:

Param_0 => (Param_0.Length == 8)
something => (something.Length == 8)
---
Param_0 => (Param_0.X == 8)
dto => (dto.X == 8)

The only thing what needs to be done is that the it type which is found by the ExpressionParser should be a public property on that class so it can be used in the Renamer.

@wertzui
Copy link
Contributor

wertzui commented Oct 19, 2018

For that to work I would neet to know the name of the Parameter upfront. But I do not know the name because I get the expression string from the consumers of my API.

I could probably work out the parameter name, but if I do not want to relay on error prone string inspection, I would have to parse the expression myself. And that is exactly what the Expressionparser does best ;)
So I think that would be the correct place for that code to live.

@StefH StefH changed the title How to keep parameter input name of query Feature: How to keep parameter input name of query Oct 21, 2018
@StefH
Copy link
Collaborator

StefH commented Oct 21, 2018

@wertzui and @Franki1986

I've build a solution, use this NuGet from a MyGet-feed:
1.0.9.1-ci-1468

What you need to do is:

var config = new ParsingConfig
{
    RenameParameterExpression = true
};

var exp = DynamicExpressionParser.ParseLambda<string, bool>(config, true, "s => s.Length == 7");
Console.WriteLine(exp.ToString());

Can you please try this?

@StefH
Copy link
Collaborator

StefH commented Nov 1, 2018

@wertzui and @Franki1986 can you please try?

@frankiDotNet
Copy link
Author

Sorry for delay. On monday I am back to office, then I will test it. Thanks for your work!

@frankiDotNet
Copy link
Author

Mhh.. in the myget package you provided there is no 'RenameParameterExpression' property...

@StefH
Copy link
Collaborator

StefH commented Nov 5, 2018

Sorry.
Please try version 1.0.9.1-ci-1468

@frankiDotNet
Copy link
Author

Ok, tried to get the package over myget, don't know if there is a problem with the source, but the current version is still ci-1369. Is there a way to get the specific version?

@frankiDotNet
Copy link
Author

Downloaded the latest master folder of 'System.Linq.Dynamic.Core' and tested ist this way.
Works fine!

@StefH
Copy link
Collaborator

StefH commented Nov 5, 2018

PM> Install-Package System.Linq.Dynamic.Core -Version 1.0.9.1-ci-1468 -Source https://www.myget.org/F/system-linq-dynamic-core/api/v3/index.json 

or

<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.9.1-ci-1468" />

Thanks for testing. I'll close this issue now.

@StefH StefH closed this as completed Nov 5, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

3 participants