A code-first modeling approach utilizing NMF.
The project is divided into four packages. Three core packages of generator and assist features and one library package.
- CTMGeneator: Contains the core functionallity of generating a model and source code.
- CTMAnalyzer: Analyzer for assistence when using the CTMGenerator.
- CTMCodeFixes: Code Fixes for issues reported by the CTMAnalyzer.
- CTMLib: Library of custom attributes.
Once the generator package is added to a project it will run in the background of every build process.
The minimum requirements to have a model created and code generate are:
- An assembly entry for the model interface namespace above the namespace declaration.
- An interface with the ModelInterface attribute.
- The interface needs to either be partial or have IModelElement as base type.
Example interface implementation:
using NMF.Models;
using CTMLib;
[assembly: ModelMetadata("http://github.com/CodeToModel", "CodeToModel.Example.MyExample.nmeta")]
namespace CodeToModel.Example {
[ModelInterface]
public interface IMyExample : IModelElement {
}
}Most model information is derived from the source code directly. Attributes are used to add information which can not come from the code directly.
Assembly attribute to identfy a model. Each entry represents a new model and needs to be paired with a namespace declaration. If declared in the interfaces file directly needs to be put above the namespace declaration.
[assembly: ModelMetadata("MODELURI", "NAME.PREFIX.SUFFIX")]Warning
Having two ModelMetadata entries with the same NAME attribute will stop the source code generator from running. Each model only needs one ModelMetadata entry!
Identfies a interface delcaration as part of a model.
[ModelInterface]
public partial interface IMyExample {}Identfies a enum delcaration as part of a model.
[ModelEnum]
public enum MyEnum {}Gives a collection an upper and lower bound. In general the default values are as follows:
- Non-Nullable: Lower = 1
- Nullable: Lower = 0
- Non-Collection: Upper = 1
- Collection: Upper = -1
[UpperBound(64)]
[LowerBound(0)]
public IListExpression<Object> objects { get; }Sets the default value of an attribute property.
[DefaultValue("ReplaceMe")]
string Name { get; set; }Marks an attribute as identifier for this model element.
[Id]
public string UUID { get; set; }Declares the scope of the identifier defined by the Id attribute in which this attribute is unique.
[ModelInterface]
[IdentifierScope(NMF.Models.Meta.IdentifierScope.Global)]
public partial interface ISomeElement {}Tells the generator to generate a abstract implementation of this interface.
[IsAbstract]
public partial interface IAbstractElement {}Marks a property as containment making it a composition (child element).
[IsContainment]
public IMyOtherModelElement MyChildModelElement { get; set; }Marks an model element as instance of another model element.
[ModelInterface]
[InstanceOf("IVehicle")]
public partial interface IBike : IVehicle {}Marks a property from another model element as opposite of this element.
namespace Worksystems {
[ModelInterface]
public interface IWorkSystem : IModelElement {
[Opposite(typeof(IProcess), nameof(IProcess.AssignedWorkflowSystem))]
public IListExpression<IProcess> Processes { get; }
}
[ModelInterface]
public interface IProcess : IModelElement {
[Opposite(typeof(IWorkflowSystem), nameof(IWorkflowSystem.Processes))]
public IWorkflowSystem AssignedWorkflowSystem { get; set; }
}
}Declares that a property refines another property.
[ModelInterface]
public interface IVehicle : IModelElement {
IListExpression<IWheel> Wheels { get; }
}
[ModelInterface]
public interface IBicylce : IVehicle, IModelElement {
[Refines(nameof(Wheels))]
IBikeWheel FrontWheel { get; set; }
[Refines(nameof(Wheels))]
IOrderedSetExpression<IWheel> HelperWheels { get; }
}
[ModelInterface]
public partial interface IWheel {}
[ModelInterface]
public partial interface IBikeWheel : IWheel {}To have summary and remarks doc comments transfered to the model elements and generated source code add GenerateDocumentationFile to the .csproj file and set it to true.
By default the created model files will be saved to the location of the first file containing the model annotations. This behavior can be overriden by providing a OutputPaths.xml file, added via the <AdditionalFiles Include="OutputPaths.xml"/> property in the .csproj, with the following structure:
<paths>
<path namespace="ALL OR FULL NAMESPACE NAME">
C:\FULL\ABSOLUTE\PATH\TO\DESIRED\LOCATION
</path>
</paths>Note
ALL means this path will be used for every namespace which has no previously defined path.
Note
No paths will be created and have to already exist and be writable for the generator to work!
By default generated code will not be directly visible and just be part of the compilation. Should it be desired to have the generated files put in a specific location adding the following properties to the .csproj file will achive it:
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>OUTPUT/LOCATION</CompilerGeneratedFilesOutputPath>