-
Notifications
You must be signed in to change notification settings - Fork 0
Built In Aspects
This section details and illustrates how to use out-of-the-box (or “Official”) shipped aspects.
To add an aspect, use the Aspects folder node in the Visual Studio project. By default, the Browse dialog box will point you to the Official aspects directory which should be something like:
%localappdata%\Microsoft\VisualStudio\16.0_26dd5244\Extensions\SoftFluent S.A.S\SoftFluent CodeModeler\1.3.0.0\Patterns
Note: Adapt 16.0_26dd5244 and/or 1.3.0.0 to your context.
The dialog box will automatically parse (or compile) the path and show the metadata it has extracted from it, as in the following picture:
The aspect description is a text, and the Aspect Descriptors are attributes specific to the selected aspect, that can be used in the Visual Studio property grid when the aspects is added to the project.
Once an aspect has been added to the project, you can configure its attributes (from descriptors definition) using the Visual Studio property grid, “Aspects and Producers Properties” tab (1), grouped by Descriptor Category (2):
Note: All official SoftFluent aspects are developed using Xml parts, so you can easily see how they are programmed and modify their behavior if needed.
The localization aspect enables database localization (a.k.a. dynamic localization as opposed to using resources in .resx files) support.
To enable the aspect, you must add the “Softfluent.Localization.xml” file.
For example, for this simple model with a Ministry entity:
You can enable it for a given property using the “Aspects and Producers Properties” tab (once the aspect has been added to the project):
When properties have been defined as localizable, once the project is built, there is one extra table per entity that contains localizable properties (with proper foreign keys in place as well):
And all load stored procedures have also been modified to join with the localized table:
CREATE PROCEDURE [Commerce].[Ministry_LoadAll]
(
@Lcid [int] = 1033,
@_orderBy0 [nvarchar] (64) = NULL,
@_orderByDirection0 [bit] = 0
)
AS
SET NOCOUNT ON
IF(@Lcid IS NULL)
BEGIN
SELECT DISTINCT [Commerce].[vMinistryLocalized].[Ministry_Id], [Commerce].[vMinistryLocalized].[Ministry_Label], [Commerce].[vMinistryLocalized].[Lcid], [Commerce].[vMinistryLocalized].[_rowVersion], [Commerce].[vMinistryLocalized].[_trackCreationTime], [Commerce].[vMinistryLocalized].[_trackLastWriteTime], [Commerce].[vMinistryLocalized].[_trackCreationUser], [Commerce].[vMinistryLocalized].[_trackLastWriteUser]
FROM [Commerce].[vMinistryLocalized]
WHERE ([Commerce].[vMinistryLocalized].[Lcid] = 65536)
ORDER BY [Commerce].[vMinistryLocalized].[Lcid] ASC
END
ELSE
BEGIN
SELECT DISTINCT [Commerce].[vMinistryLocalized].[Ministry_Id], [Commerce].[vMinistryLocalized].[Ministry_Label], [Commerce].[vMinistryLocalized].[Lcid], [Commerce].[vMinistryLocalized].[_rowVersion], [Commerce].[vMinistryLocalized].[_trackCreationTime], [Commerce].[vMinistryLocalized].[_trackLastWriteTime], [Commerce].[vMinistryLocalized].[_trackCreationUser], [Commerce].[vMinistryLocalized].[_trackLastWriteUser]
FROM [Commerce].[vMinistryLocalized]
WHERE ((@Lcid = [Commerce].[vMinistryLocalized].[Lcid]) OR ([Commerce].[vMinistryLocalized].[Lcid] = 65536))
ORDER BY [Commerce].[vMinistryLocalized].[Lcid] ASC
END
Note: To understand the details we suggest you look at the aspect’s source code
Once at least a property has been defined as localizable, the Instance Editor has an extra “Localized Values” tab that allows you to define localizable values per localizable property:
This tab allows you to edit the culture-specific property value. You can add any culture you want.
This is the content of the database once the project has been built:
And the following C# code:
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
foreach (var ministry in MinistryCollection.LoadAll())
{
Console.WriteLine(ministry.Label);
}
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("fr-FR");
foreach (var ministry in MinistryCollection.LoadAll())
{
Console.WriteLine(ministry.Label);
}
Will output this:
Education and Research
Education et Recherche
Note: This has been done without changing anything in the model (beyond setting True to localizable properties. This demonstrates the huge leverage effect that aspects can have on a CodeModeler model at BOM or persistence level.
The Text Search Pattern adds methods to search an entity using tokenization techniques (also known as Middle-Of-Word text search technique). The search is case insensitive and accent insensitive. Say you want to create a screen in your application where users can search their contacts: the Text Search Aspect provides a way to implement it.
Note: This aspect is only supported using Microsoft SQL Server databases. It does not support entities with composite or object keys.
To enable the aspect, you must add the “Softfluent.TextSearch.xml” file.
For example, let’s define this model:
And add the aspect:
Now we can set the “Text Search” attribute to True in the property grid (“Aspects and Producers Properties” tab):
And set the “Extract” attribute to True for the Description, Email, FirstName, LastName properties, again using the property grid (“Aspects and Producers Properties” tab):
After having built the project, you'll notice that, in the persistence layer:
-
a new table named TextSearch[KeyType] was created,
-
a new stored procedure named [EntityName]_TextSearchTokens was created.
Likewise, in the Business Object Model (BOM) the following classes were created:
-
Utilities\TextSearchEntityType.cs
-
Utilities\TextSearchUtilities.cs
-
Utilities\TextSearch[KeyType].cs (one per key type used),
-
Utilities\TextSearch[KeyType]Collection.cs (one per key type used).
In our example, the Contact entity is text searchable, so its resulting ContactCollection class will have two new methods:
-
TextSearchTokens(bool oneOrMore, string[] tokens)
-
PageTextSearchTokens(int pageIndex, int pageSize, CodeModeler.Runtime.PageOptions pageOptions, bool oneOrMore, string[] tokens)
The first one allows you to retrieve all results of the search, when the second one is a paged and sortable version of the first method.
If you set-up the Text Search Aspect from the very beginning of your application's life, tokens will be created and deleted along their represented instance. However, if you add the Text Search Aspect on a database already containing data, you'll have to build all tokens for instances that where created prior to the addition of the aspect. In that matter, the TextSearchUtilities class provides a method named UpdateAll.
Create yourself a console application in which the UpdateAll method is called once per searchable class. For instance:
class Program
{
static void Main(string[] args)
{
TextSearchUtilities.UpdateAll(typeof(ContactCollection));
}
}
This aspect adds method(s) to all entities having at least one many to many relations.
Added methods enable association management without loading the corresponding collections. Such methods can be very useful when creating your user interface.
Using this aspect is straightforward: import the part in your project, specify it's run step, and it'll automatically be added on many to many entities.
To enable the aspect, you must add the “Softfluent.AssociationManage.xml” file.
In the end, a method will be added to each entity holding a many to many relation to another entity. This method will be named [RelationPropertyName]Manage:
public static bool ProductsManage(System.ComponentModel.CollectionChangeAction action, int orderId, int productId)
{
if ((action == default(System.ComponentModel.CollectionChangeAction)))
{
throw new System.ArgumentNullException("action");
}
if ((orderId == CodeModelerPersistence.DefaultInt32Value))
{
throw new System.ArgumentNullException("orderId");
}
if ((productId == CodeModelerPersistence.DefaultInt32Value))
{
throw new System.ArgumentNullException("productId");
}
bool ret = CodeModelerPersistence.DefaultBooleanValue;
CodeModeler.Runtime. CodeModelerPersistence persistence = CodeModelerContext.Get(Sample.Constants.SampleStoreName).Persistence;
persistence.CreateStoredProcedureCommand(null, "Order", "ProductsManage");
persistence.AddParameterEnumInt32("@action", action, new System.ComponentModel.CollectionChangeAction());
persistence.AddParameter("@orderId", orderId);
persistence.AddParameter("@productId", productId);
System.Data.IDataReader reader = null;
try
{
reader = persistence.ExecuteReader();
if ((reader.Read() == true))
{
ret = ((bool)(ConvertUtilities.ChangeType(reader.GetValue(0), typeof(bool), null)));
}
}
finally
{
if ((reader != null))
{
reader.Dispose();
}
persistence.CompleteCommand();
}
return ret;
}
The AutoFormattable aspect adds enhanced formatting capabilities to entities.
At production time, the AutoFormattable aspect iterates on entities with the “Auto Formattable” attribute set to True (this is the default value) and adds an IFormattable implementation to the generated entity classes.
To enable the aspect, you must add the “Softfluent.AutoFormattable.xml” file.
The following example applies the AutoFormattable pattern at Salesman and Customer entity level (it’s also possible to set the “Is Auto Formattable” at project level to enable it for all entities):
ToString methods will be generated in the entity class as well as the collection class. Those methods will provide a string representation of the current class instance. The new methods are based on another class named FormatUtilities which is a CodeModeler-provided helper class that also gets generated by the aspect.
The FormatUtilities class provides a set of methods useful in order to format string representations of an instance of an entity (or a collection of entities). For instance, a developer can specify a format value such as "{0}:Name" which indicates to return the Name property value of the specified instance.
Furthermore, the format string supports dots such as "{0:Orders.Code}", or "{0:Orders.Products.Count}" which allows to navigate in the model to display hierarchical contents.
This aspect adds a DeepLoadAll method on a given set of entities. The DeepLoadAll method loads a complete inheritance hierarchy using a single server call.
Note: This aspect only supports SQL Server targets.
To enable the aspect, you must add the “SoftFluent.HierarchyDeepLoad.xml” file.
For example, let’s define this model:
And add the aspect. Now we can set the “Deep Load” attribute to True in the property grid (“Aspects and Producers Properties” tab):
This is what the SQL producer adds, Customer_DeepLoadAllProc stored procedure that loads all records from User and Contractor tables:
CREATE PROCEDURE [Commerce].[User_DeepLoadAllProc]
AS
SET NOCOUNT ON
SELECT [User].[User_Id],[User].[User_Name],[User].[_typeName],[User].[_trackLastWriteTime],[User].[_trackCreationTime],[User].[_trackLastWriteUser],[User].[_trackCreationUser],[User].[_rowVersion]
FROM [Commerce].[User]
WHERE [User].[_typeName]='Commerce.User'
SELECT [User].[User_Id],[User].[User_Name],[User].[_typeName],[User].[_trackLastWriteTime],[User].[_trackCreationTime],[User].[_trackLastWriteUser],[User].[_trackCreationUser],[User].[_rowVersion],[Contractor].[Contractor_Level]
FROM [Commerce].[User],[Commerce].[Contractor]
WHERE [User].[_typeName]='Commerce.Contractor' AND [User].[User_Id]=[Contractor].[User_Id]
And the BOM producer adds a DeepLoadAll method to the UserCollection class that calls the Customer_DeepLoadAllProc stored procedure to load all User and Contractor instances.
public static Commerce.UserCollection DeepLoadAll()
{
Commerce.UserCollection items = new Commerce.UserCollection();
CodeModeler.Runtime.CodeModelerPersistence persistence = CodeModelerContext.Get(Commerce.Constants.CommerceStoreName).Persistence;
persistence.CreateStoredProcedureCommand("Commerce.User_DeepLoadAllProc");
using (System.Data.IDataReader reader = persistence.ExecuteReader())
{
do
{
while (reader.Read())
{
string typeName = CodeModelerPersistence.GetReaderValue(reader, "_typeName", (string)null);
Commerce.User item = null;
if (typeof(Commerce.Contractor).FullName == typeName)
{
item = new Commerce.Contractor();
}
if (item == null)
{
item = new Commerce.User();
}
((CodeModeler.Runtime.ICodeModelerEntity)item).ReadRecord(reader);
items.BaseAdd(item);
item.EntityState = CodeModeler.Runtime.EntityState.Unchanged;
}
}
while (reader.NextResult());
CodeModeler.Runtime.CodeModelerPersistence.CompleteCommand(Commerce.Constants.CommerceStoreName);
}
return items;
}
- 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