-
Notifications
You must be signed in to change notification settings - Fork 0
Writing a Custom Aspect in Xml
Writing your aspects in XML has advantages over writing an aspect in a .NET assembly:
-
It's declarative and very predictable: open the XML to know exactly what the aspect is doing,
-
It's more impervious to change.
-
You can directly add it as a part to the CodeModeler project and edit it using Visual Studio. Note you will have to Rebuild instead of Build for the project to take modifications into account.
However, since you're working with XML, you won't be able to debug your aspect in Visual Studio or benefit from the auto-completion IDE feature. Two solutions to overcome this issue:
-
Write your aspect in a .NET assembly (See the previous Writing a Custom Aspect in Code chapter), and once finished, port it into XML,
-
Write it in XML and use traces so you can see what's going on when your aspect is executed.
Note: Built-in (or “Official”) aspects are all shipped as XML files and are great working examples of what you can do.
XML aspects are in fact text templates interpreted by the CodeModeler Template Engine and then executed at the inference stage. By default, the template engine doesn't compile templates with the TRACE constant defined and therefore traces do not appear when the aspect is executed.
Set the following line in your XML aspect (or any text template by the way) to enable traces:
<?code @template compilerOptions=" /define:TRACE" ?>
Note: Traces will be not be coming from the Visual Studio process (devenv.exe) but from the CodeModeler Meta Compiler (CodeModeler.Build.exe) which is the process that execute the inference and production engine.
Compilation issues with Xml aspects are shown directly in the Visual Studio Output window, for example here we have an aspect that simply has a C# syntax error:
Project producing...
SoftFluent CodeModeler Meta Compiler - 64-bit - Build Number 1.3.0.1
Copyright (C) SoftFluent S.A.S 2017-2020. All rights reserved.
License usage: Unlimited
CmdLine: "c:\users\Kilroy\appdata\local\microsoft\visualstudio\16.0_26dd5244exp\extensions\softfluent s.a.s\softfluent codemodeler\1.3.0.0\CodeModeler.Build.exe" "D:\sf\source\repos\Commerce.Model\Commerce.Model\Commerce.sfcm" /oop /Log:"D:\sf\source\repos\Commerce.Model\Commerce.Model\Commerce.build.xml" /hostpid:19768 /Results:"C:\Users\Kilroy\AppData\Local\Temp\Commerce.sfcm.oop" /DoNotCreateReportsOnException /DoNotShowReportsOnException /NoFlashOnError
Time: 15/02/2020 19:02:58
ModelLoad: fileName='D:\sf\source\repos\Commerce.Model\Commerce.Model\Commerce.sfcm'
CodeModelerTemplateException: CM7010: CodeModeler template 'D:\sf\source\repos\Commerce.Model\Commerce.Model\CopyColumn.xml' exception using language 'CSharp': Error in File 'c:\Users\Kilroy\AppData\Local\Temp\CMTemplating.Temp4.0.30319.42000\0\CM_02843890551624727178.xml.cs', Ln 48, Col 116
43 {
44 foreach(var entity in Project.Entities)
45 {
46 Trace.WriteLine("entity:" + entity.ClrFullTypeName);
47 // get all properties with the copyColumn Xml attribute (Mx) is set
48 foreach(var property in entity.Properties.Where(p => p.GetAttributeValue("copyColumn", NamespaceUri, false))
^--- ) expected
49 {
50 Trace.WriteLine("property:" + property.Name);
51 }
52 }
53 }
at CodeModeler.Runtime.Templating.Template.BuildCodeDomType(CodeDomProvider provider, Boolean refresh)
at CodeModeler.Runtime.Templating.Template.Build()
at CodeModeler.Model.Project.InternalLoad(Project parentProject, String fileName, ProjectLoadOptions loadOptions, Boolean runTemplate, Boolean subModel, XmlElement importElement)
at CodeModeler.Model.Project.LoadImport(XmlNode import, ProjectLoadOptions options, Boolean runTemplate, Boolean pattern)
at CodeModeler.Model.Project.PostLoadHandleImport(XmlElement import, ProjectLoadOptions options, ImportStep requiredStep, Boolean pattern)
at CodeModeler.Model.Project.PostLoad(ProjectLoadOptions options, ImportStep requiredStep)
at CodeModeler.Model.Project.Parse(ProjectLoadOptions options, Boolean subModel)
at CodeModeler.Model.Project.InternalLoad(Project parentProject, String fileName, ProjectLoadOptions loadOptions, Boolean runTemplate, Boolean subModel, XmlElement importElement)
at CodeModeler.Model.Project.Load(ProjectPackage package, ProjectLoadOptions options)
at CodeModeler.Model.Packaging.ProjectPackage.Open(Project project, String filePath, ProjectLoadOptions options)
at CodeModeler.Model.Project.Load(String fileName, ProjectLoadOptions options)
at CodeModeler.Build.Program.SafeMain(String[] args)
at CodeModeler.Build.Program.Main(String[] args)
An error has occured trying to produce project.
The following is the Xml code (with embedded C#) that adds a copy property to every property configured so.
The aspects hooks inference steps so it can run its code after entities are parsed and loaded (and partially inferred), but before corresponding tables are created. Because the copy property is added at that time, the CodeModeler inference engine will automatically infer a column (and everything else) just like the property was designed.
This is the self-sufficient aspect code (note it’s using .NET’s default tracing system). You can save it as a CopyColumn.xml file for example (you can put it in the project’s physical folder):
<?code @template compilerOptions=" /define:TRACE" ?>
<cm:project xmlns:cm="http://www.softfluent.com/cm/2018/1" defaultNamespace="Samples.CopyColumns">
<cm:pattern name="Copy Columns Aspect" namespaceUri="http://www.softfluent.com/cm/aspects/copyColumns/2020/1" preferredPrefix="_cc" step="Methods">
<cm:message class="_doc">
Copy Column Aspect
Version 1.0.0.0 - 2020/01/01
</cm:message>
<cm:descriptor name="copyColumn" targets="Property" displayName="Add Copy Column" typeName="bool" description="Adds a copy column to the final table." category="Copy Columns" />
</cm:pattern>
<?code @namespace name="System" ?>
<?code @namespace name="System.Diagnostics" ?>
<?code @namespace name="CodeModeler.Model" ?>
<?code
const string NamespaceUri = "http://www.softfluent.com/cm/aspects/copyColumns/2020/1";
Trace.WriteLine("Running aspect");
Project.StepChanging += (s, e) =>
{
// the Tables step happens just before tables are created
// so if we add a property here, it will automatically be inferred into a column
if (e.Step == ImportStep.Tables)
{
foreach(var entity in Project.Entities)
{
Trace.WriteLine("scanning entity: " + entity.ClrFullTypeName);
// get all properties with the copyColumn Xml attribute (Mx) is set
foreach(var property in entity.Properties.ToArray()) // use ToArray because we will add properties
{
if (!property.GetAttributeValue("copyColumn", NamespaceUri, false))
continue;
Trace.WriteLine("adding copy column for property:" + property.Name);
var copyProperty = new Property();
// copy some attributes
copyProperty.Name = property.Name + "_Copy";
copyProperty.TypeName = property.TypeName;
copyProperty.MaxLength = property.MaxLength;
copyProperty.SortOrder = property.SortOrder + 1;
entity.Properties.Add(copyProperty);
}
}
}
};
?>
</cm:project>
You can add the aspect using the “Add Existing Aspect” Aspects folder node context menu item:
Here we can see the aspect description and the aspect descriptor. An aspect can contain any number of descriptors.
Note: Descriptors are cached with the Visual Studio project, so if you change a descriptor in an Xml aspect, you will have to close the solution or project and open it again to have the descriptor modifications considered.
Once added, the Xml aspect can be modified directly from the Visual Studio xml editor, and each Rebuild will use the new version:
For the following model:
We can configure the Name property to add a copy column using the Visual Studio property grid (“Aspects and Producers Properties” tab):
The aspect effect can be seen without even generating or building anything. You can use the “View Inferred Model” item in the Ribbon Bar:
We can check that a “Name_Copy” property has been added, as well as a “Customer_Name_Copy” column.
- Introduction
- Architect Guide
- Concepts
- Using Visual Studio
- Overview
- Creating a CodeModeler Project
- Visual Environment
- Project Hierarchy
- Design Surface
- Customizing Design Surfaces
- Ribbon Bar
- Property Grid
- Member Format Expressions
- Model Grid
- Method Editor
- View Editor
- Instance Editor and Grid
- Resources Editor
- Inferred Model Viewer
- Building
- Project Physical Layout
- Source Control Support
- Generating
- Aspect Oriented Design (AOD)
- Developer Guide
- The Business Object Model (BOM)
- CodeModeler Query Language (CMQL)
- Starting Guide - Tutorial
- Upgrade From CFE