Generating code from a model

In this part, you will learn how to turn your Yellicode model into actual C# code. We are going to modify the default template (csharp-tutorial.template.ts) from part I so that it generates the C# classes and enumerations that we need.

Sharpen the code generation template

Open up csharp-tutorial.template.ts and start by adding a new import line that adds the C# extension that you installed in the previous step.

import { CSharpWriter } from '@yellicode/csharp';

Then change the name of the output file to ./output/Domain.cs and replace the contents of the generateFromModel function so that the generator function looks as follows:

Generator.generateFromModel({ outputFile: './output/Domain.cs' }, (writer: TextWriter, model: elements.Model) => {    
    const csharp = new CSharpWriter(writer);
    csharp.writeUsingDirectives('System');
    csharp.writeLine();

    csharp.writeNamespaceBlock({name: 'CSharpTutorial.Domain'},() => {
        model.getAllClasses().forEach((eachClass) => {    
            csharp.writeClassBlock(eachClass, () => {
                // some work to be done here
            });        
            csharp.writeLine();
        })
    })    
});

Generate the first domain model

Let's see what the first output looks like. Run Yellicode (in watch mode so that you only need to run this command once):

yellicode --watch

When you see the message 'Code generation done', find the output file ./output/Domain.cs and observe the first version of our domain model.

The template enumerated all classes in your model (using the Meta Model API) and wrote an empty class declaration - including comments - for each one of them. Except that one comment is empty, because the Task class is undocumented. Let's quickly fix this!

Go back to Yellicode Modeler, find the Task class and enter a description (hint: A task is a discrete piece of work required to complete a story). Now save the model (make sure to unfocus out of the description input first), go back to the output file and notice your new comments.

Completing the generated code

Now let's complete the generated C# code by adding auto-properties and enumerations. First, update the writeClassBlock call as follows:

csharp.writeClassBlock(eachClass, () => {
    eachClass.ownedAttributes.forEach(p => {
        csharp.writeAutoProperty(p);
        csharp.writeLine();
    })
});        

Save the template, observe the terminal output and wait for the 'Code generation done.' message. Now we're really getting somewhere, you should see that a property is generated for each attribute (that's the UML term for a property) in your model.

Side note: the ownedAttributes property returns all attributes that are defined on a class, excluding inherited properties. The Class element also has a getAllAttributes() function that includes all inherited properties (which we obviously don't need here).

The current output would not compile because we are still missing enumerations that some of the classes depend on. This is a simple four-liner to be added to your template:

model.getAllEnumerations().forEach((eachEnum) => {
    csharp.writeEnumeration(eachEnum);
    csharp.writeLine();
})

The final template

Your final template should now look as follows:

import { Generator, TextWriter } from '@yellicode/templating';
import * as elements from '@yellicode/elements';
import { CSharpWriter } from '@yellicode/csharp';

Generator.generateFromModel({ outputFile: './output/Domain.cs' }, (writer: TextWriter, model: elements.Model) => {
    const csharp = new CSharpWriter(writer);
    csharp.writeUsingDirectives('System');
    csharp.writeLine();

    csharp.writeNamespaceBlock({ name: 'CSharpTutorial.Domain' }, () => {
        // Generate enumerations
        model.getAllEnumerations().forEach((eachEnum) => {
            csharp.writeEnumeration(eachEnum);
            csharp.writeLine();
        })
        // Generate classes
        model.getAllClasses().forEach((eachClass) => {
            csharp.writeClassBlock(eachClass, () => {
                eachClass.ownedAttributes.forEach(p => {
                    csharp.writeAutoProperty(p);
                    csharp.writeLine();
                })
            });
            csharp.writeLine();
        })
    })
});

If you haven't done so, save the template again and wait for the enumerations to appear.

Note that the generated enumerations have an explicit integer value. Obviously, this is not mandatory (but you might prefer them). You can see where the values come from when you open the enumeration in Yellicode Modeler and observe the Enumeration Literals section.

Generating each class in its own file

You might prefer to generate each class in its own .cs file. This is outside the scope of this tutorial, but is a pretty simple change to the structure of your template. We will leave that as an exercise for the reader.

This completes the second part of this tutorial. In the next part, you will see how we can bring .NET types into the mix.

Continue to Part II - Using framework-specific types »