CacheFactory.GetCacheManager() returning a new instance of the cache manager everytime

Topics: Caching Application Block
Jun 23, 2011 at 6:34 PM

CacheFactory.GetCacheManager() returning a new instance of the cache manager everytime. I have other applications that use CAB and they work fine. This one particular module that uses CAB seems to behave like this. I verified that I have the configuration and assembly references setup correctly. Any idea on what could cause the CAB to create a new instance of the cache manage every time. I even verified in the debugger that after I get the cache manager instance for the first time, I add an entry into cache then step back to the GetCaheManager line of code in debugger and this time the manager returned does not contain the item that I just added.

Any help is greatly appreciated.

Thank you,

Ramesh

 

Jun 24, 2011 at 5:35 AM

Hi,

Have you defined more than one Cache Manager? If yes, check if you are calling the right Cache Manager. Can you post the relevant code and config here? 

 

Noel Angelo Bolasoc
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Jun 24, 2011 at 5:16 PM
Edited Jun 30, 2011 at 1:48 PM

Hello Noel,

 

There is only one cache manager defined in the config file. Cache Manager is accessed by name and the cache factory successfully returns a cache manage instance. But the problem is that it was not returning the same instance on every call. Here are the snippets from code and config file:

 

Config File:

<cachingConfiguration defaultCacheManager="GenericCache">

<cacheManagers> 

<add name="GenericCache" type="Microsoft.Practices.EnterpriseLibrary.Caching.CacheManager, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

 expirationPollFrequencyInSeconds="86400" maximumElementsInCacheBeforeScavenging="1000"

 numberToRemoveWhenScavenging="10" backingStoreName="InMemoryOnly" />

</cacheManagers> 

<backingStores>

<add encryptionProviderName="" type="Microsoft.Practices.EnterpriseLibrary.Caching.BackingStoreImplementations.NullBackingStore, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

name="InMemoryOnly" />

</backingStores>

</cachingConfiguration>

 

Code:

CacheManager mgr = (CacheManager)CacheFactory.GetCacheManager("GenericCache");

 

I tried to create a small test project but the test project seems to work correctly like all my other projects that use CAB. This is the only one project that behaves like this and I couldn’t spot any difference in the settings and usage that could cause this change in behavior.

 

Thank you,

Ramesh

 

Jun 24, 2011 at 9:56 PM

Ok, I figured out what the problem is. This project also uses DAAB and that is configured at runtime using fluent configuration API. So, when DAAB is configured, CAB config is also refreshed so a new cache manager instance is getting created when accessed again. DAAB is re-configured by building the config and then calling:

EnterpriseLibraryContainer.Current = EnterpriseLibraryContainer

.CreateDefaultContainer(configSource); 

 

So, is there a way to reconfigure any block withoiut having to re-create the EntLib Container? Can we just refresh any particular block?

Thank you,

Ramesh

Jun 27, 2011 at 4:54 AM

Though I haven't tried this yet, you can try the article found on this Microsoft blog.

 

Noel Angelo Bolasoc
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Jun 28, 2011 at 6:31 PM

This would also have the same problem since the Enterprise Library Container will be re-created, which is the root cause.

Thank you,

Ramesh

Jun 29, 2011 at 3:57 AM
Edited Jun 29, 2011 at 4:00 AM

Sorry for the misunderstandings. Anyway, I tried to create a project similar to yours, though I'm using Logging Application Block instead of DAAB, I can't seem to reproduce it. Here is my code:

 static void Main(string[] args)
        {
            //Log using the configuration settings
            LogWriter myWriter = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();
            myWriter.Write("From Configuration: Writing to the text file named FirstFile.txt");

            //Create the first cache manager and display the value
            ICacheManager firstManager = EnterpriseLibraryContainer.Current.GetInstance<ICacheManager>();
            firstManager.Add("MyKey", "MyValue");
            Console.WriteLine(firstManager.GetData("MyKey")); //displays MyValue

            //Create the second cache manager and check if it will display the same value
            ICacheManager secondManager = EnterpriseLibraryContainer.Current.GetInstance<ICacheManager>();
            Console.WriteLine(secondManager.GetData("MyKey")); //displays MyValue

            //Modify Logging Settings by modifying the file and message format
            MyWriter MyWriter = new MyWriter();
            MyWriter.Writer("MyFile.txt", "From Config API: Writing to the text file named MyFile.txt");

            //Create the third cache manager and check if it will display the same value
            ICacheManager thirdManager = EnterpriseLibraryContainer.Current.GetInstance<ICacheManager>();
            Console.WriteLine(thirdManager.GetData("MyKey")); //displays MyValue
        }

        public class MyWriter
        {
            public void Writer(string fileName, string message)
            {
                var myConfig = new ConfigurationSourceBuilder();
                myConfig.ConfigureLogging().WithOptions
                    .DoNotRevertImpersonation()
                    .LogToCategoryNamed("MyCategory").WithOptions.SetAsDefaultCategory()
                    .SendTo.FlatFile("Flat File")
                    .FormatWith(new FormatterBuilder().TextFormatterNamed("MyFormatter")
                    .UsingTemplate("{newline} Timestamp: {timestamp}...{newline} Message: {message}" {newline})).WithHeader("----------- The Beginning -------------")
                    .WithFooter("----------- The End -------------")
                    .ToFile(fileName);

                var configSource = new DictionaryConfigurationSource();
                myConfig.UpdateConfigurationWithReplace(configSource);

                LogWriter writer
                 = EnterpriseLibraryContainer.CreateDefaultContainer(configSource).GetInstance<LogWriter>();

                writer.Write(message);

            }
If you'd like to, you can send us a sample repro project to entlib.support@avanade.com
 
Noel Angelo Bolasoc
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com
Jun 29, 2011 at 3:40 PM

Hello Noel,

I couldn’t get your sample to work with log application block. But I created a small test app using your sample and simulating our scenario using DAAB. This test app does not really use DAAB so it doesn’t require any database or any other database related requirements since it simply configures but never uses.

Summary of the test app:

· App Config file defines the default cache manager and its settings

· Test code configures DAAB using fluent configuration API

· Cache settings from the app config are also copied to the new configuration

o Code demos 2 ways of copying this

· Access cache manager and inspect the cache item that was added before the config was changed

Following is the main application test code. I’m also attaching the entire VS solution if you would like to test it on your end.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Microsoft.Practices.EnterpriseLibrary.Common;

using Microsoft.Practices.EnterpriseLibrary.Caching;

using Microsoft.Practices.EnterpriseLibrary.Logging;

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;

using Microsoft.Practices.Unity;

using System.Configuration;

using System.Diagnostics;

namespace CABTestConsole

{

class Program

{

static void Main(string[] args)

{

//Create the first cache manager and display the value

ICacheManager firstManager = EnterpriseLibraryContainer.Current.GetInstance<ICacheManager>();

firstManager.Add("MyKey", "MyValue");

Console.WriteLine(firstManager.GetData("MyKey")); //displays MyValue

//Create the second cache manager and check if it will display the same value

ICacheManager secondManager = EnterpriseLibraryContainer.Current.GetInstance<ICacheManager>();

Console.WriteLine(secondManager.GetData("MyKey")); //displays MyValue

//Configure DAAB which re-creates EntLib Container

ConfigureDAAB();

//Create the third cache manager and check if it will display the same value

ICacheManager thirdManager = EnterpriseLibraryContainer.Current.GetInstance<ICacheManager>();

Console.WriteLine(thirdManager.Contains("MyKey") ? "Contains MyKey" : "MyKey NOT Found");

Console.WriteLine(thirdManager.GetData("MyKey")); //displays MyValue

}

public static void ConfigureDAAB()

{

var configSourceBuilder = new ConfigurationSourceBuilder();

var configBuilder = configSourceBuilder.ConfigureData();

string dbProviderName = dbProviderName = "System.Data.SqlClient";

configBuilder = configBuilder.WithProviderNamed(dbProviderName).MappedToDatabase(typeof(Microsoft.Practices.EnterpriseLibrary.Data.Sql.SqlDatabase));

configBuilder.ForDatabaseNamed("DefaultDB")

.ThatIs

.AnotherDatabaseType(dbProviderName)

.WithConnectionString("<Dummy Connection String>")

.AsDefault();

// Copy the caching configuration section from app config using one of the following two methods

CopyAppCacheSettingsTo_1(configSourceBuilder);

//CopyAppCacheSettingsTo_2(configSourceBuilder);

// re-configure Enterprise Library Container from config source builder

var configSource = new DictionaryConfigurationSource();

configSourceBuilder.UpdateConfigurationWithReplace(configSource);

EnterpriseLibraryContainer.Current = EnterpriseLibraryContainer.CreateDefaultContainer(configSource);

}

private static void CopyAppCacheSettingsTo_1(ConfigurationSourceBuilder configSourceBuilder)

{

// Copy the caching configuration section from app config

var appConfigSource = new SystemConfigurationSource();

ConfigurationSection cachingConfigSection = appConfigSource.GetSection("cachingConfiguration");

Microsoft.Practices.EnterpriseLibrary.Caching.Configuration.CacheManagerSettings cacheMgrSettings = cachingConfigSection as Microsoft.Practices.EnterpriseLibrary.Caching.Configuration.CacheManagerSettings;

var cacheConfigBuilder = configSourceBuilder.ConfigureCaching();

cacheConfigBuilder.ForCacheManagerNamed(cacheMgrSettings.DefaultCacheManager)

.WithOptions.UseAsDefaultCache()

.WithOptions.StoreInMemory();

}

private static void CopyAppCacheSettingsTo_2(ConfigurationSourceBuilder configSourceBuilder)

{

var appConfigSource = new SystemConfigurationSource();

ConfigurationSection cachingConfigSection = appConfigSource.GetSection("cachingConfiguration");

// add to configuration source builder using caching config sction from app config

var configSource = new DictionaryConfigurationSource();

configSourceBuilder.AddSection("cachingConfiguration", cachingConfigSection);

}

}

}

Thank you,

Ramesh

Jun 30, 2011 at 6:55 AM

I've tested your code and was able to reproduce your issue. The difference in my previous code is that I'm not recreating the container. Anyway,  I found out that instead of copying the cache settings, you are actually creating a new one, with this line of code:

var cacheConfigBuilder = configSourceBuilder.ConfigureCaching();

What I did is to retrieve the caching section first before configuring DAAB then add that section before calling the UpdateConfigurationWithReplace. The updated code would be:

        public static void ConfigureDAAB()
        {

            var configSourceBuilder = new ConfigurationSourceBuilder();
            
            //Retrieve the caching section
            var appConfigSource = new SystemConfigurationSource();
            ConfigurationSection cachingConfigSection = appConfigSource.GetSection("cachingConfiguration");
            
            var configBuilder = configSourceBuilder.ConfigureData();
            string dbProviderName = dbProviderName = "System.Data.SqlClient";
            configBuilder = configBuilder.WithProviderNamed(dbProviderName).MappedToDatabase(typeof(Microsoft.Practices.EnterpriseLibrary.Data.Sql.SqlDatabase));
            configBuilder.ForDatabaseNamed("DefaultDB")
            .ThatIs
            .AnotherDatabaseType(dbProviderName)
            .WithConnectionString("<Dummy Connection String>")
            .AsDefault();

            //CopyAppCacheSettingsTo_1(configSourceBuilder);
            //CopyAppCacheSettingsTo_2(configSourceBuilder);

            // re-configure Enterprise Library Container from config source builder
            var configSource = new DictionaryConfigurationSource();
            
            //Add the previously retrieved caching section
            configSourceBuilder.AddSection("cachingConfiguration", cachingConfigSection);

            configSourceBuilder.UpdateConfigurationWithReplace(configSource);
            EnterpriseLibraryContainer.Current = EnterpriseLibraryContainer.CreateDefaultContainer(configSource);

        }

 
 
Noel Angelo Bolasoc
Global Technologies and Solutions Avanade, Inc.
Jun 30, 2011 at 1:43 PM
Edited Jun 30, 2011 at 1:47 PM

Hello Noel,

 

There are 2 methods in my code – one that copies the caching configuration like you mentioned and the other that creates new one based on the current configuration. And, both these methods did not help my situation. I did try the code snippet from your post but even that has the same problem.

 

When you said ‘The difference in my previous code is that I'm not recreating the container.’ – what do you mean. The following code which is same in both yours and mine does create a new Container, isn’t that right?

 EnterpriseLibraryContainer.Current = EnterpriseLibraryContainer.CreateDefaultContainer(configSource); 

 

Is there a an API or a way to do something like:

EnterpriseLibraryContainer.Current = EnterpriseLibraryContainer.UpdateDefaultContainer(configSource);

 

Thank you,

Ramesh

Jul 1, 2011 at 5:38 AM

Hi Ramesh,

I was referring to my first code snippet, which I'm directly getting the new instance of LogWriter from the CreateDefaultContainer

LogWriter writer = EnterpriseLibraryContainer.CreateDefaultContainer(configSource).GetInstance<LogWriter>();

Unfortunately there is no API like the one you've mentioned. You can request this feature here. I forgot to mention that I used the Isolated Storage since caching in memory doesn't work. I'm also not sure what's the cause, but its the only work around I have right now.

 

Noel Angelo Bolasoc
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Jul 1, 2011 at 5:06 PM

Ok, Thank you Noel.

Thank you,

Ramesh