Enterprise Library 6 - Foolproof Bootstrapping Code

Topics: Data Access Application Block, Logging Application Block
Jun 2, 2013 at 8:26 PM
Edited Jun 2, 2013 at 8:26 PM
I attempted to upgrade a few apps and of course they failed due to changes in how LogWriterFactory and the DatabaseProviderFactory classes are instantiated since the Logging and Database Block are what I have used the most.

For one app I was successful since I know where the app starts and can add in the appropriate bootstrapping code like below which I found here.
               IConfigurationSource configurationSource = ConfigurationSourceFactory.Create();
                DatabaseFactory.SetDatabaseProviderFactory(new DatabaseProviderFactory(configurationSource));
                LogWriterFactory logWriterFactory = new LogWriterFactory(configurationSource);
                Logger.SetLogWriter(logWriterFactory.Create());
For other apps I have components that are instantiated due to events and are part of a larger engine or process. BizTalk is a big one that I would have to create some type of static mechanism to determine if the factories were instantiated.
I other applications where new App Domains are created dynamically that may or may not work.

Is there a way check if the factories are already instantiated so I don't have to add a static thread safe method to track this? I am still going to have to add this code to every component.

In the past it was nice to create an app or component and as long as the configuration was present in its context it could start logging.
Jun 3, 2013 at 5:49 AM
One approach that you could take would be to wrap the static facades behind your own facade. This would have the benefit of working without implementing locking as well as minimizing code changes to the application. If you are using the static facades (e.g. Logger.Write()) then probably you would only need to change a using statement. So you could create a Logger class with a static constructor that performs the initialization:
    public static class Logger
    {
        static Logger()
        {
            IConfigurationSource configurationSource = ConfigurationSourceFactory.Create();
            LogWriterFactory logWriterFactory = new LogWriterFactory(configurationSource);
            LogWriter logger = logWriterFactory.Create();
            Logger.SetLogWriter(logger);
        }

        [SecurityCritical]
        public static void SetContextItem(object key, object value)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.SetContextItem(key, value);
        }

        [SecurityCritical]
        public static void FlushContextItems()
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.FlushContextItems();
        }

        public static void Write(object message)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(message);
        }

        public static void Write(object message, string category)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(message, category);
        }

        public static void Write(object message, string category, int priority)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(message, category, priority);
        }

        public static void Write(object message, string category, int priority, int eventId)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(message, category, priority, eventId);
        }

        public static void Write(object message, string category, int priority, int eventId, TraceEventType severity)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(message, category, priority, eventId, severity);
        }

        public static void Write(object message, string category, int priority, int eventId,
                                 TraceEventType severity, string title)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(message, category, priority, eventId, severity, title);
        }

        public static void Write(object message, IDictionary<string, object> properties)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(message, properties);
        }

        public static void Write(object message, string category, IDictionary<string, object> properties)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(message, category, properties);
        }

        public static void Write(object message, string category, int priority, IDictionary<string, object> properties)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(message, category, priority, properties);
        }

        public static void Write(object message, string category, int priority, int eventId,
                                 TraceEventType severity, string title, IDictionary<string, object> properties)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(message, category, priority, eventId, severity, title, properties);
        }

        public static void Write(object message, ICollection<string> categories)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(message, categories);
        }

        public static void Write(object message, ICollection<string> categories, int priority)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(message, categories, priority);
        }

        public static void Write(object message, ICollection<string> categories, int priority, int eventId)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(message, categories, priority, eventId);
        }

        public static void Write(object message, ICollection<string> categories, int priority, int eventId, TraceEventType severity)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(message, categories, priority, eventId, severity);
        }

        public static void Write(object message, ICollection<string> categories, int priority, int eventId,
                                 TraceEventType severity, string title)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(message, categories, priority, eventId, severity, title);
        }

        public static void Write(object message, ICollection<string> categories, IDictionary<string, object> properties)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(message, categories, properties);
        }

        public static void Write(object message, ICollection<string> categories, int priority, IDictionary<string, object> properties)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(message, categories, priority, properties);
        }

        public static void Write(object message, ICollection<string> categories, int priority, int eventId,
                                 TraceEventType severity, string title, IDictionary<string, object> properties)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(message, categories, priority, eventId, severity, title, properties);
        }

        public static void Write(LogEntry log)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(log);
        }

        public static T GetFilter<T>()
            where T : class, ILogFilter
        {
            return Microsoft.Practices.EnterpriseLibrary.Logging.Logger.GetFilter<T>();
        }

        public static T GetFilter<T>(string name)
            where T : class, ILogFilter
        {
            return Microsoft.Practices.EnterpriseLibrary.Logging.Logger.GetFilter<T>(name);
        }

        public static ILogFilter GetFilter(string name)
        {
            return Microsoft.Practices.EnterpriseLibrary.Logging.Logger.GetFilter(name);
        }

        public static bool IsLoggingEnabled()
        {
            return Microsoft.Practices.EnterpriseLibrary.Logging.Logger.IsLoggingEnabled();
        }

        public static bool ShouldLog(LogEntry log)
        {
            return Microsoft.Practices.EnterpriseLibrary.Logging.Logger.ShouldLog(log);
        }

        public static void Reset()
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Reset();
        }

        public static LogWriter Writer
        {
            get
            {
                return Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Writer;
            }   
        }

        public static void SetLogWriter(LogWriter logWriter, bool throwIfSet = true)
        {
            Microsoft.Practices.EnterpriseLibrary.Logging.Logger.SetLogWriter(logWriter, throwIfSet);
        }
    }
It's not particularly elegant but should get the job done in a straightforward manner that would not require any major changes. There are other approaches you could take by writing your own class that manages the bootstrapping (e.g. Lazy<LogWriter>() or catch an InvalidOperationException when calling Logger.Writer) but those seem a bit more work and could involve more refactoring.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Jun 5, 2013 at 5:18 AM
Edited Jun 5, 2013 at 5:32 AM
I have always created a facade within the context of a solution and was successful with an MVC app getting this to work in a static constructor. I also put the facade in a Unity container.

I was not successful converting a Lync UCMA 4.0 Windows Service app and don't have the time now to figure out why. A static constructor worked fine in a console app for development and testing but in the context of a Windows service it still failed.

This really just seems to me a bad change. Before I could write (or farm out) code that was just dependent on the Ent Lib configuration and the Ent Lib assemblies. Now it would be dependent on the EntLib 6 configuration, the EntLib 6 assemblies, and an additional custom shared assembly with a facade class and a static constructor to instantiate the factories.

It would be nice to have the capability to inspect the state of the factories and see if they are already instantiated. I guess I can just swallow exceptions if they are already instantiated but that is just wrong. At least I know the issue and can make the decision to upgrade everything if I have to.
Jun 5, 2013 at 6:30 AM
I've used the code I posted above in a Windows service and it worked fine. In a Windows service you should definitely know when to bootstrap. Perhaps it has something to do with UCMA?
I guess I can just swallow exceptions if they are already instantiated
I'm assuming you meant if they are not already instantiated.

If this a huge pain point for you, I would recommend sticking with/using Enterprise Library 5. From Just released - Microsoft Enterprise Library 6:

Do I have to upgrade?

Strictly speaking, no. If you are happy with Enterprise Library 5.0, you can continue using that version. If the new and improved features of Enterprise Library 6.0 appeal to you, then you would want to migrate. Note: Enterprise Library 6.0 targets .NET4.5 framework only. Enterprise Library 5.0 works fine on .NET3.5, .NET4.0 as well as .NET4.5. So, in order for you to move to .NET4.5, you don’t have to upgrade.
~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to