Thomas Jaeger
Thomas Jaeger

Reputation: 943

How to process a DynamoDBEvent in a C# Lambda Function?

I created a C# Lambda function that is being triggered with a DynamoDB stream. It gets excecuted just fine. However, the StreamRecord of the NewImage value returns no values. The count is 0. What am I doing wrong? I have checked all the AWS documention but this seems more and more like a bug. My below lambda function should work and it should return at least 1 StreamRecord in my example. attributeMap.Count always returns 0 but it should return 1.

    public void FunctionHandler(DynamoDBEvent dynamoDbEvent, ILambdaContext context)
    {
        Console.WriteLine($"Beginning to process {dynamoDbEvent.Records.Count} records...");

        foreach (var record in dynamoDbEvent.Records)
        {
            Console.WriteLine($"Event ID: {record.EventID}");
            Console.WriteLine($"Event Name: {record.EventName}");

            var attributeMap = record.Dynamodb.NewImage;
            if (attributeMap.Count > 0) // If item does not exist, attributeMap.Count will be 0
            {
                Console.WriteLine(attributeMap["AccountId"].S);
            }
        }

        Console.WriteLine("Stream processing complete.");
    }

Upvotes: 1

Views: 3108

Answers (1)

Thomas Jaeger
Thomas Jaeger

Reputation: 943

UPDATE: October, 4th, 2018. I no longer use an admin app. I now use CloudFormation exclusively to create and maintain everyting including full CI/CD pipelines with CodePipelines. This includes all Serverless lamba functions in .NET Core 2.1.

I figured it out. The sucky thing is that AWS Docs do not say anything about this. This was a pain in the ass to find out. For anyone else who might need this information here it is: You have to set the stream view type when you create the DynamoDB stream for the table. Here is a picture for the AWS Console:

enter image description here

However, since I setup all tables via an admin console (in C# Core 2.0), here is how I setup the setup the table including the stream specification and the event source mapping request to the lambda function:

        var request = new CreateTableRequest
        {
            TableName = TABLE_CREATE_ACCOUNT,
            AttributeDefinitions = new List<AttributeDefinition>()
            {
                new AttributeDefinition
                {
                    AttributeName = "CommandId",
                    AttributeType = ScalarAttributeType.S
                }
            },
            KeySchema = new List<KeySchemaElement>()
            {
                new KeySchemaElement
                {
                    AttributeName = "CommandId",
                    KeyType = KeyType.HASH
                }
            },
            ProvisionedThroughput = new ProvisionedThroughput
            {
                ReadCapacityUnits = 1,
                WriteCapacityUnits = 1
            },
            StreamSpecification = new StreamSpecification
            {
                StreamEnabled = true,
                StreamViewType = StreamViewType.NEW_IMAGE
            }
        };

        try
        {
            var response = _db.CreateTableAsync(request);
            var tableDescription = response.Result.TableDescription;
            Console.WriteLine("{1}: {0} ReadCapacityUnits: {2} WriteCapacityUnits: {3}",
                tableDescription.TableStatus,
                tableDescription.TableName,
                tableDescription.ProvisionedThroughput.ReadCapacityUnits,
                tableDescription.ProvisionedThroughput.WriteCapacityUnits);
            string status = tableDescription.TableStatus;
            Console.WriteLine(TABLE_CREATE_ACCOUNT + " - " + status);
            WaitUntilTableReady(TABLE_CREATE_ACCOUNT);

            // This connects the DynamoDB stream to a lambda function
            Console.WriteLine("Creating event source mapping between table stream '"+ TABLE_CREATE_ACCOUNT + "' and lambda 'ProcessCreateAccount'");
            var req = new CreateEventSourceMappingRequest
            {
                BatchSize = 100,
                Enabled = true,
                EventSourceArn = tableDescription.LatestStreamArn,
                FunctionName = "ProcessCreateAccount",
                StartingPosition = EventSourcePosition.LATEST
            };
            var reqResponse =_lambda.CreateEventSourceMappingAsync(req);
            Console.WriteLine("Event source mapping state: " + reqResponse.Result.State);
        }
        catch (AmazonDynamoDBException e)
        {
            Console.WriteLine("Error creating table '" + TABLE_CREATE_ACCOUNT + "'");
            Console.WriteLine("Amazon error code: {0}", string.IsNullOrEmpty(e.ErrorCode) ? "None" : e.ErrorCode);
            Console.WriteLine("Exception message: {0}", e.Message);
        }
        catch (Exception e)
        {
            Console.WriteLine("Error creating table '" + TABLE_CREATE_ACCOUNT + "'");
            Console.WriteLine("Exception message: {0}", e.Message);
        }

The key is

StreamViewType = StreamViewType.NEW_IMAGE

That was it.

Upvotes: 3

Related Questions