5 ms·
The main reason I'm curious is because they write 'We solve this problem by allowing users to create instances of the class/interface without invoking anything
by 0xcoffee 3y ago
The main reason I'm curious is because they write
'We solve this problem by allowing users to create instances of the class/interface without invoking anything on it. For classes this is done via FormatterServices.GetUninitializedObject and for interfaces this is done via Castle Dynamic Proxy. This lets us "reference" (hence the name Ref) methods on the objects without actually instantiating them with side effects. Method calls should never be made on these objects (and most wouldn't work anyways).'
Which sounds like a lot of heavy lifting. It seems something like
public class WorkflowBuilder<T> where T : class
{
// Somehow workflow gets the real instance
private T Instance;
public async Task<TResult> ExecuteActivityAsync<TResult>(Func<T, Task<TResult>> func) => await func(Instance);
public async Task<TResult> ExecuteActivityAsync<TResult>(Func<Task<TResult>> func) => await func();
public async Task ExecuteActivityAsync(Func<Task> func) => await func();
public async Task ExecuteActivityAsync(Func<T, Task> func) => await func(Instance);
}
public record Purchase(string ItemID, string UserID);
public class PurchaseActivities
{
public static WorkflowBuilder<PurchaseActivities> OneClickBuyWorkflow => new WorkflowBuilder<PurchaseActivities>();
public async Task DoPurchaseAsync(Purchase purchase)
{
await OneClickBuyWorkflow.ExecuteActivityAsync(e => e.DoPurchaseAsync(purchase));
}
public static async Task DoPurchaseAsyncStatic(Purchase purchase)
{
await OneClickBuyWorkflow.ExecuteActivityAsync(() => DoPurchaseAsyncStatic(purchase));
}
}
Would pretty much achieve the same thing.
- kodablah 3y ago> Which sounds like a lot of heavy lifting How is `e` created for the `e => e.DoPurchaseAsync(purchase)` lambda? You're going to have to do that lifting anyways to create an instance for `e` that isn't really a usable instance. Unless you use source generators which we plan on doing. I think what you have there is a lot more heavy lifting. Also note that workflows and activities are unrelated to each other. Workflow can invoke any activities. The code you have is a bit confusing because `PurchaseActivities` should be completely unrelated to workflows.
- 0xcoffee 3y ago>You're going to have to do that lifting anyways to create an instance for `e` that isn't really a usable instance. But this should never happen, these is no reason to create an unusable instance. The real instance should be resolved in the same way however the current workflow resolves it.
- kodablah 3y agoActivities may actually run on completely different systems than where the workflow calls execute on. All "execute activity" is from a workflow perspective is telling Temporal server to execute an activity with a certain string name and serializable arguments. Everything else is sugar. So if you're gonna use a type caller-side to refer to the name and argument types, you can't instantiate it fully (its constructor may have side effects for when it really runs). You can jump through a bunch of hoops like requiring interfaces which is what some frameworks do. But in our case, we just decided to make it easy to reference the method without invoking it or its instance.
- 0xcoffee 3y agoLamba/Func/Expressions are doing exactly this in C#, there is no instantiation required. Creating a unusable Ref object is jumping through hoops. You can parse an expression to serialize it and run it on a different server etc See e.g. https://github.com/6bee/Remote.Linq https://github.com/6bee/Remote.Linq
- kodablah 3y agoHrmm, I will investigate using an expression tree for this (now's the time while it is alpha). I was hoping to avoid people having to create lambdas. I hope I don't run into overload ambiguity with the existing `ExecuteActivity` calls where you can just pass an existing method as Func<T, TResult> param. I will investigate this approach, thanks! Of course the "Ref" pattern is user-choice/suggested-pattern, it's not a requirement in any of our calls that just take simple delegates however you can create those delegates. So I may be able to work it in there.