question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Feature Request] Change Ref approach to expression-based

See original GitHub issue

Describe the solution you’d like

Akin to Hangfire and others, use LINQ expressions to let calls be made. This is more inline with how other .NET libraries work. We need to make sure status still work and we can extract return types in Task vs non-Task. This will also require updating docs and the blog post.

Also, as part of this effort, we need to modernize registration of activities and workflows. We probably need to:

  • Make definitions the only collections on worker options instead of the “additional”
  • Allow activities to also be registered by entire type/class that crawls looking for activity attributed
  • Abstract activity definition a tad to accept a form with arg types, return type, and a Func<object?, Task<object?[]?>> invoker. Put an InvokeAsync on it I guess. This is needed for dependency injection support. Would accept alternative activity class factory approach if we can think of it, though this is probably good enough. Can expose this all the way out to the AddActivity overload in the worker options.

Issue Analytics

  • State:closed
  • Created 5 months ago
  • Reactions:2
  • Comments:5 (4 by maintainers)

github_iconTop GitHub Comments

3reactions
cretzcommented, May 8, 2023

Ok, did some discussion with team. I am going to open a PR all-in on the expression approach (assuming I don’t hit snags with inference). We can evaluate that then. That means you actually can’t use the delegate/ref pattern if you wanted to (sorry!). So basically:

var temp1 = await Workflow.ExecuteActivityAsync(Foo.StaticWithArgAndResult, 123);

var temp2 = await Workflow.ExecuteActivityAsync(Foo.Ref.InstanceWithArgAndResult, 123);

Becomes:

var temp1 = await Workflow.ExecuteActivityAsync(() => Foo.StaticWithArgAndResult(123));

var temp2 = await Workflow.ExecuteActivityAsync((Foo foo) => foo.InstanceWithArgAndResult(123));

With the removal of the dependency, familiarity to .NET devs, and that we (really really) hate two ways of doing things, this just won out. But we’ll reevaluate during PR time if I hit any snags.

0reactions
cretzcommented, May 15, 2023

Ok, could use some more feedback here (x-posted to #dotnet-sdk on Slack). If I have:

await Workflow.ExecuteActivityAsync((Foo foo) => foo.SomeMethod(123 + 456));

I need to evaluate that argument. From what I read, that means I have to use an approach like https://stackoverflow.com/a/60002882 (basically create a lambda out of each argument and compile at call time) which is really slow. Even Compile(true) available in all but our oldest supported version (framework 4.6) is slow. If you had passed:

await Workflow.ExecuteActivityAsync((Foo foo) => foo.SomeMethod(579));

Then I could just use ConstantExpression.Value, but that is a non-obvious optimization to users to make sure they create their parameter outside the closure. And .NET can’t infer params for something like:

await Workflow.ExecuteActivityAsync((Foo foo) => foo.SomeMethod, 123 + 456);

Looking at hangfire and others, they have elaborate evaluators and will cache some known expression tree patterns resorting to Compile() as needed.

Options (not all are mutually exclusive):

  1. Abandon expression trees - pro: good performance, con: not as common in ecosystem
  2. Accept the performance hit of compiling non-constant expressions for arguments of workflows/activities on each invocation - pro: easy to read, con: bad performance
  3. Put in the effort for matching some common pattern types - pro: better performance in some cases, con: lots of code and probably won’t support common use case of record instantiation (also https://learn.microsoft.com/en-us/dotnet/csharp/advanced-topics/expression-trees/expression-trees-execution#execution-and-lifetimes discourages caching saying equality/identity checks are more expensive than compile)
  4. Disallow in-lambda evaluation (i.e. require all arguments be ConstantExpression provided outside the lambda) - pro: good performance, con: runtime only and encourages potentially uglier code
  5. Warn on in-lambda evaluation - pro: encourages good performance, con: noisy, non-obvious, encourages potentially uglier code
  6. Use something like https://github.com/dadhi/FastExpressionCompiler - pro: better performance, con: new dependency, some expressions don’t work
  7. Another option I am unaware of?

Thoughts? I didn’t put “allow expression trees and ref pattern” above because we just can’t reasonably have two approaches. I may do option 2 for now while implementing, but option 1 sure looks tempting and I am hoping I don’t have to maintain two approaches just so I can benchmark differences.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Expression-Based Access Control :: Spring Security
Expressions are evaluated with a "root object" as part of the evaluation context. Spring Security uses specific classes for web and method security ......
Read more >
Managing feature requests: A comprehensive guide
Discuss feature requests with your teammates and relevant stakeholders (and ensure they understand how you're making decisions) · How to share feedback. ·...
Read more >
How do I write a good feature request?
A feature request typically stems from a selfish need: Yes, something that you feel should change to benefit your usage of the site....
Read more >
Rename 'Feature Request' to 'Feedback'
A feature request is a change request or new requirement, to your existing product. Something where a customer says, what she would like...
Read more >
Showing the Feature Requests List
The following method can be used to display the Feature Requests page to your users. Once you display the page, the users can...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found