Skip to content

Conversation

@alexeypj
Copy link

@alexeypj alexeypj commented Apr 3, 2024

This request contains a solution to problem #71 and a test case of use. The request was prepared with the support of P9avel.

Copy link

@P9avel P9avel left a comment

Choose a reason for hiding this comment

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

Please to merge

@P9avel
Copy link

P9avel commented Apr 3, 2024

Please to merge this PR

@alexeypj
Copy link
Author

@koenbeuk Please merge this PR

Copy link
Owner

@koenbeuk koenbeuk left a comment

Choose a reason for hiding this comment

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

Thanks for this PR, Apart from some minor changes, I did propose a workaround for the added unit test. functionality that would work today. Can you let me know in what scenarios my proposed workaround would not support and why we would need this PR merged?

Thanks again!

{
public ProjectableAttribute() { }

public ProjectableAttribute(string useMemberBody, object memberBodyParameterValue)
Copy link
Owner

Choose a reason for hiding this comment

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

A constructor would be an aggressive change as this would limit our ability in the future to add additional constructor arguments. Just having a property would be sufficient. Can this constructor be dropped?

Copy link
Author

Choose a reason for hiding this comment

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

Comment accepted

/// <summary>
/// Parameters values for UseMemberBody.
/// </summary>
public object[]? MemberBodyParameterValues { get; set; }
Copy link
Owner

Choose a reason for hiding this comment

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

I would prefer the name UseMemberBodyArguments

Copy link
Author

Choose a reason for hiding this comment

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

Comment accepted

if (reflectedExpression.Parameters.Count > 1)
{
var projectableAttribute = nodeMember.GetCustomAttribute<ProjectableAttribute>(false)!;
foreach (var prm in reflectedExpression.Parameters.Skip(1).Select((Parameter, Index) => new { Parameter, Index }))
Copy link
Owner

Choose a reason for hiding this comment

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

I would prefer calling this parameterWithIndex to stay in sync with the naming conventions used throughout the rest of the code base

Copy link
Author

Choose a reason for hiding this comment

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

Comment accepted

var projectableAttribute = projectableMemberInfo.GetCustomAttribute<ProjectableAttribute>()
?? throw new InvalidOperationException("Expected member to have a Projectable attribute. None found");

var expression = GetExpressionFromGeneratedType(projectableMemberInfo);
Copy link
Owner

Choose a reason for hiding this comment

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

There should be no need to call this if projectableAttribute.UseMemberBody is not null

Copy link
Author

Choose a reason for hiding this comment

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

Comment accepted


if (expression is null && projectableAttribute.UseMemberBody is not null && projectableAttribute.MemberBodyParameterValues is null)
{
expression = GetExpressionFromGeneratedType(projectableMemberInfo, true, projectableAttribute.UseMemberBody);
Copy link
Owner

Choose a reason for hiding this comment

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

The result will be overwritten by the call on line 24

Copy link
Author

Choose a reason for hiding this comment

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

I think differently that when calling line 20 the expression will not be empty

public int Id { get; set; }
public List<OrderItem> OrderItem { get; set; } = new List<OrderItem>();

[Projectable(nameof(GetOrderItemNameExpression), 1)]
Copy link
Owner

Choose a reason for hiding this comment

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

This example confuses me, could you not just have done:

[Projectable(UseMemberBody = nameof(GetOrderItemNameInnerExpressionWithArgument))]
public string FirstOrderPropName => GetOrderItemName(1);

private static Expression<Func<Order, string>> GetOrderItemNameInnerExpressionWithArgument()
   => new MyParameterExpressionVisitor(parameter: "id", value: 1).Visit(GetOrderItemNameInnerExpression());

private static Expression<Func<Order, int, string>> GetOrderItemNameInnerExpression()
    => (@this, id) => @this.OrderItem
        .Where(av => av.Id == id)
        .Select(av => av.Name)
        .FirstOrDefault() ?? string.Empty;

Where MyParameterExpressionVisitor swaps out the parameter named id for a constant value.

Copy link
Author

Choose a reason for hiding this comment

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

I'm also confused, but we generate this call in runtime and therefore it is convenient for us to pass the value of the parameters through the attribute.

@P9avel
Copy link

P9avel commented May 24, 2024

Hi, @koenbeuk When you planning to merge this PR?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants