CodeFluent Entities Documentation
Writing an Aspect in XML
See Also Send comments on this topic.
CodeFluent Entities > Architect Guide > Aspect Oriented Design > Custom Aspects > Writing an Aspect in XML

Glossary Item Box

This topic illustrates how to write an aspect in XML.

Writing your aspects in XML is definitely the most insteresting format compared to writing an aspect in a .NET assembly since:

However, since you're in XML you won't be able to debug your aspect in Visual Studio, or benefit from the auto-completion. Two solutions to overcome this issue:

  1. Write your aspect in a .NET assembly (Writing an Aspect in Code), and once finished, port it into XML,
  2. Write it in XML right away, and use traces so you can see what's going on when your aspect is executed.

 

Activating Traces in XML aspects

XML aspects are in fact text templates interpreted by the CodeFluent Entities 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:

Enabling Traces Copy Code
<?code @template compilerOptions=" /define:TRACE" ?>

Now that you know how to trace information from your aspect, let's see how to write one.

Note: Built-in Aspects are all shipped as XML files and are great working examples of what you can do.

 

Example: Designing a custom aspect

This example demonstrates how to write a Synchronize XML aspect that adds a synchronization capability to the generated object model. This aspects adds a method called right before saving an entity and which synchronizes property values between two entities.

To do this the aspect binds to the StepChanging event of the Project class. As a consequence the aspect will be notified each time we switch step in the inference pipeline, and if ever we're at the Tables step, the aspect will add an OnBeforeSave rule and a snippet implementing the OnBeforeSave snippet. Finally, the added snippet will contain the synchronization code.

Note: The Tables step is used so that ProjectTypes were inferred (i.e. we'll be able to access all entities, properties, etc.).

For your information, the StepChanging event is raised when we're changing from on pipeline step to another (i.e. after the previous step was processed but before the current step). Therefore running at the Tables step means we're running right after the ProjectTypes step, but before the Tables step started.

The aspect then iterates on all entities of the project, and if the srcEntity and srcProperty attributes are defined in our custom namespace ("http://www.sample.com/aspects/synchronize/2008/1") on the current entity, the OnBeforeSave rule is added with its corresponding snippet containing the actual synchronization code.

Here's the aspect:

Sample.Synchronize.xml Copy Code
<cf:project xmlns:cf="http://www.softfluent.com/codefluent/2005/1">
    <!-- CodeFluent API namespaces -->   
    <?code @namespace name="CodeFluent.Model.Code" ?>
    <?code @namespace name="CodeFluent.Model.Rules" ?>

    <!-- ProjectHandler class for listening to Project StepChanging events -->   
    <?code @member
    public class ProjectHandler
    {
        private static string synchronizeUri = "http://www.sample.com/aspects/synchronize/2008/1";
          
        public static void ProcessSynchronizedEntities(Project project)
        { 
            foreach (Entity entity in project.Entities)
            {
                string srcEntity = entity.GetAttributeValue<string>("srcEntity", synchronizeUri, null);
                string srcProperty = entity.GetAttributeValue<string>("srcProperty", synchronizeUri, null);
                if ((srcEntity != null) && (srcProperty != null))
                {                 
                    // Add 'OnBeforeSave' rule method to entity
                    Rule rule = new Rule();
                    rule.TypeName = EntityEventType.OnBeforeSave.ToString();
                    rule.SortOrder = 1000;
                    entity.Rules.Add(rule);
                    ((EntityEventRule)rule.Instance).MethodName = "OnBeforeSave_Synchronize";
                    // Create body for 'OnBeforeSave' snippet
                    Body snippetBody = new Body();
                    snippetBody.Text = string.Format("private void OnBeforeSave_Synchronize() {{ {1} = {0}.{1}; }}", srcEntity, srcProperty);
     
                    // Add 'OnBeforeSave' snippet method to entity 
                    Method snippetMethod = new Method();
                    snippetMethod.Name = "OnBeforeSaveSnippet";
                    snippetMethod.MethodType = MethodType.Snippet;
                    entity.Methods.Add(snippetMethod); 
                    snippetMethod.Bodies.Add(snippetBody);         
                }
            }     
        }
   
        public static void StepChanging(object sender, StepChangeEventArgs e)
        {
            switch (e.Step)
            {
                case ImportStep.Tables:
                    ProcessSynchronizedEntities((Project)sender);
                    break;
            } 
        }
    } 
    ?>
 
    <!-- Add Project StepChanging event listener  -->
    <?code Project.StepChanging += new Project.OnStepChangeEventHandler(ProjectHandler.StepChanging); ?>
</cf:project>

 

Now that our aspect is functional we need to add it to a model.

 

Using the aspect

This example shows a CodeFluent model that imports the our Synchronize aspect at the Start parsing step.

This model declares the http://www.sample.com/aspects/synchronize/2008/1 namespace at the project level as well as the srcEntity and srcProperty attributes on the Employee entity. Those custom attributes will be recognized by the custom aspect and the aspect will add a rule and a snippet to this Employee entity. The snippet will synchronize the Employee's Department property with the Department property of its associated Manager.

MyApplication.xml Copy Code
<cf:project xmlns:cf="http://www.softfluent.com/codefluent/2005/1"
            xmlns:synchronize="http://www.sample.com/aspects/synchronize/2008/1"
            defaultNamespace="MyApplication">
 
    <!-- Import the Synchronize pattern -->
    <cf:import enabled="true" path="Sample.Synchronize.xml" runTemplate="true" step="Start"/>
 
    <!-- Producers -->
    <cf:producer name="SQL Server Producer" typeName="CodeFluent.Producers.SqlServer.SqlServerProducer, CodeFluent.Producers.SqlServer">
        <cf:configuration targetDirectory="..\Generated\Sql" />
    </cf:producer>

    <cf:producer name="BOM Producer" typeName="CodeFluent.Producers.CodeDom.CodeDomProducer, CodeFluent.Producers.CodeDom">
        <cf:configuration targetDirectory="..\Generated\Model" outputName="{0}.dll"/>
    </cf:producer>
 
    <Manager>
        <Id />
        <Name />
        <Department />
    </Manager>

    <Employee synchronize:srcEntity="Manager" synchronize:srcProperty="Department">
        <Id />
        <Name />
        <Department />
        <Manager typeName="Manager" />
    </Employee>
 </cf:project>

  

If we take a look at what was generated, we can see that the Business Object Model Producer generated an OnBeforeSave_Synchronize method which sets the Employee's Department to its Manager one.

The OnBeforeSave rule was also applied since as we can see the Employee's Save method calls our OnBeforeSave_Synchronize before actually saving.

Generated Output Copy Code
// Emloyee class : Save method
public virtual bool Save()
{
    this.OnBeforeSave_Synchronize();
    return this.BaseSave(false);
}
 
// Emloyee class : OnBeforeSave_Synchronize snippet method
private void OnBeforeSave_Synchronize()
{
    this.Department = this.Manager.Department;
}

Note: Using the core edition, you can view what CodeFluent Entities inferred without generating over using the CodeFluent Meta Model Explorer tool.

Using the Modeler Edition, once your model is saved, you can click on the "View Inferred Model" button in the ribbon.

See Also