Skip to content

The Business Object Model (BOM) Caching

Simon Mourier edited this page Feb 19, 2020 · 1 revision

CodeModeler provides out of the box caching features to developers. However, those caching features in the Business Object Model (BOM) are not generated by default and must be explicitly configured in the model. This is done as so, because using caching in the BOM adds complexity to development by adding new issues (such as when does the cache needs to be cleared?) and is useful in only specific scenarios.

Caching in the BOM should not be implemented by default; instead, it should be added to optimize performances, once standard features and good practices have proven to be insufficient. Thus, the Object Model Cache (OMC) is relevant only in some application designs or scenarios:

  • the OMC is an in-memory cache meaning that it's non-permanent, and it's relevant only in single server cases. Consequently, the OMC is not scalable as it's not shared between servers and no out of the box synchronization between servers is provided. Have you multiple servers, you'll probably be better off using the standard caching features available in Microsoft SQL Server.

  • the OMC is particularly interesting in realms where business objects do not change, or at least not often.

A common realm where the OMC illustrates its value, is in the referential of your business application. For instance, if your application contains a set of roles per user, it's unlikely that those roles will change in the application's lifetime. However, it's very likely that those roles will be checked recurrently throughout your application. Optimizing role checking could be a great place to use the OMC.

From a developer's point of view here's how you'd implement a basic cached role checking. Let’s first define a model with a Role and* User *entities:

Caching - Picture 315

Then we to add the Object Model Cache Producer and enable caching on our Role entity setting the “Is Enabled” attribute (Aspect and Producers tab of the property grid) to True for the entity:

Caching - Picture 316

Caching can be activated per entity or per method. Here-above we activated the cache for the Role entity. To activate caching per method, all you need to do is set the “Is Enabled” attribute to True to the LoadByEmail method we’ve added, such as here-under:

Caching - Picture 317

Caching - Picture 318

Note: The sample above focuses on demonstrating the caching per method feature. On a modeling point of view and out of the caching topic, adding the “Is Collection Key” attribute to the Email property would definitely be the best practice in this case; since it would automatically generate the LoadByEmail method, the unique constraint in the data layer, and would use the e-mail string as a collection key in the generated entity collection.

On an entity caching scope, CodeModeler will then generate a set of new public methods such as Cache[StandardMethodName] (i.e. CacheLoad, CacheLoadByName in this sample), NoCache[StandardMethodName] (i.e. NoCacheLoad, NoCacheLoadByName), and ClearCache. On the other hand, on a method caching scope, CodeModeler will generate the same output but only for the specified method (i.e. CacheLoadByEmail and NoCacheLoadByEmail).

The cache is cleared whenever the Role entity is saved or deleted by calling the ClearCache method at the beginning of the Save and Delete methods. Moreover, as the method is public and static, the clearing mechanism can be triggered on demand.

Here is the generated output:

public static Commerce.Role Load(System.Guid id)
{
    return Role.CacheLoad(true, id);
}
 
public static Commerce.Role NoCacheLoad(System.Guid id)
{
    return Role.CacheLoad(false, id);
}
 
private static Commerce.Role CacheLoad(bool useCache, System.Guid id)
{
    if ((id.Equals(CodeModelerPersistence.DefaultGuidValue) == true))
    {
        return null;
    }
    Commerce.Role role = null;
    string cacheKey = null;
    if ((useCache == true))
    {
        cacheKey = CodeModeler.Runtime.Caching.SimpleCacheManager.BuildCacheKey("8db707619d4dd48dbf3414916999a9ff", id);
role = Commerce.Caching.CacheManager0.Manager.Get("Commerce.Role", cacheKey) as Commerce.Role;
        if ((role != null))
        {
            return role;
        }
    }
    role = new Commerce.Role();
    CodeModeler.Runtime.CodeModelerPersistence persistence = CodeModelerContext.Get(Commerce.Constants.CommerceStoreName).Persistence;
    persistence.CreateStoredProcedureCommand("Commerce", "Role", "Load");
    persistence.AddParameter("@Id", id, CodeModelerPersistence.DefaultGuidValue);
    System.Data.IDataReader reader = null;
    try
    {
        reader = persistence.ExecuteReader();
        if ((reader.Read() == true))
        {
            role.ReadRecord(reader, CodeModeler.Runtime.ReloadOptions.Default);
            role.EntityState = CodeModeler.Runtime.EntityState.Unchanged;
            if ((useCache == true))
            {
                Commerce.Caching.CacheManager0.Manager.Add("Commerce.Role", cacheKey, role, null);
            }
            return role;
        }
    }
    finally
    {
        if ((reader != null))
        {
            reader.Dispose();
        }
        persistence.CompleteCommand();
    }
    return null;
}
 
public static void ClearCache()
{
    Commerce.Caching.CacheManager0.Manager.Clear("Commerce.Role");
}
Clone this wiki locally