Skip to content

CDK for .NET does not use the compiler to flag construct required properties #1845

@mabead

Description

@mabead

I am using the CDK version 0.24.1 with c#. If I create a stack that contains this:

new Amazon.CDK.AWS.Lambda.Function(this, "SomeId", new FunctionProps());

It compiles perfectly. But then, when I run cdk diff, I get the following error:

Unhandled Exception: Amazon.JSII.Runtime.JsiiException: Cannot read property 'name' of undefined
   at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
   at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
   at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
   at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
   at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
   at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
   at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
   at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
   at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
   at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
   at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
   at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
   at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
   at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
   at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
   at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
   at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
   at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
   at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
   at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
   at Amazon.JSII.Runtime.Deputy.DeputyBase..ctor(DeputyProps props)

So two problems here:

  1. The error message does not say anything useful. It is pretty hard to know which construct is causing the error.
  2. The error is caused by the fact that I didn't provide the required FunctionProps properties to my lambda. The required function properties are handler, code and runtime.

I'd like to focus on the second point. The fact that we get a runtime error and not a compile time error is a big lost compared to TypeScript. In TypeScript, if I don't provide a required property, I see it directly in my IDE:

image

Given that C# is a compiled language, I would expect that all required properties be enforced at compile time.

In my opinion, the way to do this is simply to use constructor for the different props. For example, the FunctionProps is currently defined like this:

public class FunctionProps : DeputyBase, IFunctionProps
{
   // The class just has a default/empty constructor
   public FunctionProps() {}
   // These 3 properties are required but there is no way to differentiate 
   // them from optional properties from the compiler point of view.
   public string Handler { get; set; }
   public Code Code { get; set; }
   public Runtime Runtime { get; set; }
   // All other properties are optional
   public string FunctionName { get; set; }
}

My suggestion would be to use a constructor with parameters instead of relying on getters/setters. Ex:

public class FunctionProps : DeputyBase, IFunctionProps
{
    public FunctionProps(
        // Required parameters come first and don't have default values.
        string handler,
        Code code,
        Runtime runtime,
        // Optional parameters come second and *DO* have default values
        string functionName = null)
    {
        Handler = handler;
        Code = code;
        Runtime = runtime;
        FunctionName = functionName;
    }

    // Required properties
    public string Handler { get; set; }
    public Code Code { get; set; }
    public Runtime Runtime { get; set; }
    // Optional properties
    public string FunctionName { get; set; }
}

With such code, I get a nice intellisense that tells me what is required and what is optional:

image

And if I instantiate a FunctionProps and don't provide the required properties, I get a compile time error:

error CS7036: There is no argument given that corresponds to the required formal parameter 'handler' of 'FunctionProps.FunctionProps(string, Code, Runtime, string)'

And finally here's how it looks like when I define a lambda.

new Amazon.CDK.AWS.Lambda.Function(this, "SomeId", new FunctionProps(
    handler: "my handler",
    code: Code.Inline("my code"),
    runtime: Runtime.NodeJS810,
    // Optional parameters below: since they have default 
    // values in the constructor, I can omit specifying them.
    //
    // I use a named parameter here but I could also have used 
    // the property setter. It's just a matter of preference.
    // Personally, I prefer to use named constructor parameters 
    // for the optional parameters since they end up at the same 
    // indentation level as the required parameters.
    functionName: "SomeName");

Please let me know what you think of this proposition.

Metadata

Metadata

Assignees

Labels

bugThis issue is a bug.effort/smallSmall work item – less than a day of effortlanguage/dotnetRelated to .NET bindingsp0

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions