@@ -312,4 +312,227 @@ await TestHelpers.RunTestAsync<DurableTaskSourceGenerator>(
312312 expectedOutput ,
313313 isDurableFunctions : true ) ;
314314 }
315+
316+ /// <summary>
317+ /// Verifies that using the class-based syntax for authoring entities generates
318+ /// <see cref="EntityTriggerAttribute"/> function triggers for Azure Functions.
319+ /// </summary>
320+ /// <param name="stateType">The entity state type.</param>
321+ [ Theory ]
322+ [ InlineData ( "int" ) ]
323+ [ InlineData ( "string" ) ]
324+ public async Task Entities_ClassBasedSyntax ( string stateType )
325+ {
326+ string code = $@ "
327+ #nullable enable
328+ using System.Threading.Tasks;
329+ using Microsoft.DurableTask;
330+ using Microsoft.DurableTask.Entities;
331+
332+ namespace MyNS
333+ {{
334+ [DurableTask(nameof(MyEntity))]
335+ public class MyEntity : TaskEntity<{ stateType } >
336+ {{
337+ public { stateType } Get() => this.State;
338+ }}
339+ }}" ;
340+
341+ string expectedOutput = TestHelpers . WrapAndFormat (
342+ GeneratedClassName ,
343+ methodList : @"
344+ [Function(nameof(MyEntity))]
345+ public static Task MyEntity([EntityTrigger] TaskEntityDispatcher dispatcher)
346+ {
347+ return dispatcher.DispatchAsync<MyNS.MyEntity>();
348+ }" ,
349+ isDurableFunctions : true ) ;
350+
351+ await TestHelpers . RunTestAsync < DurableTaskSourceGenerator > (
352+ GeneratedFileName ,
353+ code ,
354+ expectedOutput ,
355+ isDurableFunctions : true ) ;
356+ }
357+
358+ /// <summary>
359+ /// Verifies that using the class-based syntax for authoring entities with inheritance generates
360+ /// <see cref="EntityTriggerAttribute"/> function triggers for Azure Functions.
361+ /// </summary>
362+ /// <param name="stateType">The entity state type.</param>
363+ [ Theory ]
364+ [ InlineData ( "int" ) ]
365+ [ InlineData ( "string" ) ]
366+ public async Task Entities_ClassBasedSyntax_Inheritance ( string stateType )
367+ {
368+ string code = $@ "
369+ #nullable enable
370+ using System.Threading.Tasks;
371+ using Microsoft.DurableTask;
372+ using Microsoft.DurableTask.Entities;
373+
374+ namespace MyNS
375+ {{
376+ [DurableTask]
377+ public class MyEntity : MyEntityBase
378+ {{
379+ public override { stateType } Get() => this.State;
380+ }}
381+
382+ public abstract class MyEntityBase : TaskEntity<{ stateType } >
383+ {{
384+ public abstract { stateType } Get();
385+ }}
386+ }}" ;
387+
388+ string expectedOutput = TestHelpers . WrapAndFormat (
389+ GeneratedClassName ,
390+ methodList : @"
391+ [Function(nameof(MyEntity))]
392+ public static Task MyEntity([EntityTrigger] TaskEntityDispatcher dispatcher)
393+ {
394+ return dispatcher.DispatchAsync<MyNS.MyEntity>();
395+ }" ,
396+ isDurableFunctions : true ) ;
397+
398+ await TestHelpers . RunTestAsync < DurableTaskSourceGenerator > (
399+ GeneratedFileName ,
400+ code ,
401+ expectedOutput ,
402+ isDurableFunctions : true ) ;
403+ }
404+
405+ /// <summary>
406+ /// Verifies that using the class-based syntax for authoring entities with custom state types generates
407+ /// <see cref="EntityTriggerAttribute"/> function triggers for Azure Functions.
408+ /// </summary>
409+ [ Fact ]
410+ public async Task Entities_ClassBasedSyntax_CustomStateType ( )
411+ {
412+ string code = @"
413+ #nullable enable
414+ using System.Threading.Tasks;
415+ using Microsoft.DurableTask;
416+ using Microsoft.DurableTask.Entities;
417+
418+ namespace MyNS
419+ {
420+ public class MyState
421+ {
422+ public int Value { get; set; }
423+ }
424+
425+ [DurableTask(nameof(MyEntity))]
426+ public class MyEntity : TaskEntity<MyState>
427+ {
428+ public MyState Get() => this.State;
429+ }
430+ }" ;
431+
432+ string expectedOutput = TestHelpers . WrapAndFormat (
433+ GeneratedClassName ,
434+ methodList : @"
435+ [Function(nameof(MyEntity))]
436+ public static Task MyEntity([EntityTrigger] TaskEntityDispatcher dispatcher)
437+ {
438+ return dispatcher.DispatchAsync<MyNS.MyEntity>();
439+ }" ,
440+ isDurableFunctions : true ) ;
441+
442+ await TestHelpers . RunTestAsync < DurableTaskSourceGenerator > (
443+ GeneratedFileName ,
444+ code ,
445+ expectedOutput ,
446+ isDurableFunctions : true ) ;
447+ }
448+
449+ /// <summary>
450+ /// Verifies that using the class-based syntax for authoring a mix of orchestrators, activities,
451+ /// and entities generates the appropriate function triggers for Azure Functions.
452+ /// </summary>
453+ [ Fact ]
454+ public async Task Mixed_OrchestratorActivityEntity_ClassBasedSyntax ( )
455+ {
456+ string code = @"
457+ #nullable enable
458+ using System;
459+ using System.Threading.Tasks;
460+ using Microsoft.DurableTask;
461+ using Microsoft.DurableTask.Entities;
462+
463+ namespace MyNS
464+ {
465+ [DurableTask(nameof(MyOrchestrator))]
466+ public class MyOrchestrator : TaskOrchestrator<int, string>
467+ {
468+ public override Task<string> RunAsync(TaskOrchestrationContext ctx, int input) => Task.FromResult(string.Empty);
469+ }
470+
471+ [DurableTask(nameof(MyActivity))]
472+ public class MyActivity : TaskActivity<int, string>
473+ {
474+ public override Task<string> RunAsync(TaskActivityContext context, int input) => Task.FromResult(string.Empty);
475+ }
476+
477+ [DurableTask(nameof(MyEntity))]
478+ public class MyEntity : TaskEntity<int>
479+ {
480+ public int Get() => this.State;
481+ }
482+ }" ;
483+
484+ string expectedOutput = TestHelpers . WrapAndFormat (
485+ GeneratedClassName ,
486+ methodList : $@ "
487+ static readonly ITaskOrchestrator singletonMyOrchestrator = new MyNS.MyOrchestrator();
488+
489+ [Function(nameof(MyOrchestrator))]
490+ public static Task<string> MyOrchestrator([OrchestrationTrigger] TaskOrchestrationContext context)
491+ {{
492+ return singletonMyOrchestrator.RunAsync(context, context.GetInput<int>())
493+ .ContinueWith(t => (string)(t.Result ?? default(string)!), TaskContinuationOptions.ExecuteSynchronously);
494+ }}
495+
496+ /// <inheritdoc cref=""IOrchestrationSubmitter.ScheduleNewOrchestrationInstanceAsync""/>
497+ public static Task<string> ScheduleNewMyOrchestratorInstanceAsync(
498+ this IOrchestrationSubmitter client, int input, StartOrchestrationOptions? options = null)
499+ {{
500+ return client.ScheduleNewOrchestrationInstanceAsync(""MyOrchestrator"", input, options);
501+ }}
502+
503+ /// <inheritdoc cref=""TaskOrchestrationContext.CallSubOrchestratorAsync(TaskName, object?, TaskOptions?)""/>
504+ public static Task<string> CallMyOrchestratorAsync(
505+ this TaskOrchestrationContext context, int input, TaskOptions? options = null)
506+ {{
507+ return context.CallSubOrchestratorAsync<string>(""MyOrchestrator"", input, options);
508+ }}
509+
510+ public static Task<string> CallMyActivityAsync(this TaskOrchestrationContext ctx, int input, TaskOptions? options = null)
511+ {{
512+ return ctx.CallActivityAsync<string>(""MyActivity"", input, options);
513+ }}
514+
515+ [Function(nameof(MyActivity))]
516+ public static async Task<string> MyActivity([ActivityTrigger] int input, string instanceId, FunctionContext executionContext)
517+ {{
518+ ITaskActivity activity = ActivatorUtilities.GetServiceOrCreateInstance<MyNS.MyActivity>(executionContext.InstanceServices);
519+ TaskActivityContext context = new GeneratedActivityContext(""MyActivity"", instanceId);
520+ object? result = await activity.RunAsync(context, input);
521+ return (string)result!;
522+ }}
523+
524+ [Function(nameof(MyEntity))]
525+ public static Task MyEntity([EntityTrigger] TaskEntityDispatcher dispatcher)
526+ {{
527+ return dispatcher.DispatchAsync<MyNS.MyEntity>();
528+ }}
529+ { TestHelpers . DeIndent ( DurableTaskSourceGenerator . GetGeneratedActivityContextCode ( ) , spacesToRemove : 8 ) } ",
530+ isDurableFunctions : true ) ;
531+
532+ await TestHelpers . RunTestAsync < DurableTaskSourceGenerator > (
533+ GeneratedFileName ,
534+ code ,
535+ expectedOutput ,
536+ isDurableFunctions : true ) ;
537+ }
315538}
0 commit comments