Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Tuesday, April 30, 2013

Creating a logging service using the Microsoft Enterprise Library Logging Application Block

As part of my recent foray into creating various Windows services, I've come across the need for logging (like all serious apps do) and decided to use the Microsoft Enterprise Library Logging Application Block. We used it at a previous company and it got very good reviews from the developers there who used it. Unfortunately, all of the tutorials out there are a bit useless when it comes to doing anything real with the Logging Application Block, like using custom configuration. Fortunately, it was easy enough to figure out how to get customized configuration of logging working just by looking in the assembly and writing some unit tests to experiment.

For my needs, I like to keep all of my configuration as compartmentalized as possible. To that end, I usually end up using .NET Configuration files on a per-assembly basis. Fortunately, this works very well with the Logging Application Block because you can configure logging with arbitrary Configuration files. To get custom logging working easily in Visual Studio, do the following:
  1. Open Visual Studio (I'm using 2010)
  2. Install NuGet from the Extension Manager, if you haven't already.
  3. Install EnterpriseLibrary.Config from the Extension Manager if you haven't already.
  4. Create your solution and project that requires logging.
  5. If your project (web or application) comes with a .config file already (i.e. Web.config or App.config respectively) you can use that, otherwise, you can create an app.config file for the assembly in which you're creating your logging service.
  6. Once you've figured out which app.config (or Web.config) file you're going to be using (i.e. the same assembly where you're placing your logging class, right click on the .config file. You should see an 'Edit configuration file' entry with an orange logo (provided by the add-in installed in Step 3 above).
  7. Follow any other tutorial out there on the internet for configuring the Logging Application Block. (repeating such information here would be redundant and pointless). There's one such article here.
  8. Now that you've got your configuration, create a class to be your logging service wrapper.
  9. Figure out where your application config file is going to end up after your application is built.
  10. In your logging service wrapper, create a Microsoft.Practices.EnterpriseLibrary.Common.Configuration.FileConfigurationSource, giving the configuration file path as the sole parameter to the constructor.
  11. Create a new Microsoft.Practices.EnterpriseLibrary.Logging.LogWriterFactory, providing the FileConfigurationSource you just created as the sole parameter. You'll probably want to store this as a static object, because we only need a single factory.
  12. For each instance of your logging service, use this static factory that you've created to create a LogWriter.
  13. Implement whatever methods you need to wrap the logging service.
  14. You're done.
Your code should now look something like the following :
    using System;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.IO;
    using System.Text;
    using Interfaces;
    using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
    using Microsoft.Practices.EnterpriseLibrary.Logging;
    using Support.Extensions;

    /// 
    /// A logging services that uses the Microsoft Enterprise Patterns and 
    /// Practices library to provide logging support.
    /// 
    public class MicrosoftPracticesLoggingService : ILoggingService
    {
        /// 
        /// The log writer factory
        /// 
        private static readonly LogWriterFactory LogWriterFactory;

        /// 
        /// Initializes the  class.
        /// 
        static MicrosoftPracticesLoggingService()
        {
            string configFilePath = typeof(MicrosoftPracticesLoggingService).Assembly.GetCodeBaseFile().FullName + ".config";

            if (File.Exists(configFilePath) == false)
            {
                throw new InvalidOperationException(String.Format("Configuration file could not be found at '{0}'", configFilePath));
            }

            FileConfigurationSource configSource = new FileConfigurationSource(configFilePath);

            LogWriterFactory = new LogWriterFactory(configSource);
        }

        /// 
        /// The log writer used by this service to write to logs.
        /// 
        private readonly LogWriter logWriter;

        /// 
        /// Initializes a new instance of the  class.
        /// 
        public MicrosoftPracticesLoggingService()
        {
            this.logWriter = LogWriterFactory.Create();
        }

        #region Implementation of ILoggingService

        /// 
        [SuppressMessage("Microsoft.StyleCop.CSharp.DocumentationRules", "SA1604:ElementDocumentationMustHaveSummary", Justification = "InheritDoc")]
        [SuppressMessage("Microsoft.StyleCop.CSharp.DocumentationRules", "SA1611:ElementParametersMustBeDocumented", Justification = "InheritDoc")]
        public void LogException(Exception ex, string messageFormat, params object[] parameters)
        {
            if (ex == null)
            {
                throw new ArgumentNullException("ex");
            }

            if (messageFormat == null)
            {
                throw new ArgumentNullException("messageFormat");
            }

            StringBuilder messageBuilder = new StringBuilder();

            messageBuilder.AppendFormat(messageFormat, parameters).AppendLine();
            messageBuilder.AppendLine(ex.StackTrace);

            if (ex.InnerException != null)
            {
                Exception root = ex;

                while (root.InnerException != null)
                {
                    root = root.InnerException;
                }

                messageBuilder.AppendLine("Caused by:");
                messageBuilder.AppendLine(root.StackTrace); 
            }

            LogEntry logEntry = new LogEntry
                {
                    Message = messageBuilder.ToString(),
                    Severity = TraceEventType.Error
                };

            this.logWriter.Write(logEntry);
        }

        #endregion
    }

Friday, April 12, 2013

Finally understand the purpose of event accessors in .NET

As most people who deal with .NET are aware, C# (and also VB.NET) have Properties and Events.  In the case of properties, there are property accessors (get and set) which you can leave to the compiler to define, or you can define your own custom accessors. Similarly, there are event accessors (e.g. add and remove) so that you can customize subscriptions to your event handlers. Until today, I never understood why, until I read the MSDN article on Advanced C#. The reason is explained near the end of the section called "Standard Event Pattern", and it's pretty simple and common sense: in classes where you have a significant number of events (e.g. WPF / Forms Controls) and only a few of them are likely to be subscribed to, you can achieve a smaller memory footprint by overriding the event accessors and placing the event subscribers in an internal IDictionary, rather than using compiler-generated event accessors. In the former case, you'll have to store only the subscribers + the dictionary, whereas in the latter case, you'll have to store the subscribers + n events, and the latter is likely to be far larger than the former.

Wednesday, December 19, 2012

Finding the public key and public key token of an assembly for use with InternalsVisibleTo

It seems like one of the most common problems out there when it comes to testing and dealing with strongly-named assemblies is getting the public key and the public key token of a CLR assembly when you want to create test projects for that assembly. The quick and easy way: 1. Ensure that 'sn.exe' is in your path. If it's not, it's usually in the Windows SDK directory, typically in a folder like : C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\sn.exe 2. Open up a command window, and navigate to the directory containing the snk you're signing your assemblies with. 3. sn -p my.snk Public.snk 4. sn -tp Public.snk ...and voila, the program will print out the public key and public key token to the screen for you. The reminder I found this at was here. After you've found the public key, you'll want to paste a line similar to the following in the AssemblyInfo.cs file of the project whose internals you wish to expose:

[assembly: InternalsVisibleTo("MyCompany.ProjectName.Tests,
PublicKey=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")]

Wednesday, December 30, 2009

Composing XML in C# 2.0 or later

When testing some custom XML serialization I was writing for a project, I ran across a quirk of the XmlWriter created by XmlWriter#Create() : The writer that gets produced by this method doesn't actually write out XML to whatever stream or file you've given it in the Create method until Close() is called on the generated writer. This means that if you've been using the XmlWriter along with a using() statement, you're fine, but if you haven't been using it, as I have in the unit tests I've been trying to write, you're going to get some unexpected results. Something to pay attention to.

Monday, August 27, 2007

I love Eclipse now more than ever

Eclipse has always been a very useful platform, with tons of little developer driven quirks that make the programmer's job so muche easier. I found this out especially lately when I've been trying to get into developing with C# in Visual Studio 2005, and I've been missing features that are just there in Eclipse, but you have to buy a plugin for with VS2k5. And here's the great thing about that whole situation : you can develop C# in Eclipse on Windows with a free plugin. But that's really all just a side note. The main point of this post is this : I just now observed an exceedingly useful feature that's been in Eclipse for several versions. Ever press Ctrl + Shift + T to open the quick loader for classes ? It has a text bar at the top so you can type in the simple name of the class you're looking for and it'll filter out the results. In the filtering options up top, there's the usual ? and * filters, but beside that there's also a spot that says "TZ - TimeZone" which I've never really noticed before, and I don't know what made me notice it today. I typed in TZ, and much to my surprise, a whole list of classes remained in the window, filtered, and they all had TZ capital letters in them, following the general naming conventions in Java. Intrigued by this, I typed in MPREF (ManualPaymentRequestEntryFlow) which is a class in my project that I just finished working on, and sure enough, it filtered the list to have that in it (it was the only class not filtered out) . I thought that was just the greatest filter I'd ever seen. And to build on it, if you use the content assist hotkey (Ctrl + Space) with the same scheme, it automatically fills in those classes for you. How cool is that !?