Archive

Archive for December, 2012

Working with both the cache sub producer and the localization aspect

December 27, 2012 1 comment

Are you planning on using caching and localization for one of your CodeFluent Entities projects? If so, this article is for you.

CodeFluent Entities comes with a sub producer that handles cache in a simple and powerful way. The cache sub producer uses a class called SimpleCacheManager, which is based on the ASP.NET cache. Read more about the SimpleCacheManager class here: http://www.softfluent.com/documentation/BOM_Caching.html.

Also, CodeFluent Entities offers a variety of aspects, including the localization aspect. This localization aspect allows you to select properties that need to handle localized values. Read more about the localization aspect here: https://blog.codefluententities.com/2011/03/25/codefluent-entities-aspects/

At the moment, CodeFluent Entities does not support the use of both of these features at the same time. If you try to use them in a project, you will realize that objects are cached with the same key (this key is used to save/retrieve cached objects), regardless of the current localization.

The following model uses the cache sub producer, as well as the localization aspect. The label property of the Product entity is localized, and the cache sub producer uses the standard SimpleCacheManager class to handle the cache of our application:

<cf:import path="Default.Surface.cfp" />
<cf:pattern path="C:\Projects\sfdev01\CodeFluent\CodeFluent.FX2\CodeFluent.Model\Patterns\SoftFluent.Localization.xml" step="Methods" name="CodeFluent Localization Aspect" />
<cf:entity name="Product" namespace="Locali" categoryPath="/Locali">
    <cfpc:cache typeName="CodeFluent.Runtime.Caching.SimpleCacheManager, CodeFluent.Runtime" enabled="true"/>
    <cf:property name="Id" key="true" />
    <cf:property name="Label" _loc:localizable="true" />
</cf:entity>
<cf:producer name="Business Object Model (BOM)" typeName="CodeFluent.Producers.CodeDom.CodeDomProducer, CodeFluent.Producers.CodeDom">
    <cf:configuration compileWithVisualStudio="true" compile="false" codeDomProviderTypeName="CSharp" targetDirectory="..\LocaliCache" cfx:targetProject="..\LocaliCache\LocaliCache.csproj" cfx:targetProjectLayout="Update">
        <subProducer typeName="CodeFluent.Producers.Cache.CacheProducer, CodeFluent.Producers.Cache, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1bb6d7cccf1045ec" defaultEnabled="true" />
    </cf:configuration>
</cf:producer>
<cf:producer name="SQL Server" typeName="CodeFluent.Producers.SqlServer.SqlServerProducer, CodeFluent.Producers.SqlServer">
    <cf:configuration produceViews="true" targetDirectory="..\LocaliCache\Scripts" createDatabase="true" updateDatabase="true" cfx:targetProject="..\LocaliCache\LocaliCache.csproj" cfx:targetProjectLayout="Update, DontRemove" createDiffs="true" targetVersion="Sql2008">
    </cf:configuration>
</cf:producer>

Let’s use a console application to test this model.

static void Main(string[] args)
{
    Product p = new Product();
    Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
    p.Label = "name of the product";
    p.Save();
    Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr-FR");
    p.Label = "nom du produit";
    p.Save();

    Guid id = p.Id;
    // US localization
    Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
    // first load, not cached
    p = Product.Load(id);
    Console.WriteLine(Thread.CurrentThread.CurrentUICulture.Name + ": " + p.Label);
    // second load, cached
    p = Product.Load(id);
    Console.WriteLine(Thread.CurrentThread.CurrentUICulture.Name + ": " + p.Label);
    // French localization
    Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr-FR");
    // third load, cached
    p = Product.Load(id);
    Console.WriteLine(Thread.CurrentThread.CurrentUICulture.Name + ": " + p.Label);
    // fourth load, cached
    p = Product.Load(id);
    Console.WriteLine(Thread.CurrentThread.CurrentUICulture.Name + ": " + p.Label);
    Console.Read();
}

Running this program will output this:

en-US: name of the product
en-US: name of the product
fr-FR: name of the product
fr-FR: name of the product

While the first product is loaded from the database, the three other products are loaded from cache. Although the localization was changed, the cached products were retrieved from the same (cached) key.

Fortunately, CodeFluent Entities offers full flexibility of the cache sub producer, by letting you select your own cache handler! Let’s now build our own class that will handle the localization. To do so, we will create a class called LocaleCacheManager that extends the SimpleCacheManager class. In this example, a class is added to its own class library project, so that we can add its reference to our BOM project:

using System;
using System.Collections;
using System.Web.Caching;
using CodeFluent.Runtime.Caching;
using CodeFluent.Runtime.Utilities;
using System.Threading;
namespace Locali
{
    public class LocaleCacheManager : SimpleCacheManager
    {
        public override void Add(string domain, string key, object value, IDictionary context)
        {
            if (domain == null)
                throw new ArgumentNullException("domain");
            if (key == null)
                throw new ArgumentNullException("key");
             base.Add(domain, BuildKey(key), value, context);
        }

        public override void Remove(string domain, string key)
        {
            if (domain == null)
                throw new ArgumentNullException("domain");
            if (key == null)
                throw new ArgumentNullException("key");
            base.Remove(domain, BuildKey(key));
        }

        public override object Get(string domain, string key)
        {
            if (domain == null)
                throw new ArgumentNullException("domain");
            if (key == null)
                throw new ArgumentNullException("key");
            return base.Get(domain, BuildKey(key));
        }

        private static string BuildKey(string key)
        {
            return key + ":" + Thread.CurrentThread.CurrentUICulture.LCID;
        }
    }
}

This class overrides three methods and changes the key which is used to save and retrieve cached objects. The key is modified so that the culture (localization) is added to it. This allows one object to have multiple cached versions, depending on its localization.

Now, we need to update our model by modifying the class that will handle our cache:

<cfpc:cache typeName="Locali.LocaleCacheManager, Locali" enabled="true"/>

That’s all we need to do! We can now regenerate the model and run the application. The new output of our application is:

en-US: name of the product
en-US: name of the product
fr-FR: nom du produit
fr-FR: nom du produit

Our own cache sub producer works perfectly with the localization aspect!

What do you think? Any suggestions regarding this LocaleCacheManager class?

Note (2012/2/7): the LocaleCacheManager has been added to the CodeFluent.Runtime.dll (namespace CodeFluent.Runtime.Caching) starting with build 700.

Vincent Patry

Website Templates and Custom Renderers

December 4, 2012 Leave a comment

There are basically two ways to customize web sites generated by CodeFluent Entities:

1) edit the generated files in place. The generated code is very readable, and by design easy to customize.
2) modify how the out-of-the-box site is generated. Please note these auto-generated sites are very well suited for back-end/back-office/administration/testing/sample purpose. However, they are generated using site templates, and these templates can be customized. We will try to explain how.

Site templates are located in \Program Files (x86)\SoftFluent\CodeFluent\Modeler\Templates\UI. Here you will find the various templates for ASP.NET, MVC, Ajax, etc…

These templates are a good start to build your own. Each template is composed of two directories:

    • A directory that contains a cf_template.xml which is the template manifest, used by tools such as Visual Studio. Let’s say it’s named ‘MyAspNet’ for our example.
    • Another directory named [directoryName]_Renderers. So it should be named MyAspNet_Renderers for our example.

Both directories can contain template files (text files with the .tpl extension) or any other files. Template files are files that are executable files and can run in the context of the model being produced (more here:http://www.softfluent.co…_TheTemplateEngine.html and here: http://www.softfluent.co…s_TemplateProducer.html).

The first directory will define the target site files. The renderers directory will contains the renderers implementation. Now the question: what is a renderer?

A renderer in a CodeFluent Entities model is an abstract platform-independent item which basically has two important properties: a name, and a type (unspecified, read, write, create, column, parameter), and will be used to define how a concept will be rendered ultimately on a specific platform (whatever that platform will be: web, winforms, WPF, etc.).
Renderers can be associated with properties, view properties, entities or method parameters. Renderers can be shared.

For example, let’s suppose you handle a lot of IP Address in your application. There’s nothing built-in in the product but many entities have properties that are of the IP Address type. Then, you would declare a renderer named ‘IPAddress’ (for example) and associate this renderer to all the properties of that type.
At production time, a producer such as the ASP.NET producer will relate the renderer definition to a concrete UI artifact using it’s name and type. In the case of the ASP.NET producer, a renderer will be simply represented by a file, containing some HTML (or ASP.NET) fragment. You can see what the out-of-the-box renderers look like if you open the files in the [directory]_Renderers directory of the out-of-the-box templates. A renderer file name starts with “Renderer.”.

Let’s go to CodeFluent Entities installation directory, and navigate through the Templates\UI directory. Here you can see a set of folders containing the templates and renderer.

We’re going to create a new template from an existing one and also add a renderer. To do so copy the following folders to a folder called “UI” in the location of your choice (Note the folder root must contain ‘UI’):

  • AspNet
  • AspNet_Renderers
  • AspNetWebForms
  • AspNetWebForms_Renderers

Rename the last two copied folders like this:

  • CustomWebsite
  • CustomWebsite_Renderers
    You should have something like this:

image

We We copied two website templates because the AspNetWebForms template inherits the AspNet template.

You can already customize your website here. For instance edit CustomWebsite\Default.aspx.tpl, and modify the page title by modifying the
<%@ Page %> tag with something like this:

<%@ Page Title=”This is my Custom Website Home Page” …

Now add a new file called Renderer.MyContactSource.tpl in the CustomWebsite_Renderers folder. Open this file in a text editor such as NotePad and type the following text: “this is my custom Contact Source”. It is an unspecified renderer because the file name does not end with Write, or Column for example.

image

Now start Visual Studio, and create a CodeFluent Entities project (Demo.ContactManager, C#, SQL Server, ASP.NET WebForms). Go in the Contact Entity, select the ContactSource property and add a ‘MyContactSource’ renderer of unspecified type:

Now, double-click on the ASP.NET Webforms producer, and configure it to point to your custom site template:

Build the project, and execute the generated web site. You should now see this when you try to Add or Edit a new Contact:

what happened is, at generation time, every time a property is associated with the MyContactSource renderer, it will be rendered using the content of the Renderer.MyContactSource.tpl file. This file just contain a pure (HTML) text, but it could contain anything you would like, including server-side ASP.NET controls. This file can also contain template code so you can indeed have it vary using the model and the source object it was instantiated from at generation time.
For example, if you don’t like the drop down list that’s used to represent relations by default, you can just modify the Renderer.Relation.Write.tpl content and this will change how relations are rendered (in write mode) in every screen of the application, because Relation is the name of the default renderer associated with M:0/1 or 0/1:M relations. Association is the name of the default renderer associated with M:N relations.

Besides, navigate back to the homepage and note that Tab’s title reflects our change made in the Default.aspx file.

Therefore, thanks to Renderers and custom templates you can generate full customized websites.

Cheers

New Online Documentation Release: R17

December 4, 2012 Leave a comment

New Online Documentation Release: R17

A new version (R17) of the product documentation was released!

This new version includes:

Additions:

    • New articles:
      • About Producers
      • Automatic Traces Producer
      • ASP.NET Web Site Producer V2

Updates:

  • Entire “Generating” chapter was updated to match how producers are named and organized in the Modeler,
  • Extra “See also” links on most of producer pages

As well as miscellaneous corrections as usual Smile

Interested in some extra articles?
Comment this post and let us what you’d like to see in the next release!

Categories: Documentation Tags: