Home > Developing Using CodeFluent Entities > Working with both the cache sub producer and the localization aspect

Working with both the cache sub producer and the localization aspect


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

  1. No comments yet.
  1. April 24, 2013 at 5:02 pm

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s