Thursday, July 31, 2014

log4net: DebugAppender

The DebugAppender in log4net allows you to output messages to any “debug” listener.  There is another similar appender called OutputDebugStringAppender. The difference is that OutputDebugStringAppender uses unmanaged code to call the Windows Debug API.  If you are running through the Visual Studio debugger, the output will not be displayed unless you request display from Unmanaged Code.  With DebugAppender, the display will happen automatically because its managed code.

Use with Windows Service

One of the cool things you can do with the DebugAppender is use this in a Windows Service.  In your service, use debug statements liberally throughout your code going to the DebugAppender.  If you use TopShelf for your Windows Service (which allows you to run in console mode and windows service mode), then while running through Visual Studio, you will see you debug statements showing through the Debug Output Window.  Then, what’s really cool, is that when you deploy the application, you can run Microsoft’s DebugView (dbgview.exe) on the machine where the service is running and you can AGAIN see the debug messages in a LIVE environment.  If debug view is not running, then the overhead on the live application should be minimal.

Visual Studio “swallows” Debug Messages


Note that if you want to see debug messages in DebugView while running through Visual Studio, you will have to “Debug >> Run without Debugging”.  If the Visual Studio debugger is running, then it EATS the debug message and DebugView will never “see” the message.

log4net: EventLogAppender

Using the EventLogAppender in log4net allows you to post messages to the Windows Event Log so that they can be seen in the Event Viewer.  However, there are a few “special” issues with this logger that I’d like to point out.

Here’s a typical appender definition:

<appender name="EventLogAppender" type="log4net.Appender.EventLogAppender" >
<applicationName value="MainService" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>

<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="INFO" />
<levelMax value="FATAL" />
</filter>
</appender>


1) applicationName attribute
The application name is what the “source” column will display in the event viewer:
image 
2) Default Event ID is 0 – Causes error message
image If you do not specify an EventID, then by default, the EventID logged with the message is 0.  This causes the event viewer to display a warning with you message content (1):
The description for Event ID 0 from source MainService cannot be found. Either the component that raises this event is not installed on your local computer or the installation is corrupted. You can install or repair the component on the local computer.

If the event originated on another computer, the display information had to be saved with the event.

The following information was included with the event:


You message is still displayed (2), but you will have this message displayed first.

To eliminate this message, you simply need to specify an EventID greater than 0 in either the ThreadContext or GlobalContext arrays for log4net.  I will let you research this further on your own, but to quickly get rid of the message, right after you configure log4net in your application, just set the GlobalContext property like this:

XmlConfigurator.ConfigureAndWatch(new FileInfo(@".\log4net.config"));
GlobalContext.Properties["EventID"] = 1;
var log = LogManager.GetLogger(typeof(Program));

With a 1 as the EventID, your logged message will be cleaner:
image


3) log4net Levels and Event Log Levels
When logging log4net messages in the Event Log, here is how the “levels” translate:

log4net Level –> Windows Event Log Level
DEBUG, INFO –> Information
WARN –> Warning
ERROR, FATAL –> Error

image

log4net: Custom Filter

I have been playing around with log4net and some of its advanced features.  I wanted to blog about a custom filter I wrote: CompoundFilter.  This filter allows me to combine multiple standard filters into one “if all these filters are true, then…”.  Specifically, what I wanted to do was define an Appender, but then filter messages based on BOTH logger (LoggerMatchFilter) AND Level (LevelMatchFilter or LevelRangeFilter).

In a project called Log4Net.CustomFilter, I added a (nuget) log4net reference, then created CompoundFilter.cs:

using System;
using System.Collections.Generic;
using log4net.Core;
using log4net.Filter;

namespace Log4Net.CustomFilters
{
public class CompoundFilter : FilterSkeleton
{
private bool _acceptOnMatch;
private readonly IList<IFilter> _filters = new List<IFilter>();

public override FilterDecision Decide(LoggingEvent loggingEvent)
{
if (loggingEvent == null)
throw new ArgumentNullException("loggingEvent");

foreach (IFilter filter in _filters)
{
if (filter.Decide(loggingEvent) != FilterDecision.Accept)
return FilterDecision.Neutral; // one of the filter has failed
}

// All conditions are true
return _acceptOnMatch ? FilterDecision.Accept : FilterDecision.Deny;
}

public IFilter Filter
{
set { _filters.Add(value); }
}

public bool AcceptOnMatch
{
get { return _acceptOnMatch; }
set { _acceptOnMatch = value; }
}
}
}


Then, in my main application, I add a reference to the project (better – add a reference to the DLL).  Then, in the log4net.config file, you can add this syntax:

<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n"/>
</layout>

<filter type="Log4Net.CustomFilters.CompoundFilter, Log4Net.CustomFilters">
<filter type="log4net.Filter.LoggerMatchFilter">
<loggerToMatch value="MultiConnectG2.MainService" />
</filter>
<filter type="log4net.Filter.LevelMatchFilter">
<levelToMatch value="DEBUG"/>
</filter>
<acceptOnMatch value="true" />
</filter>

<filter type="log4net.Filter.LoggerMatchFilter">
<loggerToMatch value="MultiConnectG2.MainService" />
<acceptOnMatch value="false" />
</filter>

</appender>


The first filter uses the new CompoundFilter and tells it what assembly to find it in (Log4Net.CustomFilters).  Inside, we specify 2 standard filters: LoggerMatchFilter and LevelMatchFilter.  What this means is that this Compound filter will match if the logger’s name is MultiConnectG2.MainService and the level of the message is DEBUG.  And, if both of these match, then we should accept this message.


The second filter is a simply LoggerMatchFilter that simply says, if the logger name is MultiConnectG2.MainService, then do not accept the message.


Filters quit trying to match once the first filter is matched.  So this appender will ONLY allow DEBUG messages from any logger who’s name starts with MultiConnectG2.MainService and reject any other message from those loggers.


Any other logger (with a name that does not start with MultiConnectG2.MainService) will be processed normally through this appender.

Tuesday, July 31, 2012

Creating a JavaScript Application Timeout

In my previous blog post, I showed how you can use jQuery dialog boxes to replace the JavaScript alert() and confirm() functions for a more advanced and skinnable interface.

In this blog post, I am going to dive a little deeper into the “timer” functionality and see how that can be combined with the dlgConfirm dialog box to create a pro-active application timer.

With today’s client-centric programming (JavaScript + Ajax = Web 2.0), your user may be using your application for quite some time before actually communicating back to the server.  If you are using any authentication or sessions, you may need to contact the server with a “keep-alive” message periodically while still keeping the application secure if the user steps away from the application for any length of time.

The technique I am going to demonstrate starts a count down timer on the client.  Each time a call is made back to the server, the timer is reset.  When the timer runs down to 0, a confirmation dialog appears asking the user if they want to continue.  If the user does not respond to the popup confirmation within a specified time period, the user is automatically logged out.

The Timer Code

function Timer(id, minLimit, callback) {
var thisTimer = this;
this.limit = 0
this.callback = callback;
this.minLimit = minLimit;
thisTimer.resetTimer();
thisTimer.id = "#" + id;
thisTimer.intv = setInterval(function () { thisTimer.intvFunc(); }, 1000);
}

Timer.prototype = {

intvFunc: function () {
$(this.id).text(this.formatTime(--this.timer));
if (this.timer === this.limit) {
clearInterval(this.intv);
this.callback.call(this);
}
},

formatNum: function (n) {
return n < 10 ? '0' + n : n;
},

formatTime: function (n, m, s) {
s = n % 60;
m = (n - s) / 60;
//return (m + (n > 0 ? 1 : 0)) + ' minute' + ((m > 0 || n == 0) ? 's' : '');
return [this.formatNum(m), ':', this.formatNum(s)].join('');
},

stopTimer: function () {
clearInterval(this.intv);
},

resetTimer: function () {
this.timer = this.minLimit * 60;
}

};


The Timer is basically a JavaScript object that attaches to an identifier (typically a <span> or <div>) to display a count down.  When the count down reaches 0, it fires a function call.

The following code:


Time for Fun in <span id='funTime'></span>.
<script type="text/javascript">
new Timer('funTime', 5, function () { alert('FUN TIME!!'); });
</script>



Produces a timer that looks like this:
Time for Fun in 03:03.

When the timer reaches 00:00, an alert box will popup that look like this:
image


The parameters for the Timer object are:
1) The id of the object (div or span) that will display the timer
2) The length of time for the Timer in minutes
3) The code that will fire if the Timer reaches 00:00


If you keep a reference to the Timer object, you can call other functions on the object like:
var myTimer = new Timer('funTime', 5, function () { alert('FUN TIME!!'); });
myTimer.stopTimer();  // Stop the count down
myTimer.resetTimer(); // Reset the count down to original


Creating a Auto-Logout Timer
Step 1: Create a place-holder for your Timer
Logout Warning in <span id="timeleft"></span>.<br />


Step 2: Create a function to Start the Global Timer


function startLogoutTimer() {
var timeLeft = 30;
window.logoutTimer = new Timer('timeleft', timeLeft, function () {
dlgConfirm(
'Auto-Logout',
'<h2 style="text-align: center;">You will be automatically logged off in ' + timeLeft + ' minute'
+ (timeLeft == 1 ? '' : 's') + '.<br /><br />Would you like to continue using the system?</h2>',
function () {
var delayTimer = $('#dlgConfirm').data('delayTimer');
delayTimer.stopTimer();

$.ajax({
url: '[Your Keep Alive URL]',
type: 'POST',
success: function (data) {
if (data.Errors) {
displayErrors(data.Errors);
}
else {
$('#dlgConfirm').dialog('close');
startLogoutTimer();
}
},
error: function (jqXhr, textStatus, errorThrown) {
alert("Error '" + jqXhr.status + "' (textStatus: '" + textStatus + "', errorThrown: '" + errorThrown + "')");
}
});
},
function () { window.location = '[Your Logout URL]'; }, 600, 1);
});
}


This code creates a new Timer and attaches it to the global window object as “.logoutTimer”.  It will display the Timer in the <span> we created in step 1.

If the timer reach 00:00, the function will be called that displays a dlgConfirm (see previous post for details on this object).  The dlgConfirm() method displays a message to the user asking if they want to stay logged in.  They have 1 minute to decide if they want to stay logged in.  If they do not answer the question – or if they select ‘No’ (they don’t want to stay logged in), they they are directed to the Logout URL for your application.  If they select ‘Yes’, they an ajax call is made to extend their login cookie from some period of time.  Also, if they select ‘Yes’, the global timer is restarted.

Then we start the whole process with a call to:
$(function () {
   startLogoutTimer();
}

The Delay Parameter for dlgConfirm()
The last parameter for dlgConfirm() is the delay parameter:
function dlgConfirm(title, msg, onYes, onNo, width, delay)

The relevant code in dlgConfirm for this parameter is:
var delayTimer = null;


var delay = $(this).data('delay');
if (delay > 0) {
    var html = $(this).html();
    html += "<br /><div  style='text-align: center;'>Time Left: <span id='delaytimer'></span></div><br />";

    $(this).html(html);
    var onNo = $(this).data('onNo');
    delayTimer = new Timer('delaytimer',
         delay,
         function () {
              $('#dlgConfirm').dialog('close'); onNo(); });
}


$(this).data('delayTimer', delayTimer);




What this code says is:
1) Look for the delay value in the “data”.
2) If its greater than 0, then do the following:
3) Get the current “message” in the dialog and append a break (<br />), then a
     centered <div>, then add the text and place-holder for:
     Time Left: 00:00
4) Put the new message (with the appended Timer placeholder) back into the dialog
5) Start a new Timer that will call the onNo function if the Timer expires

jQuery Replacements for JavaScript Alert & Confirm

When coding applications today, we are doing everything down on the browser to give the user a nice rich user experience.  Often, we need to alert the user to some situation or ask the use a question.
 
The quick and dirty methods for doing this are to use the built-in JavaScript functions: alert() and confirm().  The problem is, these functions do not give you enough control over their look and feel, and they render differently on different browsers.
 
Below, you can see the effect of the following JavaScript in the different browsers installed on my development machine:

alert('Here!');
confirm('Are you sure?');

Chrome

image image

IE9

image image

Firefox

image image

As you can see, each browser displays the code slightly different.  If your web application happens to have a different color scheme, you can see how this would further serve to confuse an end user.

The replacements I use for these features in my application centers around jQuery’s dialog plug-in.  The dialog plug-in is part of the jQuery UI library of extended input controls.  What this means as far as “look and feel” is that if you use the jQuery UI library, then all of your controls are “skinnable” and will have a consistent look throughout your application.
http://jqueryui.com/demos/dialog/

What I am going to show next is how I wrote extensions to the dialogs to make them work more like the JavaScript functions alert() and confirm().

Using the extensions, we will get a consistent look and feel along with several other nice enhancements:

Chrome:
image image

IE9:
image image

Firefox:
image image


First, on the page I put the following “<div>’s”:
<div id='dlgAlert'></div>
<div id='dlgConfirm'></div>


Alert Dialog (dlgAlert)

I then turn these divs into hidden dialogs.  Let’s examine the code one at a time starting with the alert (the simpler of the two):

$('#dlgAlert').dialog({
autoOpen: false,
modal: true,
close: function (event, ui) {
$(this).text('');
},
open: function (event, ui) {
$(this).dialog('option', 'position', 'center');
},
buttons: {
'Ok': function () {
$(this).dialog('close');

var onOK = $(this).data('onOK');
//alert('onOK: ' + (typeof onOK));
if (onOK != null) onOK();
}
}
});


autoOpen: false
Tells jQuery to turn the div into a dialog box, but do not open the dialog until we tell it to.

modal: true
Tells jQuery that when we open the dialog, we want the users focus to ONLY be on the dialog, so jQuery will place a translucent background over the screen, then display the dialog and the user will not be able to interact with anything on the screen except the dialog.


close: function()
When jQuery closes the dialog, the contents will be cleared.


open: function()
When jQuery opens the dialog, the dialog box will be positioned in the center of the screen.


buttons: ‘Ok’
Similar to the JavaScript alert() function, the dialog box will display an ‘Ok’ button to the user so that they can read the message and click the “ok” button.  The remainer of the Ok button code will be explained below.



Problem with Substituting a Dialog for alert()
Ok, so you may have noticed that in neither the “div”, not the dialog setup is there any “content”.  If you were to simply open this dialog box at this point with:
$(‘#dlgAlert’).dialog(‘open’);


You would get just an empty box with an ‘Ok’ button on it.  Additionally, we have another problem with the dialog box.  In JavaScript, if we write the following code:
alert(‘I am here!’);
$.ajax(…);


When the JavaScript hits the alert() function, all processing STOPS until the user clicks the ‘Ok’ button.  Only after the ‘Ok’ button is clicked will the $.ajax(…) call runs.  However, if we substitute our dialog box for the alert() call like this:
$(‘#dlgAlert’).dialog(‘open’);
$.ajax(…);


The dialog box would open, but code execution would NOT stop and the $.ajax(…) call would be made.  Clearly, this does not function like the JavaScript alert(), so we need to alter our thinking just a little.


dlgAlert() Function (replacement for JavaScript alert())
Here is the function that we call to make the dlgAlert dialog function close to the JavaScript alert() function:



function dlgAlert(title, msg, onOK) {
if (arguments.length < 3) onOK = null;
//alert('typeof onOK: ' + (typeof onOK));


$('#dlgAlert').data('onOK', onOK);

//alert('Added onOK');

$('#dlgAlert').html(msg)
.dialog('option', 'title', title)
.dialog('open');
}



If we call this function like this:
dlgAlert('Sample Title', 'Here!', function () { alert('You clicked Ok!'); });

We get a box that looks like this:
image

And when we click the ‘Ok’ button, we get the ugly JavaScript alert() box telling us that we clicked ‘Ok’:
image

So if we examine the code, we are passing the title (“Sample Title”), the message to be displayed (“Here!”) and the third parameter is a function to be called with the user clicks ‘Ok’ (function () { alert(‘You clicked Ok!’); }).  So in our original example, rather than placing the $.ajax(…) call AFTER the dialog display, instead, we would place it in the onOk parameter like this:
dlgAlert('Sample Title', 'Here!', function () { $.ajax(…) });

Now the $.ajax(…) call will happen after the user clicks ‘Ok’ just like in the JavaScript alert() example.

The way this works is that when we call the dlgAlert() function, we add the onOk parameter to the dlgAlert div object using this line of code:
$('#dlgAlert').data('onOK', onOK);


Then, when the dialog opens and the user clicks ‘Ok’, this code will run:
'Ok': function () {
    $(this).dialog('close');


    var onOK = $(this).data('onOK');
    //alert('onOK: ' + (typeof onOK));
    if (onOK != null) onOK();
}


Notice that the code picks up the ‘onOk’ function from the div:
var onOK = $(this).data('onOK');


Then, if the value is not null, the function is called:
if (onOK != null) onOK();


Message Can Be HTML
Another benefit to using these jQuery dialog boxes is that you can place any HTML in your “Message”.  For instance, the following dlgAlert() call:
dlgAlert('Sample Title',
'<div style="text-align: center">Center with <b>bold</b> text.</div>');


Produces:
image

A Little More Complicated:Confirm Dialog (dlgConfirm)
Ok…  so hopefully you got all that from the dlgAlert() example.  Let dive into the dlgConfirm dialog.  First, the initialization of the dialog:



$('#dlgConfirm').dialog({
autoOpen: false,
modal: true,
close: function (event, ui) {
$(this).text('');
},
open: function (event, ui) {
var width = $(this).data('width');
if (width > 0) $(this).dialog("option", "width", width);

var delayTimer = null;

var delay = $(this).data('delay');
if (delay > 0) {
var html = $(this).html();
html += "<br /><div style='text-align: center;'>Time Left: <span id='delaytimer'></span></div><br />";
$(this).html(html);
var onNo = $(this).data('onNo');
delayTimer = new Timer('delaytimer', delay, function () { $('#dlgConfirm').dialog('close'); onNo(); });
}

$(this).data('delayTimer', delayTimer);

$(this).dialog('option', 'position', 'center');
},
buttons: {
'Yes': function () {
$(this).dialog('close');

var onYes = $(this).data('onYes');
//alert('onOK: ' + (typeof onOK));
if (onYes != null) onYes();
},
'No': function () {
$(this).dialog('close');

var onNo = $(this).data('onNo');
//alert('onOK: ' + (typeof onOK));
if (onNo != null) onNo();
}
}
});



So, I’m not going through all of the code line by line as its very similar to the alert code.  The difference highlights are:
1) There is a width parameter  for expanding the dialog box
2) There is a delay parameter that will allow the dialog to display for a set period
3) There are 2 buttons: Yes and No (instead of Ok Cancel in the JavaScript version)


dlgConfirm() Function (Replacement for JavaScript confirm())


function dlgConfirm(title, msg, onYes, onNo, width, delay) {
if (arguments.length < 3) onYes = null;
if (arguments.length < 4) onNo = null;
if (arguments.length < 5) width = 0;
if (arguments.length < 6) delay = 0;
//alert('dlgConfirm() - START');

$('#divLoading').hide();

$('#dlgConfirm').data('onYes', onYes);
$('#dlgConfirm').data('onNo', onNo);
$('#dlgConfirm').data('width', width);
$('#dlgConfirm').data('delay', delay);

//alert('Added onOK');

$('#dlgConfirm').html(msg)
.dialog('option', 'title', title)
.dialog('open');
}




The code for the dlgConfirm call takes more parameters. The first 2 are the same as the alert call: Title and Message. Obviously, for the message, you want to end the message in the form of a question that can be answered as a Yes or No.


The next 2 parameters: onYes and onNo are function calls that are called when the user presses either the Yes or No buttons respectively.  To “skip” these parameters, simply pass a null for those parameters.


The next parameter is the width.  By default, the dialog box options with a default width, but you might want to adjust that to better fit your message text.  to skip this parameter, pass a 0 as the width and it will be ignored.


The final parameter is the delay parameter.  To use this parameter, you will need an additional piece of code that I will place at the bottom of this article.  The delay feature is something that the JavaScript function does not have.  It allows the dialog to appear for a specified period of time and, if the user does not select Yes or No in that time period, the delay will time out and call the “onNo” function as if the user selected that button.  I will write another blog about how to use this feature to create an auto-logout for your application.


Calling dlgConfirm()
A sample dlgConfirm() call will look like this:



dlgConfirm(
'Sample Title',
'Do you want to continue?',
function() { alert('You clicked YES!'); },
function() { alert('You clicked NO!'); },
500,
0);




Which results in a dialog that looks like this:
image


Pressing Yes:
image

Pressing No:
image



Delay Parameter – Timer Code
To understand and user the “delay” parameter of the dlgConfirm() function, please read my next Blog post.  For completeness, I will include the Time code here:


Timer.prototype = {

intvFunc: function () {
$(this.id).text(this.formatTime(--this.timer));
if (this.timer === this.limit) {
clearInterval(this.intv);
this.callback.call(this);
}
},

formatNum: function (n) {
return n < 10 ? '0' + n : n;
},

formatTime: function (n, m, s) {
s = n % 60;
m = (n - s) / 60;
//return (m + (n > 0 ? 1 : 0)) + ' minute' + ((m > 0 || n == 0) ? 's' : '');
return [this.formatNum(m), ':', this.formatNum(s)].join('');
},

stopTimer: function () {
clearInterval(this.intv);
},

resetTimer: function () {
this.timer = this.minLimit * 30;
}

};

Friday, July 6, 2012

Blogs: Windows Live Writer & Blogger/BlogSpot

Ok…  So here is a blog about blogging.  I started this blog in March and I quickly learned that the thing I hated most about blogging was that my blogs will consist of articles that need both images and code snippets in them.

So right off the bat, I will have problems blogging with my IPad because I don’t have Visual Studio or my screen capture tool.  So I can use the IPad to start a blog or edit an existing blog, but I cannot use it to write a full blog.

The next thing I did not like about blogging was where to put the images.  When I write technical documentation for users or customers, I typically write in Word and possibly convert to PDF.  With those tools, I simply take a screen capture with Greenshot or SnagIt, mark up the image, copy to the clipboard and paste it into my document – quick and easy.  No saving to the disk, no uploading and inserting a link.  With blogging, my blog never “flowed” because I was always stopping to “deal” with images.  Additionally, copying and pasting code from Visual Studio never really displayed the way I wanted.

So I decided to write my next blog with Word so that it flowed.  My plan was to then see what the best way would be to copy and paste the word document into a blog post.  When I finish the post, I went to research posting Word to a blog and I found that starting with Word 2007 (my edition), it actually had a built in Blog posting tool!  How cool!  Problem was, I had trouble making it connect to my http://jdready.blogspot.com account.  So I created a WordPress.com account and I was able to make it connect there.  But still, there were formatting problems.

I continued my search for blog posting software when a product called “Windows Live Writer” continued to come up.  I already use Windows Live Messenger for IM and it turned out I already had Writer installed.  I was instantly able to connect to my original Blogger account – and, it magically uploaded and connected my images to my Blog!

Everything was going along swimingly!  I created several blogs and was really enjoying the experience… Until…  This morning, I tried to post a particularly long post, and when WLW connected to Blogger, I got an error (403 – Forbidden).  What is forbidden?  Why is it forbidden?  None of these questions were immediately answered, so I started Googling…

What I found was that it seemed to have something to do with images.  Up to this point, I was not paying any attention to how or why or where WLW was posting images – it was just magic that worked…  But not it was not working.  So I needed to learn more about this “magic”.  The posts indicated that WLW was saving images to another Google service called “Picasa”.  If you Google “Picasa”, you go to a Google photo sharing site and are prompted to download a program called Picasa.  This didn’t seem right, so I them Googled “picasa web album” and found what I was looking for!

Because I was already logged into Google, I was suddenly presented with 2 albums: JD Ready: Pocket Protector (0 photos) and Windows Live Writer (62 photos).
image

At first, I thought my 403 Errors were because I was trying to upload too many images (which is why I have a 5 part blog series instead of a 3 part series!).  But the truth is, when WLW starts communicating with Google (both Blogger and Picasa are Google products), it gets a security “token” if that token expires during a session (like me taking over an hour to write my 3rd blog post), Picasa’s service will return a 403.

All I had to do to clear the 403 was to shut down WLW and start it up again.  I presume it got a new security token and I was able to continue my blog posting!

Transforms for *.config Files, Part 5: SlowCheetah & Advanced

Can this be made any easier? Yes: SlowCheetah!
Ok… at this point you may be asking yourself if this process can be made any simpler. First, you have to be certain that you spell your transforms and Solution Configurations EXACTLY the same, otherwise they won’t get picked up properly. Additionally, you probably wouldn’t want to take the time to add the nice little <DependentUpon> tags to the transforms so they show up under the live config file.

Fortunately, you don’t have to. There is a Visual Studio Extension called SlowCheetah that will do this for you! SlowCheetah was originally developed for the same use we have described in these blog posts, but for modifying App.config files for console apps. See even App.config files do not have the transform capabilities of Web.config. SlowCheetah was developed by some Microsoft employees that wanted this transform capabilities. In fact, for console apps, SlowCheetah even hooks into your project script to run the transforms on application build, but only for Console apps – not web apps, so we still need our (Project).wpp.targets file.

Installing SlowCheetah
Tools >> Extension Manager >> Online Gallery >> Search for “Slow Cheetah”
Click “Download” and follow the instructions to install. When finished, you will need to restart Visual Studio to have the changes take effect.

Delete the log4net.Release.config and log4net.Debug.config files from your project. Now, RtClick on the log4net.config file and you should see a context menu like this:
image_thumb2_thumb
We now have a new options called “Add Transform”. Go ahead and select that option. You will get a notice that in order for your project to support transforms, the .csproj file will need to be modified and the project will be unloaded and reloaded to make that change happen. Go ahead and accept that message and you will end up with a project that looks like this:
image_thumb3_thumb

SlowCheetah went ahead an created the transform files we needed for all of our defined Solution Configurations and added them to our project and even added the <DependentUpon> tags to the files so they show up nicely under our log4net.config file.

The “REAL” Kicker: SlowCheetah’s Preview!
Ok… SlowCheetah is cool in that it will create our base transforms for any config file (including Web.config). But as I showed, it’s really nothing that you couldn’t do already. So what’s the real kicker to using SlowCheetah? Slow Cheetah will do a PREVIEW of your transform! You don’t even have to compile your code to see it!

Ok, but the problem we have right now is that our transforms are associated (read: <DependentUpon>) the transient Web.config and log4net.config. The “Preview” function of SlowCheetah compares the transform against is <DependentUpon> target. So what we need to do is move our transforms so that they hang from the *.BASE.config files.

SlowCheetah thinks any Web.*.config Pattern is a Transform
Another caveat to this process is that it appears that SlowCheetah thinks that ANY file with the pattern Web.*.config is a transform file. When I right click on Web.BASE.config, it gives me the option to “Preview Transform”:
image_thumb5_thumb

What I expected was the ability to “Add Transforms”. By renaming the file to Web.Main.BASE.config, I achieved what I was looking for:
image_thumb7_thumb

When finished, you should end up with log4net.BASE.config which is your main log4net configuration with 2 transforms: log4net.BASE.Debug.config and log4net.BASE.Release.config. and a log4net.config file which is the result file for the transform process. On the Web.config side, you should end up with a Web.Main.BASE.Config file which is your main Web configuration with 2 transforms: Web.Main.BASE.Debug.config and Web.Main.BASE.Release.config and a web.config file which is the result file for the transform process. You project should look similar to:
image_thumb8_thumb

Now, if you RtClick and of the transform files, you will get a content menu option to “Preview Transform” like this:
image_thumb10_thumb

Select the “Preview Transform” option and a window will display showing the original config file (Web.Main.BASE.config) and a temporary file with the transforms applied where you can then jump through the “differences” between the 2 files to confirm that your transforms worked!
image_thumb12_thumb

Final Step: Make the (Project).wpp.targets Use the New Names
So we almost have SlowCheetah working. Through this process, we have done a lot of naming changes. Our original log4net.config file is now really log4net.BASE.config. Our Web.config has morphed from Web.config to Web.BASE.config to Web.Main.BASE.config. Subsequently, out transforms have changed as well. In the end, after the transforms are complete, we still want the result of the transforms to become Web.config and log4net.config. So we modify the (Project).wpp.targets file to look like this:

<?xml version="1.0" encoding="utf-8" ?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="TransformXml"
AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll" />

<Target Name="TransformWebBASEConfig" BeforeTargets="BeforeBuild" >
<TransformXml Source="$(ProjectDir)Web.Main.BASE.config"
Transform="$(ProjectDir)Web.Main.BASE.$(Configuration).config"
Destination="$(ProjectDir)Web.config" />
</Target>

<Target Name="TransformLog4netBASEConfig" BeforeTargets="BeforeBuild" >
<TransformXml Source="$(ProjectDir)log4net.BASE.config"
Transform="$(ProjectDir)log4net.BASE.$(Configuration).config"
Destination="$(ProjectDir)log4net.config" />
</Target>
</Project>


Advanced Issue: TFS
I was going to make this series a 4 part-er, but I think I can wrap up the final issues here. How do these changes effect TFS? Our web.config and log4net.config files are the most important files when we publish, but are not important to save in Source Control (TFS). To accomplish this scenario, you should exclude the result config files from TFS:
Click the Web.config/log4net.config in Solution Explorer then:
File >> Source Control >> Exclude (file) from Source Control
image_thumb[14]_thumb

Now, if these files have ever been in Source Control, they will have their Read-Only flag set. If you do not remove this, MSBuild will fail when it tries to write the result of the transform to these files.

Advanced Issue: PUBLISHING
As far as “publishing” goes, You want the log4net.config and web.config files to be copied always to the output folder, but you want the *.BASE.config files to never get copied. Recall that the publish process is where MSBuild tries to do transformations. However, with this new format, there are no transformation files hanging from the root Web.config or log4net.config files, so no transforms are attempted and the files are simply copied to the output folder.

Transforms for ANY .config file: 5 Part Series
Part 1: Transforms on Build
This first part does an overview of the default behavior of transforms. It then shows how you can plug into the MSBuild process and apply your transforms at Build time rather than the default publish time.

Part 2: New Config Profiles
The second part of the series shows how to create a new solution configuration and the nuances of what happens behind the scenes (in case you want to delete a configuration). It also discusses having a local development configuration specific to an individual developer.

Part 3: Other Config Files
The third part discusses how to add a configuration for another element of the application (using log4net as an example). This post shows how this process differs from the handling of Web.config.

Part 4: Making the transforms look nice
This fourth part is just a quickie to show how you can manually make your other configuration files sit under their respective parent files.

Part 5: SlowCheetah & Advanced
This fifth and final post is where I try to bring together all of the elements from the previous posts together and introduce a Visual Studio extension that allows much of the previous posts to be accomplished easily and automatically.