Search code examples
enterprise-library

Fluent API takes effect only once (Enterprise Library)


I wish to do the below using the fluent api
Modify the configuration at run time; perhaps to take into account external factors or changes to the environment.
https://msdn.microsoft.com/en-us/library/ff664363(PandP.50).aspx
But I get into issues. The problem is exactly same as the below link. Change Enterprise Library configuration midway in a program


Solution

  • This code works in Enterprise Library 6:

    class Program
    {
        static void Main(string[] args)
        {
            FirstConfig();
            Logger.Write("Before processing", "General"); //Some wrapper around EntLib logger methods
    
            //Do some processing for some time
    
            SecondConfig();
            Logger.Write("After after processing", "General");
        }
    
        private static void FirstConfig()
        {
            var textFormatter = new FormatterBuilder()
                .TextFormatterNamed("First Text Formatter")
                .UsingTemplate("{message}");
    
            var builder = new ConfigurationSourceBuilder();
            builder.ConfigureLogging()
                .WithOptions.DoNotRevertImpersonation()
                .LogToCategoryNamed("General").WithOptions.SetAsDefaultCategory()
                .SendTo.FlatFile("First Listener")
                .FormatWith(textFormatter).WithHeader("").WithFooter("")
                .ToFile("BeforeChange.log");
    
            var configSource = new DictionaryConfigurationSource();
            builder.UpdateConfigurationWithReplace(configSource);
    
            Logger.SetLogWriter(new LogWriterFactory(configSource).Create());
        }
    
        private static void SecondConfig()
        {
            var textFormatter = new FormatterBuilder()
                .TextFormatterNamed("Second Text Formatter")
                .UsingTemplate("{message}");
    
            var builder = new ConfigurationSourceBuilder();
            builder.ConfigureLogging()
                .WithOptions.DoNotRevertImpersonation()
                .LogToCategoryNamed("General").WithOptions.SetAsDefaultCategory()
                .SendTo.FlatFile("Second Listener")
                .FormatWith(textFormatter).WithHeader("").WithFooter("")
                .ToFile("AfterChange.log");
    
            var configSource = new DictionaryConfigurationSource();
            builder.UpdateConfigurationWithReplace(configSource);
    
            // Dispose any existing loggers
            Logger.Reset();
            Logger.SetLogWriter(new LogWriterFactory(configSource).Create());
        }
    }
    

    After running this there will be two log files created: BeforeChange.log and AfterChange.log

    For EntLib 5 the code is very similar except that the container needs to be setup:

    class Program
    {
        static void Main(string[] args)
        {
            FirstConfig();
            Logger.Write("Before processing", "General"); //Some wrapper around EntLib logger methods
    
            //Do some processing for some time
    
            SecondConfig();
            Logger.Write("After after processing", "General");
        }
    
        private static void FirstConfig()
        {
            var textFormatter = new FormatterBuilder()
                .TextFormatterNamed("First Text Formatter")
                .UsingTemplate("{message}");
    
            var builder = new ConfigurationSourceBuilder();
            builder.ConfigureLogging()
                .WithOptions.DoNotRevertImpersonation()
                .LogToCategoryNamed("General").WithOptions.SetAsDefaultCategory()
                .SendTo.FlatFile("First Listener")
                .FormatWith(textFormatter).WithHeader("").WithFooter("")
                .ToFile("BeforeChange.log");
    
            var configSource = new DictionaryConfigurationSource();
            builder.UpdateConfigurationWithReplace(configSource);
    
            EnterpriseLibraryContainer.Current = EnterpriseLibraryContainer.CreateDefaultContainer(configSource);
        }
    
        private static void SecondConfig()
        {
            var textFormatter = new FormatterBuilder()
                .TextFormatterNamed("Second Text Formatter")
                .UsingTemplate("{message}");
    
            var builder = new ConfigurationSourceBuilder();
            builder.ConfigureLogging()
                .WithOptions.DoNotRevertImpersonation()
                .LogToCategoryNamed("General").WithOptions.SetAsDefaultCategory()
                .SendTo.FlatFile("Second Listener")
                .FormatWith(textFormatter).WithHeader("").WithFooter("")
                .ToFile("AfterChange.log");
    
            var configSource = new DictionaryConfigurationSource();
            builder.UpdateConfigurationWithReplace(configSource);
    
            // Dispose any existing loggers
            Logger.Reset();
            EnterpriseLibraryContainer.Current = EnterpriseLibraryContainer.CreateDefaultContainer(configSource);
        }
    }
    

    The downside of the above EntLib 5 approach is that the second configuration will configure logging but will also wipe out any other blocks that were already configured. The solution is to use Unity directly and keep the same container and just make modifications to the blocks we want changed. In this example FirstConfig() configures data access and logging but SecondConfig only (re)configures logging.

    class Program
    {
        private static IUnityContainer container = new UnityContainer();
    
        static void Main(string[] args)
        {
            FirstConfig();
            Logger.Write("Before processing", "General"); //Some wrapper around EntLib logger methods
    
            EnterpriseLibraryContainer.Current.GetInstance<Database>("MyDatabase");
            //Do some processing for some time
    
            SecondConfig();
    
            // This would fail if we cleared the existing configuration because SecondConfig()
            // does not configure the data access block
            EnterpriseLibraryContainer.Current.GetInstance<Database>("MyDatabase");
    
            Logger.Write("After after processing", "General");
        }
    
        private static void FirstConfig()
        {
            var textFormatter = new FormatterBuilder()
                .TextFormatterNamed("First Text Formatter")
                .UsingTemplate("{message}");
    
            var builder = new ConfigurationSourceBuilder();
            builder.ConfigureLogging()
                .WithOptions.DoNotRevertImpersonation()
                .LogToCategoryNamed("General").WithOptions.SetAsDefaultCategory()
                .SendTo.FlatFile("First Listener")
                .FormatWith(textFormatter).WithHeader("").WithFooter("")
                .ToFile("BeforeChange.log");
    
            builder.ConfigureData()
               .ForDatabaseNamed("MyDatabase")
                 .ThatIs.ASqlDatabase()
                 .WithConnectionString("server=(local); database=Northwind; Integrated Security=true;")
                 .AsDefault();
    
            var configSource = new DictionaryConfigurationSource();
            builder.UpdateConfigurationWithReplace(configSource);
    
            container.AddNewExtension<EnterpriseLibraryCoreExtension>();
    
            // Create a configurator to use to configure our fluent configuration
            var configurator = new UnityContainerConfigurator(container);
            EnterpriseLibraryContainer.ConfigureContainer(configurator, configSource);
    
            // Use the configured container with fluent config as the Enterprise Library service locator
            EnterpriseLibraryContainer.Current = new UnityServiceLocator(container);
        }
    
        private static void SecondConfig()
        {
            var textFormatter = new FormatterBuilder()
                .TextFormatterNamed("Second Text Formatter")
                .UsingTemplate("{message}");
    
            var builder = new ConfigurationSourceBuilder();
            builder.ConfigureLogging()
                .WithOptions.DoNotRevertImpersonation()
                .LogToCategoryNamed("General").WithOptions.SetAsDefaultCategory()
                .SendTo.FlatFile("Second Listener")
                .FormatWith(textFormatter).WithHeader("").WithFooter("")
                .ToFile("AfterChange.log");
    
            var configSource = new DictionaryConfigurationSource();
            builder.UpdateConfigurationWithReplace(configSource);
    
            // Dispose any existing loggers
            Logger.Reset();
    
            var configurator = new UnityContainerConfigurator(container);
            EnterpriseLibraryContainer.ConfigureContainer(configurator, configSource);
        }
    }