Search code examples
moryx

How to define a `TaskStep<T1,T2>` with ctor parameters in MORYX


I tried to create a TaskStep<T1,T2> with constructor parameters.

[Display(Name = "Some Task", Description = "Task which does something with a product")]
public class SomeTask : TaskStep<SomeActivity, SomeParameters>
{
    public TestTask([Display(ResourceType = typeof(Strings), Name = nameof(Strings.SOME_PARAMETER))] string someParameter) { }
}

Importing a workplan with this task works just fine. Also the Moryx.Workplans.Web editor handles a step creation as expected and asks me for the parameter when I drag a step into my workplan.

However, when I try to save or load a workplan with a step in it in the editor I get a 500 Http response and the following exception is in the body:

System.MissingMethodException: Cannot dynamically create an instance of type 'Moryx.Demo.Activities.SomeTask'. Reason: No parameterless constructor defined.
   at System.RuntimeType.ActivatorCache..ctor(RuntimeType rt)
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean wrapExceptions)
   at Moryx.Products.Management.RecipeStorage.LoadSteps(WorkplanEntity workplan, IDictionary`2 connectors)
   at Moryx.Products.Management.RecipeStorage.LoadWorkplan(WorkplanEntity workplanEntity)
   at Moryx.Products.Management.RecipeStorage.LoadWorkplan(IUnitOfWork uow, Int64 id)
   at Moryx.Products.Management.RecipeManagement.LoadWorkplan(Int64 workplanId)
   at Moryx.Products.Management.ProductManagementFacade.LoadWorkplan(Int64 workplanId)
   at lambda_method1492(Closure, Object, Object[])
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncActionResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|7_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context)
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

Solution

  • MORYX always needs an empty constructor to recreate objects from the database. If you do not define any constructor, than C# defines the default constructor for you. If you create a parameterized constructor however, the default one is no longer available, so you must provide a replacement. This can also be a private constructor, like in the SplitStep

    [Display(Name = "Some Task", Description = "Task which does something with a product")]
    public class SomeTask : TaskStep<SomeActivity, SomeParameters>
    {
        private SomeTask(){}
    
        public SomeTask([Display(ResourceType = typeof(Strings), Name = nameof(Strings.SOME_PARAMETER))] string someParameter) { }
    }