Archive

Posts Tagged ‘Aspects’

SQL Server In-Memory OLTP

July 10, 2014 Leave a comment

In-Memory OLTP comes with Microsoft SQL Server 2014 and can significantly improve OLTP database application performance. It is a memory-optimized database engine integrated into the standard SQL Server engine. This system provides memory-optimized tables which are fully transactional and are accessed using class Translact-SQL instructions.

In-Memory Tables comes with some limitations. We won’t enumerate all but only those which are related to CodeFluent Entities:

  1. Foreign keys aren’t supported
  2. RowVersion and Timestamp columns aren’t supported: http://msdn.microsoft.com/en-us/library/dn133179.aspx
  3. Default constraints aren’t supported
  4. Some Transact-SQL constructs aren’t supported: http://msdn.microsoft.com/en-us/library/dn246937.aspx

Let’s handle those four points!

Foreign Keys

There are two options:

  • Don’t create relation :(
  • Create relation without foreign key :)

The second solution requires the usage of an Aspect. Fortunately we already wrote it a few time ago: http://www.softfluent.com/forums/codefluent-entities/how-to-disable-creating-foreign-key-by-sql-producer-

Even if foreign keys do not exist anymore, CodeFluent Entities still generates LoadBy_Relation methods so you won’t see any difference in your code. :)

Foreign Keys

RowVersion

RowVersion is not supported by In Memory tables so let’s remove it. We have to set “Concurrency Mode” to “None”:

RowVersion

 

Default Constraints

Default constraints used by tracking columns (creation time & last write time) are not supported. Here we have two options:

  • Remove default constraints :(
  • Move them into the Save stored procedure :)

The first option is available in the Property Grid at project or entity level by removing the tracking time columns:

Properties

The second option can be done with an Aspect as you can see in the full example (see below). The edited INSERT statement looks like:

    INSERT INTO [Customer] (
        [Customer].[Customer_Id],
        [Customer].[Customer_Name],
        [Customer].[Customer_ContactSource_Id],
        [Customer].[_trackCreationUser],
        [Customer].[_trackLastWriteUser],
        [Customer].[_trackLastWriteTime])
    VALUES (
        @Customer_Id,
        @Customer_Name,
        @Customer_ContactSource_Id,
        @_trackLastWriteUser,
        @_trackLastWriteUser,
        (GETDATE())) -- Default Value

Tansact SQL

By default the SQL Server Producer surround the procedure code with a transaction. This transaction isn’t supported when using In Memory Table. The following exception is thrown when calling the stored procedure:

Unhandled Exception: System.Data.SqlClient.SqlException: Accessing memory optimized tables using the READ COMMITTED isolation level is supported only for autocommit transactions. It is not supported for explicit or implicit transactions. Provide a supported isolation level for the memory optimized table using a table hint, such as WITH (SNAPSHOT).

To remove it, we have to configure the SQL Server to not produce it:

SQL Server

 

Migrate the table

After those small changes, we can migrate the table to an In Memory table:

Migration

Migration Result

 

We can now use the In Memory table from the application:

Customer customer = new Customer();
customer.Name = "John Doe";
customer.Save();

All-in-One method

All the previous steps are automated by an aspect. All you have to do is include the aspect and set “enabled” on tables:

SqlServer In Memory Aspect

The full code sample including the aspect is available on our GitHub repository.

The R&D Team

Store Enums as Strings


CodeFluent Entities fully suports .NET enumerations since 2005. This supports includes:

  • Being able to create your own enumerations at design time,
  • Use already existing CLR enumerations.
  • Using our designer, you can create and use enumerations in your model (e.g. OrderStatus in the screenshot below):

Enumeration

Currently CodeFluent Entities allows to store the enumeration value as a numeric value. Sometimes you prefer storing it as text so we write an aspect to do that.

Without the Aspect:
Without the aspect

With the Aspect:
With the Aspect

After adding the Aspect, you’ll see new properties in the property grid:

Store Enumeration As Text Property

The “Store Enumeration As Text” property exists at:

  • Enumeration level
  • Property level

You can also specified the column size. By default the column size will be computed from the enumeration values when possible. For example if you have a flag enumeration with values “First”, “Second”, “Third” the longest value will be “First, Second, Third”, so the column size will be 21. Of course you can override the value by setting the property “Default Column Size” at enumeration level or “Column Size” at property level.

How to install the aspect

Create a C# project in the solution and copy the Aspect files: https://github.com/SoftFluent/CodeFluent-Entities/blob/master/Extensions/SoftFluent.StoreEnumAsText/SoftFluent.Samples.StoreEnumAsText.Aspects/StoreEnumerationAsTextAspect.cs

Add a reference in the model to the aspect project:

Solution Explorer

 

Then, select those two projects:

 

Add Reference

Finally, add the Aspect into your model:

Add Existing Aspect

The code of the aspect is available on GitHub repository. Please leave feedback on how you liked this Aspect and what we could improve. You can also find additional resources about Aspects here.

Happy Aspecting!

The R&D team.

Writing a custom CodeFluent Entities aspect to encrypt/decrypt columns values at runtime

September 25, 2013 Leave a comment

Today, we will demonstrate how to automatically change the SQL code generated during the build process in order to encrypt and decrypt the values stored in the database columns. This is the answer to a very interesting question that was posted to stackoverflow recently: How to manage Encrypt* and Decrypt* TSQL functions on an entity property?

Let’s consider this model:

model

A card number is a sensible piece of information, so you should encrypt it before saving it in the database. Obviously, You should also be able to read it back and decrypt it.

Of course, with CodeFluent Entities, you can do it in the Business Object Model Layer generated BOM (C# or VB.NET) using OnAddSaveParameters and OnAfterReadRecord rules, but this post will demonstrate how it can be done directly in the database layer!

Microsoft SQL Server 2005 and higher provides two new useful functions: ENCRYPTBYPASSPHRASE and DECRYPTBYPASSPHRASE. These functions allow you to encrypt or decrypt data with a pass phrase. For example:

ENCRYPTBYPASSPHRASE(‘my super secret key’, ‘1234-5678-9012-3456-7890’) -- will write 0x01000000FFB251B13ADE1344597535490BDD7ABB4A5094CF24C211A63FFDD465052795A9 in the database

So all we need to do is to call ENCRYPTBYPASSPHRASE during saving (INSERT or UPDATE statements), and DECRYPTBYPASSPHRASE during loading (SELECT statements). A code such as this one for instance:

INSERT INTO [Test] ([Test].[Test_CardNumber])
   VALUES (ENCRYPTBYPASSPHRASE(@PassPhrase, @Test_CardNumber))

SELECT [Test].[Test_Id], CONVERT(nvarchar, DECRYPTBYPASSPHRASE(@PassPhrase, Test_CardNumber)) AS [Test_CardNumber]
   FROM   [Test]
   WHERE  [Test].[Test_Id] = @Id

Moreover, you’ll have to change the column type from string to varbinary to match the ENCRYPTBYPASSPHRASE return type.

In the CodeFluent Entities context, you’ll have to add the PassPhrase parameter to the stored procedure parameters, and to the BOM generated code.

Some theory

Before anything is actually generated, CodeFluent Entities parses the model and transforms it into a complete memory representation which contains Entities, Properties, Methods, Tables, Columns, Procedures, etc. The inference engine that does this transformation is using a pipeline that’s divided into steps. CodeFluent Entities Aspects can be introduced at any step, and are able to modify the model currently in memory, therefore influencing the next steps.

Here are the main steps of the inference pipeline:

pipeline

The most important thing to note here is the fact that each step processing uses what has been created in memory during the previous steps. So for example, if you add a property to an entity early enough during inference, this property will be used to create a column automatically, all standard methods will use this property, procedures – based on methods – will use this column automatically, and so on.

At the final stage, generators (a.k.a. ‘producers’ in CodeFluent Entities terminology) will transform this in-memory model into real code, files, etc.

You can read more about the inference pipeline at http://www.softfluent.com/documentation/Aspects_Overview.html

Enough theory… Let’s do it!

To make it short, an aspect is simply a .NET class that implements the CodeFluent.Model.IProjectTemplate interface (located in CodeFluent.Model.dll).

public interface IProjectTemplate
{
    XmlDocument Run(IDictionary context);
}

You’ll find some information about this interface in previous posts http://blog.codefluententities.com/2012/07/27/codefluent-entities-writing-a-custom-aspect/

An aspect usually declares a specific XML namespace it will use for its specific XML attributes that will be store alongside CodeFluent Entities ones. These attributes should also be declared. It’s not mandatory, but it’s cool if you want to use them directly in the graphical editor. To each descriptor will correspond a property grid line in the Visual Studio standard property grid.

public class EncryptAspect : IProjectTemplate
{
    public static readonly XmlDocument Descriptor;
    public const string Namespace = "http://www.softfluent.com/aspects/samples/crypt"; // this is my custom XML namespace
    private const string PassPhraseToken = "PassPhrase";
    public Project Project { get; set; }

    static EncryptAspect()
    {
        Descriptor = new XmlDocument();
        Descriptor.LoadXml(
        @"<cf:project xmlns:cf='http://www.softfluent.com/codefluent/2005/1' defaultNamespace='MyAspect'>
            <cf:pattern name='Encrypt Aspect' namespaceUri='" + Namespace + @"' preferredPrefix='ca' step='Start'>
                <cf:message class='_doc'> CodeFluent Sample Encrypt Aspect Version 1.0.0.1 - 2013/09/20 This aspect modifies Save and Load* procedures in order to call Sql Server         ENCRYPTBYPASSPHRASE / DECRYPTBYPASSPHRASE functions.</cf:message>
                <cf:descriptor name='encrypt'
                    typeName='boolean'
                    category='Encrypt Aspect'
                    targets='Property'
                    defaultValue='false'
                    displayName='Encrypt the property'
                    description='Determines if the property must be encrypted when saving to the database.' />
            </cf:pattern>
        </cf:project>");
    }
}

When the aspect runs, it needs to be notified whenever a property is added to an entity, anywhere in the model, in order to check whether it should be encrypted. If it should be encrypted, the entity should be modified accordingly.

The aspect should also be able to modify stored procedures code, once they are are generated. The step after stored procedures inference is ‘Categories’, so we need to handle this inference pipeline step as well:

public XmlDocument Run(IDictionary context)
{
    if (context == null || !context.Contains("Project"))
    {
        // we are probably called for meta data inspection, so we send back the descriptor xml<br />
        return Descriptor;
    }

    // the dictionary contains at least these two entries
    Project = (Project)context["Project"];

    // hook on new base entities, and hook on new properties
    Project.Entities.ListChanged += (sender, e) =>
    {
        if (e.ListChangedType != ListChangedType.ItemAdded)
            return;

        var entity = Project.Entities[e.NewIndex];
        if (!entity.IsPersistent)
            return;

        if (!entity.IsProjectDerived)
        {
            entity.Properties.ListChanged += OnPropertiesListChanged;
        }
    };

    Project.StepChanging += (sender, e) =>
    {
        if (e.Step != ImportStep.Categories)
            return;

        foreach (var procedure in Project.Database.Procedures.Where(procedure => procedure.Parameters[PassPhraseToken] != null))
        {
            UpdateProcedure(procedure);
        }
    };

    // we have no specific Xml to send back, but aspect description
    return Descriptor;
}

We have designed our aspect so it considers a property should be encrypted if the XML attribute “encrypt” (in the aspect XML namespace) is set to ‘true’ and if the property is persistent (e.g. available in the persistence layer). CodeFluent Entities provides methods to read XML file attributes easily. In this example, if the attribute “encrypt” is not defined or if its value is not convertible to a boolean value, the function will return false.

private static bool MustEncrypt(Property property)
{
    return property != null && property.IsPersistent && property.GetAttributeValue("encrypt", Namespace, false);
}

Now the OnPropertiesListChanged method applies the necessary changes whenever a new property is added:

  1. Check whether it must be encrypted
    private void OnPropertiesListChanged(object sender, ListChangedEventArgs e)
    {
        if (e.ListChangedType != ListChangedType.ItemAdded)
            return;
    
        var property = ((PropertyCollection)sender)[e.NewIndex];
        if (!MustEncrypt(property))
            return;
        ...
    }
  2. Change its persistence type to Binary
    property.DbType = DbType.Binary;
    property.MaxLength = 8000;
    
  3. Add an ambient parameter “PassPhrase” to the entity. This parameter will be used for all methods without explicitly declaring it on each one. The ambient parameter will automatically be inferred as a standard parameter for stored procedures, but it will get its value from a static property or method in the BOM. In this example it will get its value from a static parameterless arbitrarily named “GetPassPhrase” method, described further down the document. Its ambient expression (the expression to use in the WHERE part of the stored procedures) must be also set. Since this parameter is not really used as a filter clause in this example, let’s simply set it to “(1=1)” which is equivalent to a “NOP” in a WHERE SQL clause (i.e: WHERE (([Test].[Test_Id] = @Id) AND (1 = 1)))
    var passPhraseParameter = new MethodParameter
        {
            Name = PassPhraseToken,
            ClrFullTypeName = "string",
            Nullable = Nullable.False,
            Options = MethodParameterOptions.Ambient |
                        MethodParameterOptions.Inherits |
                        MethodParameterOptions.UsedForLoad |
                        MethodParameterOptions.UsedForSearch |
                        MethodParameterOptions.UsedForCount |
                        MethodParameterOptions.UsedForRaw |
                        MethodParameterOptions.UsedForSave,
            ModelName = "[" + Project.DefaultNamespace + ".PassPhrase.GetPassPhrase()]", // Note the brackets here. It means that code should not be verified by CodeFluent Entities; otherwise an existing property of the current entity is expected.
            AmbientExpression = "(1=1)"
        };
    
    property.Entity.AmbientParameters.Add(passPhraseParameter);
    

Ok, we applied the required changes to the future BOM, and now we need to update stored procedures before they get generated.

CodeFluent Entities creates an in-memory Abstract Syntax Tree (AST) to represent stored procedures. This AST is independent from the target database type, and can be modified during inference as well.

To update the in-memory stored procedures AST, you can visit (using a visitor pattern) this tree and modify it when needed. We will use literal expressions (ProcedureExpressionStatement.CreateLiteral(“Sql code”)) to create our ENCRYPT/DECRYPT Sql function calls. In this case, the generated code won’t be of course platform independent anymore. This aspect should be adapted if we wanted to use it on an Oracle, MySQL or PostgreSql database.

private static void UpdateProcedure(Procedure procedure)
{
    procedure.Parameters[PassPhraseToken].DefaultValue = null; // This means the passphrase must be provided, and cannot be null
    if (procedure.ProcedureType == ProcedureType.SaveEntity)
    {
        procedure.Body.Visit(s =>
        {
            var statement = s as ProcedureSetStatement;
            if (statement == null || statement.LeftExpression == null || statement.RightExpression == null || !MustEncrypt(statement.LeftExpression.RefColumn))
                return;

            string parameterName = statement.RightExpression.Parameter.Name;
            statement.RightExpression.Literal = ProcedureExpressionStatement.CreateLiteral(string.Format("ENCRYPTBYPASSPHRASE(@{0}, @{1})", PassPhraseToken, parameterName));
            statement.RightExpression.Parameter = null;

            // Column is of type varbinary but parameter must be of type string
            var parameter = procedure.Parameters[parameterName];
            if (parameter != null)
            {
                parameter.DbType = DbType.String;
            }
        });
        return;
    }
  
    procedure.Body.Visit(s =>
    {
        var statement = s as ProcedureSetStatement;
        if (statement == null || statement.LeftExpression == null || !MustEncrypt(statement.LeftExpression.RefColumn))
            return;

        statement.As = new ProcedureExpressionStatement(statement, ProcedureExpressionStatement.CreateLiteral(statement.LeftExpression.RefColumn.Column.Name));
        statement.LeftExpression.Literal = ProcedureExpressionStatement.CreateLiteral(string.Format("CONVERT(nvarchar, DECRYPTBYPASSPHRASE(@{0}, {1}))", PassPhraseToken, statement.LeftExpression.RefColumn.Column.Name));
        statement.LeftExpression.RefColumn = null;
    });
}

That’s it, the aspect is finished! But we want to use it in Visual Studio now…

Integrate the aspect in the visual modeler

To integrate your aspect, add a reference to the class library project that contains the aspect (it can be in the same solution):

integrate_aspect_in_modeler1

integrate_aspect_in_modeler2

Use the reference context menu to add an aspect (compiled) from this reference.

integrate_aspect_in_modeler3

The following dialog box will display what aspects are available in the compiled project, and what are the descriptors for the selected aspect:

integrate_aspect_in_modeler4

To use the aspect, a developer has to select the concept targeted by a given descriptor (Entity, Property, Method, etc.) and use the “Aspects and Producers Properties” tab in the Visual Studio standard property grid:

integrate_aspect_in_modeler5

Now you can build your model, add the logic to get the pass phrase, and enjoy :)

public static class PassPhrase
{
    public static string GetPassPhrase()
    {
        return "hello world";
    }
}

class Program
{
    static void Main(string[] args)
    {
        DemoEncrypt entity = new DemoEncrypt();
        entity.CardNumber = "0123-4567-8901-2346-5678";
        entity.Save();
        entity.Reload(CodeFluentReloadOptions.Default);
        Console.WriteLine(entity.Trace());
    }
}

The full source code is available here: DemoEncrypt.zip

Conclusion

With the power of CodeFluent Entities and approximately 180 lines of C# code, we have added the possibility to add database encryption to the columns of our choice and the tables of our choice. This aspect is 100% reusable across all our projects. Can you do this without CodeFluent Entities?

Cheers,
Gerald Barré

CodeFluent Entities and the Localization Aspect: how to show localizable properties in the design surface

April 12, 2013 Leave a comment

Using CodeFluent Entities, you can use “Member Format Expressions” (a.k.a “MFEX”) to customize the way names of entities and properties are formatted. We already have blogged about that feature here:

http://blog.codefluententities.com/2012/01/12/codefluent-entities-member-format-expressions-part-1/

http://blog.codefluententities.com/2012/01/13/member-format-expressions-part-2/

In this post, we’re going to explain how to create an MFEX that will display this on the surface when selected:

As you clearly see, I have defined two localized properties on my model (on the Name property of the Campaign entity, and on the Name property of the Space entity). But wait! A red ‘Localizable’ text is appended to the right of the property name. How do I do this?

Let start with this expression, demonstrated in the previous blog post:

<font condition=IsKey color=’red’>{#Name}<else/><font color=’blue’>{#Name}</font></font>

With this expression, key properties will be shown in red and other properties will be shown in blue. The condition syntax is in fact a .NET expression. IsKey is a property (of Boolean type) of the CodeFluent.Model.Property class. If I was writing C# code, I could do this ‘if (myProperty.IsKey)’ etc… But MFEX conditions also support “dot navigation”, so you could write this:

<font condition=Rules.Count color=’red’>{#Name}<else/><font color=’blue’>{#Name}</font></font>

In this case, we are navigating on the Rules property of the CodeFluent.Model.Property class, and on the Count property of the CodeFluent.Model.RuleCollection class. Count is of type Int32 and the condition evaluator needs a Boolean, so it just tests the resulting Int32 against 0, so the condition is equivalent to “Rules.Count > 0″. So this expression will show in red all the properties that have CodeFluent Entities rules defined on it.

Well, it turns out MFEX also supports (partially) methods in conditions. So, here is how to display localized properties:

{Name} <font color="red" condition="'true'=Element.GetAttribute('localizable','http://www.softfluent.com/codefluent/patterns/localization/2008/1')">Localizable</font>

Note : it’s important to respect the difference between the single quote and the double quote characters here.

So, the condition is an expression with an operation. = is the ‘equality insensitive’ operator. It means it uses equality but is case insensitive in the case of string comparison. Here is the list of available operators:

  • == or = : Equal
  • != or <> : NotEqual
  • [=CS] : EqualCaseSensitive
  • [!=CS] : NotEqualCaseSensitive
  • >= : GreaterThanEqual
  • <= : LesserThanEqual
  • > : GreaterThan
  • < : LesserThan
  • [StartsWith] : StartsWith
  • [StartsWithCS] StartsWithCaseSensitive
  • [EndsWith] : EndsWith
  • [EndsWithCS] : EndsWithCaseSensitive
  • [Contains] : Contains
  • [ContainsCS] : ContainsCaseSensitive

The condition will compare the literal string ‘true’ with the result of a method call that starts on the Element property of the CodeFluent.Model.Property class, which is of System.Xml.XmlElement type. XmlElement has a method called GetAttribute that simply gets an XML attribute given it’s local name (without a prefix if it’s in a specific XML namespace) and the XML namespace.

So, with this condition, we are accessing the XML element underlying the CodeFluent Entities Property concept. When a property is marked as localized, the corresponding XML text (in memory) is just modified like this:

  <Campaign namespace="SoftFluent.Advertising1" >
    <Id />
    <Name _loc:localizable="true" />
  </Campaign>

Happy MFEXing!

The CodeFluent R&D Team.

Using the SQL Server Template Producer to generate Clustered Indexes

November 27, 2012 Leave a comment

In this post we’ll see how using CodeFluent Entities’ SQL Server Template Producer, you can generate SQL scripts using  the built-in template engine whilst accessing the inferred meta-model, and then automatically deploy the generated script on your desired server.

By default SQL Azure and SQL Server add the clustered index on the primary key if a clustered index on the table does not already exist and if you do not specify a unique nonclustered index. In the case where the primary key is of GUID type it won’t be efficient. The reason for this is that GUIDs are generated in non-sequential order and SQL Server orders a clustered index sequentially. It will work – SQL Server will let you build a clustered index around a unique identifier column, however it will cause the SQL Server to do unnecessary work and cause performance slowdowns.

That being said, what we can do is using the SQL Template Producer to remove all inefficient clustered indexes and recreate them on the right columns. Let’s even go a bit further and create a little aspect that will add a property on each property to tell if a clustered index needs to be created or not on that particular property.

Add a new Part called IsClusteredIndexAspect and past it the following code (replacing the defaultNamespace’s value by yours):

<cf:project xmlns:cf=”http://www.softfluent.com/codefluent/2005/1” defaultNamespace=”yourNamespace”>

<cf:pattern name=”IsClusteredIndex Aspect” namespaceUri=”http://www.sample.com/aspects/isclusteredindexaspect/2012/11” preferredPrefix=”sa” step=”Tables”>

<cf:message class=”_doc”>
This aspect creates an extra IsClusteredIndex bool property on every property.
</cf:message>

<cf:descriptor name=”IsClusteredIndex” typeName=”boolean” targets=”Property” defaultValue=”false” displayName=”IsClusteredIndex” description=”Should the IsClusteredIndex Aspect apply to this property?” />

</cf:pattern>
</cf:project>

This will create our aspect and add a IsClusteredIndex property on each property in the “Aspects and Producers” property grid:

image

You can by now choose which property you want to use as a clustered index. Obviously this property should be set to true only on one property by entity since clustered index cannot be applied on several columns.

Now let’s write a script that will remove all clustered indexes and then create new ones based on the columns selected thanks to our aspect. In a file called “[Template]CreateIndexes.sql” add it the following code (Note that this code is only an illustration for this post, it does not take into account constraints, primary keys and so on):

[%@ namespace name="CodeFluent.Model"%]
[%@ namespace name="CodeFluent.Model.Persistence"%]
[%@ namespace name="CodeFluent.Producers.SqlServer"%]
/* [%=Producer.GetSignature()%] */

[%foreach (Entity e in Producer.Project.Entities)
{%]
    --remove any existing clustered index on e.Table.FullName
    --[...]
  
   [%foreach (Property p in e.Properties)
    {
        if (p.GetAttributeValue("sa:IsClusteredIndex", false))
        {%]      
        --create your index such as:
        CREATE CLUSTERED INDEX [CL_[%=p.Entity.Table.Name%]] ON [%=p.Entity.Table.FullName%] ([[%=p.Column.Name%]]);
        GO
        [%}
    }
}%]

 

Create a folder called “Template” under the file folder of your CodeFluent Entities project:

image

Right click on that folder and choose “Add existing item” then browse to your template file and select it.

Now in your CodeFluent Entities project, add an instance of the SQL Server Template Producer with your Template directory as the “Source Directory” and set the “Target Directory” to the project of you choice and then build over your model. The SQL Server Template Producer will generate a script file from your template, and run the script on the server removing and creating clustered indexes. Therefore using the template producer you can quickly create complex scripts by taking advantage of the meta model.

 

Cheers,

Thibault NESTOR

CodeFluent Entities: Writing a custom aspect

July 27, 2012 1 comment

In the previous post, we’ve seen that CodeFluent Entities infers a meta-model from our model and that we could interact with this meta-model to apply application wide changes. In this post we’ll see how Smile

Step 1: Writing your aspect

As an example we’ll write an aspect which will add a “IsDeleted” property on all entities by default.

First of all, we added a new class library project named “Demo.Aspects” to our solution and added the following references: CodeFluent.Model.dll,  CodeFluent.Model.Common.dll and CodeFluent.Runtime.dll. Aspects can either be written in XML or in .NET. In my opinion, it’s easier to start writing aspects in .NET as you’ll have Visual Studio’s IntelliSense which will help you out using the API.

In practice, an aspect is a class implementing the IProjectTemplate interface. This interface contains a single method “Run” which takes as a parameter a “context”.

Here’s a sample implementation:

using System.Collections;
using System.Xml;
using CodeFluent.Model;

namespace Demo.Aspects
{
    public class MyAspect: IProjectTemplate
    {
        public static readonly XmlDocument Descriptor;
        public const string MyAspectNamespace = "http://www.mycompany.com/aspects/myaspect/2013/1";

        static MyAspect()
        {
            Descriptor = new XmlDocument();
            Descriptor.LoadXml(
@"<cf:project xmlns:cf='http://www.softfluent.com/codefluent/2005/1' defaultNamespace='MyAspect'>
    <cf:pattern name='My Aspect' namespaceUri='" + MyAspectNamespace + @"' preferredPrefix='mya' step='Tables'>
        <cf:message class='_doc'>This is some description for our custom aspect.</cf:message>
        <cf:descriptor name='enable'
            typeName='boolean'
            category='My Custom Aspect'
            targets='Entity'
            defaultValue='false'
            displayName='Add the MyCustomProperty'
            description='Description for our custom descriptor.' />
    </cf:pattern>
</cf:project>");
        }

        public XmlDocument Run(IDictionary context)
        {
            if (context == null || !context.Contains("Project"))
            {
                // we are probably called for meta data inspection
                return Descriptor;
            }

            // the dictionary contains at least these two entries
            XmlElement element = (XmlElement)context["Element"];
            Project project = (Project)context["Project"];

            foreach (Entity entity in project.Entities)
            {
                Property property = new Property();
                property.Name = "IsDeleted";
                property.TypeName = "bool";
                entity.Properties.Add(property);
            }

            // we have no specific Xml to send back, but aspect description
            return Descriptor;
        }
}

The method returns a XmlDocument which is described above as a string. It could also be written on a separate file.

As you can see this XML defines the aspect’s name, its XML namespace, and how it extends the CodeFluent schema (here it adds a “mya:enable” attribute on entities, whose default value is false).

Step 2: Using your aspect

Compile the “Demo.Aspects” project.

In the solution explorer, select the “Aspects” folder and click “Add Existing Aspect…”. Browse for our Demo.Aspects.dll that we created previously and here’s what you should see:

image

Great! As you can see, information returned by our descriptor is properly parsed and displayed Smile

Hit OK, and now by default all your entities should have an extra “IsDeleted” property added when the model is inferred, which means that a “IsDeleted” column will be created for all tables, all stored procedures will be updated to include this column, and it’s the same in the middle tier and the UI tier. You can view this by clicking on the “View Inferred Model” button available in the ribbon:

image

Useful Links

Hope this helps,

Carl Anderson

New CodeFluent Entities Version Released!

March 16, 2012 1 comment

A new CodeFluent Entities version (646) has been released and is publicly available!

You can download it from here: CodeFluent Entities Download Page.

 

Enhanced Visual Studio 11 Beta integration

Remember the “CodeFluent Entities & Visual Studio 11 Beta” post? Well we kept our word and corrected the glitches Winking smile

 

SQL Server 2012 is supported by the SQL Server Producer

More details in this post: Added SQL Server 2012 Support

 

Enumeration declarations now support named values

You can now do stuff with multi-valued enumerations such as (supported separators are , ; | and +):

    <cf:enumeration name="WeekDays" multivalue="true" >
      <None />
      <Sunday />
      <Monday />
      <Tuesday />
      <Wednesday />
      <Thursday />
      <Friday />
      <Saturday />
      <WeekDays value="Monday,Tuesday,Wednesday,Thursday,Friday" />
    </cf:enumeration>

(Thank you GregD for the suggestion)

 

Project Level “Implements” and “Set Implements” rules

You can define “Implements” and “Set Implements” rules at project level so all generated classes implement a specific interface or derive from a specific class (http://forums.softfluent.com/default.aspx?g=posts&t=395).

 

Transaction rules

Transaction rules can now be added on methods other than Save and Delete, including custom ones. Developers don’t have to write the TransactionScope code themselves when adding a new custom method: all it takes is selecting your method and add a transaction rule on it Smile

 

The Modeler now supports Aspect assemblies

If you are familiar with CodeFluent Aspects, you already know that you can develop aspects in XML or in .NET assemblies.

You’ll be glad to know that the Modeler now fully supports .NET assemblies including assemblies containing multiple aspects (e.g. Contoso.Aspects.dll). In such cases it displays available aspects in the assembly and lets you select the one you want to use in your current CodeFluent Entities project.

 

Miscellaneous changes

Some other miscellaneous things were also added/fixed/enhanced such as the enhancing TFS integration or adding support for the CTRL+A keyboard shortcut in the method body editor was added.

 

We would love to hear your feedbacks, don’t hesitate to share your feedbacks on our forums or by commenting this post!

 

Enjoy,

Carl Anderson

Follow

Get every new post delivered to your Inbox.

Join 51 other followers