<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Alteridem Consulting &#187; Robert Prouse</title>
	<atom:link href="http://www.alteridem.net/author/admin/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.alteridem.net</link>
	<description>Software by Design</description>
	<lastBuildDate>Thu, 02 Sep 2010 17:52:37 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Visual Studio 2010 Command Prompt Here</title>
		<link>http://www.alteridem.net/2010/09/02/visual-studio-2010-command-prompt-here/</link>
		<comments>http://www.alteridem.net/2010/09/02/visual-studio-2010-command-prompt-here/#comments</comments>
		<pubDate>Thu, 02 Sep 2010 17:52:37 +0000</pubDate>
		<dc:creator>Robert Prouse</dc:creator>
				<category><![CDATA[Tips'n'Tricks]]></category>
		<category><![CDATA[Visual Studio]]></category>

		<guid isPermaLink="false">http://www.alteridem.net/2010/09/02/visual-studio-2010-command-prompt-here/</guid>
		<description><![CDATA[Real developers live on the command line. Way back in 1996, Microsoft released the Command Prompt Here Power Toy to ease their pain. Industrious developers who preferred the Visual Studio command prompt took it and adopted it to run a Visual Studio command prompt with all of the paths to Visual Studio and .NET tools [...]]]></description>
			<content:encoded><![CDATA[<p><img style="border-bottom: 0px; border-left: 0px; margin: 0px 0px 6px 6px; display: inline; border-top: 0px; border-right: 0px" title="CmdPrompt" border="0" alt="CmdPrompt" align="right" src="http://www.alteridem.net/wp-content/uploads/2010/09/CmdPrompt.png" width="310" height="296" /> Real developers live on the command line. Way back in 1996, Microsoft released the <a href="http://www.microsoft.com/windowsxp/downloads/powertoys/xppowertoys.mspx">Command Prompt Here Power Toy</a> to ease their pain. Industrious developers who preferred the Visual Studio command prompt took it and adopted it to run a Visual Studio command prompt with all of the paths to Visual Studio and .NET tools in the path.</p>
<p>In the fine, time honored tradition, I have continued to update with each new Visual Studio release and have finally done so for Visual Studio 2010.</p>
<p>To install, download, unzip and right click and install the INF file, it will add a “VS 2010 Cmd Prompt Here” menu item when you right click on a folder in Explorer. Clicking on the menu item will launch a DOS prompt in that directory with all of the Visual Studio and .NET paths set correctly.</p>
<ul>
<li><a href="http://www.alteridem.net/wp-content/uploads/2010/09/vsnet2010cmdhere_x86.zip">Visual Studio 2010 Command Prompt Here (x86)</a></li>
<li><a href="http://www.alteridem.net/wp-content/uploads/2010/09/vsnet2010cmdhere_x64.zip">Visual Studio 2010 Command Prompt Here (x64)</a></li>
</ul>
<p>This assumes that you have installed Visual Studio to the default directory on the C drive. If that is not the case, edit the INF file and change line 38 to the correct path for your installation.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.alteridem.net/2010/09/02/visual-studio-2010-command-prompt-here/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Installing Ruby on Rails on Windows</title>
		<link>http://www.alteridem.net/2010/08/17/installing-ruby-on-rails-on-windows/</link>
		<comments>http://www.alteridem.net/2010/08/17/installing-ruby-on-rails-on-windows/#comments</comments>
		<pubDate>Tue, 17 Aug 2010 21:01:25 +0000</pubDate>
		<dc:creator>Robert Prouse</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://www.alteridem.net/?p=80</guid>
		<description><![CDATA[I have been planning on learning Ruby on Rails for awhile now, but I have heard that running it on Windows was problematic. The download page makes it sound pretty easy though, so I thought I would give it a try. Install Ruby First we need the Ruby language itself. I picked up the 1.9.1 [...]]]></description>
			<content:encoded><![CDATA[<p>I have been planning on learning <a href="http://rubyonrails.org/">Ruby on Rails</a> for awhile now, but I have heard that running it on Windows was problematic. The <a href="http://rubyonrails.org/download">download page</a> makes it sound pretty easy though, so I thought I would give it a try.</p>
<h4>Install Ruby</h4>
<p>First we need the Ruby language itself. I picked up the <a href="http://rubyforge.org/frs/?group_id=167">1.9.1 windows installer</a> from RubyForge. When I installed, I checked Add Ruby executables to your PATH and Associate .rb and .rbw files.</p>
<p><img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="Capture" border="0" alt="Capture" src="http://www.alteridem.net/wp-content/uploads/2010/08/Capture.png" width="372" height="288" /> </p>
<h4>Install Ruby Gems</h4>
<p>Next, we need the Ruby package manager which allows us to install Rails and other Ruby packages. Download and extract the latest <a href="http://rubyforge.org/frs/?group_id=126">Ruby Gems ZIP file</a> from RubyForge.</p>
<p>Open a DOS command prompt where you unzipped Gems and run the command,</p>
<pre class="code">ruby setup.rb</pre>
<h4>Install Rails</h4>
<p>At the DOS prompt, run</p>
<pre class="code">gem install rails</pre>
<p>Wow, that was easy! Next, create an application and try it out.</p>
<h4>Install MySQL Driver</h4>
<p>Slow down, not so quick. A few steps ahead, I discovered that the MySQL driver is no longer distributed with Rails 2.2, so I had to install it with the command</p>
<pre class="code">gem install mysql</pre>
<p>You may also run into a problem later when you try to run <strong>db:migrate</strong> and it says Not Connected. If you do, follow <a href="http://forums.aptana.com/viewtopic.php?f=20&amp;t=7563&amp;p=27407&amp;hilit=libmysql.dll#p27407">the steps in this post</a>, they got it working for me.</p>
<h4>Initializing an Application</h4>
<p>To start out, I am going to run through the screencast <a href="http://rubyonrails.org/screencasts">Creating a weblog in 15 minutes with Rails 2</a>, so I will create the application for that. Once again, at the command line, first cd to your test web root. I use <a href="http://www.apachefriends.org/en/xampp.html">XAMPP</a> for web development on Windows, so I am going to keep my Rails apps there.</p>
<pre class="code">cd \xampp\htdocs
rails –d mysql blog
cd blog</pre>
<p>The rails command created the blog application to use MySQL as the database. Next open the <strong>blog\config\database.yml</strong> file. Update the username and password where appropriate. Now let&#8217;s try it.</p>
<pre class="code">ruby script/server</pre>
<p>The ruby command runs a development server on port 3000. If you see the Windows Firewall prompt, allow it. Test the installation by going to <a href="http://localhost:3000" target="_blank">http://localhost:3000</a>, you should see the following.</p>
<p><img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="web_1" border="0" alt="web_1" src="http://www.alteridem.net/wp-content/uploads/2010/08/web_1.png" width="644" height="452" /> </p>
<h4>What about Apache?</h4>
<p>Once again, I am using <a href="http://www.apachefriends.org/en/xampp.html">XAMPP</a>, so I tried several ways to get it working, but never managed to get either the mod_rewrite and/or the CGI handlers working. I found quite a few tutorials. All got me to the Welcome Aboard page, but none actually worked when I clicked the About your application’s environment link. I guess for now I am going to have to stick with the built in WEBrick server.</p>
<h4>Next Steps</h4>
<p>We are now setup and configured, so what next?</p>
<ul>
<li>View the various <a href="http://rubyonrails.org/screencasts">Rails screencasts</a>. As I said, I am starting by following along with the <a href="http://rubyonrails.org/screencasts">Creating a weblog in 15 minutes with Rails 2</a> screencast. </li>
<li>Learn more about the <a href="http://www.ruby-lang.org/en/">Ruby programming language</a>. </li>
<li>Read through the <a href="http://rubyonrails.org/documentation">Rails documentation</a>. </li>
<li>Check out the <a href="http://agilewebdevelopment.com/plugins">Rails extensions</a> that may make your projects go quicker. </li>
<li>Don’t forget <a href="http://stdlib.rubyonrails.org/libdoc/test/unit/rdoc/classes/Test/Unit.html">Unit Testing</a> </li>
</ul>
<h4>General Impressions</h4>
<p>The general installation was easy, but I ran into several problems like the setting up Apache on Windows and getting MySQL working. At the bottom of the article <a href="http://techiferous.com/2010/07/roadmap-for-learning-rails/">Roadmap for Learning Rails</a>, it states,</p>
<blockquote>
<p>When you’re first starting out, don’t make unusual choices, such as learning to program <a href="http://stackoverflow.com/questions/164896/limitations-in-running-ruby-rails-on-windows">Rails on Windows</a>. One of the strengths of Rails is the community, but if you’ve made unusual choices, you’ll quickly find yourself without much help.</p>
</blockquote>
<p>In my opinion, Rails is not suitable for production deployment on Windows and probably anything beyond casual development is probably better on Linux or a Mac. If I stick with it, I will probably move all of my development over to Linux.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.alteridem.net/2010/08/17/installing-ruby-on-rails-on-windows/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Migrating from Subversion to Mercurial</title>
		<link>http://www.alteridem.net/2010/07/28/migrating-from-subversion-to-mercurial/</link>
		<comments>http://www.alteridem.net/2010/07/28/migrating-from-subversion-to-mercurial/#comments</comments>
		<pubDate>Wed, 28 Jul 2010 16:48:14 +0000</pubDate>
		<dc:creator>Robert Prouse</dc:creator>
				<category><![CDATA[Mercurial]]></category>
		<category><![CDATA[Source Control]]></category>
		<category><![CDATA[Subversion]]></category>

		<guid isPermaLink="false">http://www.alteridem.net/2010/07/28/migrating-from-subversion-to-mercurial/</guid>
		<description><![CDATA[I have been reading about Git and Mercurial everywhere lately, so I wanted to give them a try. I mainly develop on Windows and the tools for Windows are better for Mercurial, so I decided to start with it. The best way to get a feel for is with real code, so I decided to [...]]]></description>
			<content:encoded><![CDATA[<p>I have been reading about <a href="http://git-scm.com/">Git</a> and <a href="http://mercurial.selenic.com/">Mercurial</a> everywhere lately, so I wanted to give them a try. I mainly develop on Windows and the tools for Windows are better for <a href="http://mercurial.selenic.com/">Mercurial</a>, so I decided to start with it.</p>
<p>The best way to get a feel for is with real code, so I decided to migrate my <a href="http://subversion.tigris.org/">Subversion</a> repository to <a href="http://mercurial.selenic.com/">Mercurial</a>.</p>
<p>The first step was to install <a href="http://tortoisehg.bitbucket.org/">TortoiseHg</a>, the Windows shell extension for <a href="http://mercurial.selenic.com/">Mercurial</a>. I then read that it is much quicker to work with a local copy of a <a href="http://subversion.tigris.org/">Subversion</a> repository, so my next step was to pull a copy down from the server.</p>
<h4>Sync Subversion to the Local Machine</h4>
<p> 
<ol>
<li>Create an \scm\svn (source control management) directory to house the local copy of the repository.
<pre>  mkdir \scm\svn</pre>
</li>
<li>Create the local <a href="http://subversion.tigris.org/">Subversion</a> repository.
<pre>  svnadmin create \scm\svn\myrepository</pre>
</li>
<li>Create an empty hooks\pre-revprop-change.bat file
<pre>  echo @ECHO OFF &gt; \scm\svn\myrepository\hooks\pre-revprop-change.bat&#160; </pre>
</li>
<li>Initialize the local repository for syncing to the remote repository
<pre>  svnsync init file:///scm/svn/myrepository http://remote.repository.com/svn/myrepository</pre>
</li>
<li>Now run the sync.
<pre>  svnsync sync file:///scm/svn/myrepository</pre>
</li>
</ol>
<p>The last command pulls in all the revisions from the remote repository one at a time until it mirrors all of the changesets. Once this is done, you have a fully functional local copy of the SVN repository to work with.</p>
<h4>Configure TortoiseHg</h4>
<p>
  <br />At a minimum, you should set your username and enable the convert extension. To do this, in Explorer, right click and select <strong>TortoiseHg | Global Settings</strong>. Set your username in the Commit section to your name/email in the format <em>First Last &lt;youremail@domain.com&gt;</em>. Next go to the Extensions section and make sure the <strong>Convert extension</strong> is enabled.</p>
<p><img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="TortoiseHg" border="0" alt="TortoiseHg" src="http://www.alteridem.net/wp-content/uploads/2010/07/TortoiseHg.png" width="562" height="538" /> </p>
<h4>Do the Initial Convert of the Repository</h4>
<p>
  <br />I then did a trial run of converting the local SVN repository to an Hg repository.</p>
<ol>
<li>Create an \scm\hg directory to house the Mercurial repository.
<pre>  mkdir \scm\hg</pre>
</li>
<li>Create the Mercurial repository to import the SVN repository into.
<pre>  hg init \scm\hg\myrepository</pre>
</li>
<li>Run the conversion.
<pre>  hg convert -s svn \scm\svn\myrepository \scm\hg\myrepository</pre>
</li>
</ol>
<h4>Cleaning up the Conversion</h4>
<p>
  <br />Looking through the history of my newly imported repository, I noticed that my username changed over time depending on the Linux user account I was working with at the time. It would be nice to change all of these to the preferred mercurial format. Luckily, that is pretty easy, <b>hg convert</b> can use a file to map SVN usernames to Hg users. To do that, create a file called <b>authors.txt</b> with conversions like the following;</p>
<pre>robprouse = Rob Prouse &lt;rob@email.com&gt;
rprouse = Rob Prouse &lt;rob@email.com&gt;
jdoe = John Doe &lt;john@doe.com&gt;</pre>
<p>My repository is also a bit of a mess. It started out as a CVS repository years ago which was later migrated to SVN. Over the years, every project I worked on was filed away in there. One thing about Mercurial, is that it is best to have one project per repository since you can only clone the whole thing. To do this, I created a <b>filemap.txt</b> file and used it to pull out each project into its own repository.</p>
<pre>include /projects/Project1/
include /projects/Project1.Tests/</pre>
<p>
  <br />For more information, <a href="http://hgbook.red-bean.com/read/migrating-to-mercurial.html#id441801">check out the documentation</a>. I then reran the conversion that I did above, except I pull now from my newly converted Hg repository and I used the author and file map files. I updated the <strong>filemap.txt</strong> for each project that I wanted to export and re-ran the conversion.</p>
<pre>hg init \scm\hg\project1
hg convert --authors authors.txt --filemap filemap.txt -s hg \scm\hg\myrepository \scm\hg\project1</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.alteridem.net/2010/07/28/migrating-from-subversion-to-mercurial/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The dangers of Macros</title>
		<link>http://www.alteridem.net/2010/07/21/the-dangers-of-macros/</link>
		<comments>http://www.alteridem.net/2010/07/21/the-dangers-of-macros/#comments</comments>
		<pubDate>Wed, 21 Jul 2010 15:02:44 +0000</pubDate>
		<dc:creator>Robert Prouse</dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Tips'n'Tricks]]></category>

		<guid isPermaLink="false">http://www.alteridem.net/?p=65</guid>
		<description><![CDATA[Another developer came to me with a problem today that he couldn’t figure out. He couldn’t believe what he was seeing in the debugger and needed a second set of eyes. He had a line of code like the following; return max( eRetVal, GetNumber() ); While debugging, he noticed that he was stepping into the [...]]]></description>
			<content:encoded><![CDATA[<p>Another developer came to me with a problem today that he couldn’t figure out. He couldn’t believe what he was seeing in the debugger and needed a second set of eyes. He had a line of code like the following;</p>
<pre name="code" class="cpp">return max( eRetVal, GetNumber() );</pre>
<p>While debugging, he noticed that he was stepping into the GetNumber() method twice, and in his code, it had side-effects. We both puzzled over it for awhile. We looked at the disassembly and sure enough, it was getting called twice, but why? Then it hit me, <b>max</b> is a macro, not a function. If you go to the definition of <b>max</b>, you find;</p>
<pre name="code" class="cpp">#ifndef max
#define max(a,b)            (((a) &gt; (b)) ? (a) : (b))
#endif</pre>
<p>So, if you expand that macro out, the original code gets compiles as;</p>
<pre name="code" class="cpp">return (((eRetVal) &gt; (GetNumber()) ? (eRetVal) : (GetNumber());</pre>
<p>Once the macro is expanded, it is easy to see why the method is called twice. Obviously I have not being doing enough C++ lately. There was a time when I would have seen that immediately as it is the classic example, but I have been working in .NET so long now that I am forgetting all of the little ways that C++ can bite you.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.alteridem.net/2010/07/21/the-dangers-of-macros/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>log4net UdpAppender with IPv6 on Windows Vista and 7</title>
		<link>http://www.alteridem.net/2010/07/09/log4net-udpappender-with-ipv6-on-windows-vista-and-7/</link>
		<comments>http://www.alteridem.net/2010/07/09/log4net-udpappender-with-ipv6-on-windows-vista-and-7/#comments</comments>
		<pubDate>Fri, 09 Jul 2010 17:31:40 +0000</pubDate>
		<dc:creator>Robert Prouse</dc:creator>
				<category><![CDATA[log4net]]></category>

		<guid isPermaLink="false">http://www.alteridem.net/2010/07/09/log4net-udpappender-with-ipv6-on-windows-vista-and-7/</guid>
		<description><![CDATA[log4net is a great logging framework for .NET, but it hasn’t been updated in years. When we moved to Windows Vista, we noticed that the UdpAppender stopped working with Chainsaw and with our internal logging tools when logging to localhost. After some Googling, we discovered that replacing localhost with 127.0.0.2 got everything working and we [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://logging.apache.org/log4net/index.html">log4net</a> is a great logging framework for .NET, but it hasn’t been updated in years. When we moved to Windows Vista, we noticed that the <a href="http://logging.apache.org/log4net/release/sdk/log4net.Appender.UdpAppender.html">UdpAppender</a> stopped working with <a href="http://logging.apache.org/chainsaw/index.html">Chainsaw</a> and with our internal logging tools when logging to <strong>localhost</strong>. After some Googling, we discovered that replacing <strong>localhost</strong> with <strong>127.0.0.2</strong> got everything working and we promptly forgot about it.</p>
<p>Earlier this week, we moved one of our projects over to .NET 4.0 and once again logging failed. In the console window while debugging the app, I noticed the following,</p>
<pre>log4net.Util.TypeConverters.ConversionNotSupportedException: Cannot convert from type [System.String]
  value [127.0.0.2] to type [System.Net.IPAddress] ---&gt;
  System.Net.Sockets.SocketException: No such host is known
       at System.Net.Dns.InternalGetHostByAddress(IPAddress address, Boolean includeIPv6)
       at System.Net.Dns.GetHostEntry(String hostNameOrAddress)
       at log4net.Util.TypeConverters.IPAddressConverter.ConvertFrom(Object source)</pre>
<p>So, I tried changing back to <strong>localhost</strong> and the error changed to,</p>
<pre>log4net:ERROR [UdpAppender] Unable to send logging event to remote host ::1 on port 7071.
      System.Net.Sockets.SocketException (0x80004005): An address incompatible with
      the requested protocol was used
       at System.Net.Sockets.Socket.SendTo(Byte[] buffer, Int32 offset, Int32 size, SocketFlags
         socketFlags, EndPoint remoteEP)
       at System.Net.Sockets.UdpClient.Send(Byte[] dgram, Int32 bytes, IPEndPoint endPoint)
       at log4net.Appender.UdpAppender.Append(LoggingEvent loggingEvent)</pre>
<p>At this point, I have some clues, specifically that localhost was being resolved as the IPv6 address ::1, not the IPv4 address 127.0.0.1. Looking through the reported issues I found that this problem had been <a href="https://issues.apache.org/jira/browse/LOG4NET-112">reported and fixed in 2007</a>. Unfortunately, that code isn’t in the release, so I downloaded the latest source and recompiled log4net. Of course, they don’t release the signing key, so I generated my own and signed the assembly myself.</p>
<p>This fixed my problems with the UdpAppender, although, if you use <strong>localhost</strong> on a Windows Vista or Windows 7 machine, it will resolve to the IPv6 address, so make sure that your receiver is listening on the IPv6 address. For example, in <a href="http://log2console.codeplex.com/">log2console,</a> under Receivers, set Use IPv6 Addresses to true for the UDP receiver. If your receiver does not support IPv6, use <strong>127.0.0.2</strong> for the address.</p>
<p>To save other people the hassle of recompiling log4net themselves to get the latest fixes, I have uploaded a release version of the assembly. Download it here.</p>
<ul>
<li><a href="http://www.alteridem.net/wp-content/uploads/2010/07/log4net_1.2.10.1.zip">log4net 1.2.10.1</a> -&#160; 260 KB Zip </li>
</ul>
<p>We are using this version of the assembly in our own projects, but I make no guarantees as to its stability.</p>
<p><strong>Update:</strong> Just to&#160; be clear, this is not an official log4net release and it is only compiled against .NET 2.0. I have made no code changes, it is just the code that is currently in the repository. This is only intended to save you having to download and compile if you run into the same problems I did. It is also not extensively tested. We are using it and File, Rolling File, Event Log and UDP Appenders seem to be working fine.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.alteridem.net/2010/07/09/log4net-udpappender-with-ipv6-on-windows-vista-and-7/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Extending CopyHelper using Extension Methods</title>
		<link>http://www.alteridem.net/2008/07/22/extending-copyhelper-using-extension-methods/</link>
		<comments>http://www.alteridem.net/2008/07/22/extending-copyhelper-using-extension-methods/#comments</comments>
		<pubDate>Tue, 22 Jul 2008 13:14:00 +0000</pubDate>
		<dc:creator>Robert Prouse</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[Libraries]]></category>
		<category><![CDATA[Tips'n'Tricks]]></category>

		<guid isPermaLink="false">http://www.alteridem.net/?p=55</guid>
		<description><![CDATA[In my last two posts, I have been developing a small utility library to do the grunt work of copying data from an instance of one class to an instance of another type. The Copier class from my last post allows me to copy all public properties from one class to another class as long [...]]]></description>
			<content:encoded><![CDATA[<p>In my <a href="http://www.alteridem.net/2008/07/21/extending-copyhelper-using-generics/">last</a> <a href="http://www.alteridem.net/2008/07/09/method-to-copy-data-between-objects-of-different-types/">two</a> posts, I have been developing a small utility library to do the grunt work of copying data from an instance of one class to an instance of another type. The <a href="http://www.alteridem.net/2008/07/21/extending-copyhelper-using-generics/"><strong>Copier</strong></a> class from my last post allows me to copy all public properties from one class to another class as long as the properties have the same name and type. All that is done with one line of code;</p>
<div class="wlWriterSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:364d24c2-3f44-49d3-b077-d600fcaece3c" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">
<pre name="code" class="c#:nogutter">Copier&lt;ICustomer&gt;.Copy( customer ).To&lt;ICustomerView&gt;( view );</pre>
</div>
<p>Today, I am going to use extension methods to simplify the above code even further. I want to be able to write</p>
<div class="wlWriterSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:37699e1e-1b46-4f94-a84f-25266f64cba3" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">
<pre name="code" class="c#:nogutter">customer.CopyTo&lt;ICustomerView&gt;( view );</pre>
</div>
<p>or if we want to rely on type inferencing with the generic <strong>CopyTo</strong> method, you could write it as simply as</p>
<div class="wlWriterSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:9c627046-fca3-4dc8-8ad8-df7c27291361" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">
<pre name="code" class="c#:nogutter">customer.CopyTo( view );</pre>
</div>
<p>How is this done? Using extension methods, it was actually much simpler than yesterday’s <a href="http://www.alteridem.net/2008/07/21/extending-copyhelper-using-generics/"><strong>Copier</strong></a> class. In fact, it just ended up being one line of code for the <strong>CopyTo</strong> method and for the <strong>CopyFrom</strong> method. I simply wrapped the <a href="http://www.alteridem.net/2008/07/09/method-to-copy-data-between-objects-of-different-types/"><strong>CopyHelper</strong></a> class like this.</p>
<div class="wlWriterSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:d57cca66-24bc-4e64-a84a-3b52c1c5ebb6" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">
<pre name="code" class="c#">public static class CopierExtensions
{
    public static void CopyTo&lt;T&gt;( this object from, T to ) where T : class
    {
        CopyHelper.Copy( from.GetType(), from, typeof( T ), to );
    }

    public static void CopyFrom&lt;T&gt;( this object to, T from ) where T : class
    {
        CopyHelper.Copy( typeof( T ), from, to.GetType(), to );
    }
}</pre>
</div>
<p>The only problem I have with this is that the class that the extension methods are applied to are not constrained by an interface, so all matching properties are copied. Does anyone have any ideas on that?</p>
<p>I have uploaded a copy of <a href="http://www.alteridem.net/wp-content/uploads/2008/07/modelviewhelpers.zip">the solution for this project</a> along with an <a href="http://www.nunit.org">NUnit</a> test project. Take a look, use it if you like and feel free to give me suggestions for improvements.</p>
<p>In the next few posts I was thinking of extending these classes even further. I might add attributes that allow you to ignore certain properties, maybe add an attribute that specifies interfaces that you are allowed to copy to, possibly an attribute that allows properties to automatically be converted between types. What would you find useful or like to see? Would you like to see a post on the performance using these methods?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.alteridem.net/2008/07/22/extending-copyhelper-using-extension-methods/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Extending CopyHelper using Generics</title>
		<link>http://www.alteridem.net/2008/07/21/extending-copyhelper-using-generics/</link>
		<comments>http://www.alteridem.net/2008/07/21/extending-copyhelper-using-generics/#comments</comments>
		<pubDate>Mon, 21 Jul 2008 15:33:55 +0000</pubDate>
		<dc:creator>Robert Prouse</dc:creator>
				<category><![CDATA[.NET 2.0]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Libraries]]></category>
		<category><![CDATA[Tips'n'Tricks]]></category>

		<guid isPermaLink="false">http://www.alteridem.net/2008/07/21/extending-copyhelper-using-generics/</guid>
		<description><![CDATA[In my last post, I created a method that does the grunt work of copying data from an instance of one class to an instance of another type. I often find myself copying data between the properties of my data layer classes and those of my user interface like this. // Copy the data from [...]]]></description>
			<content:encoded><![CDATA[<p>In my <a href="http://www.alteridem.net/2008/07/09/method-to-copy-data-between-objects-of-different-types/">last post</a>, I created a method that does the grunt work of copying data from an instance of one class to an instance of another type. I often find myself copying data between the properties of my data layer classes and those of my user interface like this.</p>
</p>
<div class="wlWriterSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:d56ee549-681a-47e3-98f2-b8699e660de1" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">
<pre name="code" class="c#:nogutter">// Copy the data from the customer to the view
view.Address = customer.Address;
view.Country = customer.Country;
view.FirstName = customer.FirstName;
view.LastName = customer.LastName;
view.PostalCode = customer.PostalCode;
view.Province = customer.Province;</pre>
</div>
<p>The newly created <a href="http://www.alteridem.net/2008/07/09/method-to-copy-data-between-objects-of-different-types/"><strong>CopyHelper</strong> class</a> allows me to shorten that to this.</p>
<div class="wlWriterSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:106cd999-9de2-4404-bfe0-d08445cb28ca" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">
<pre name="code" class="c#:nogutter">// Copy the data from the customer to the view (using reflection in .NET 1.x)
CopyHelper.Copy( typeof(ICustomer), customer, typeof(ICustomerView), view );</pre>
</div>
<p>Today, I want to extend that code using Generics and a <a href="http://en.wikipedia.org/wiki/Fluent_interface">fluent interface</a> so that I can write code like this.</p>
<div class="wlWriterSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:21d2f05d-c92d-4fc4-a163-9687494dddde" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">
<pre name="code" class="c#:nogutter">// Copy the data from the customer to the view (using reflection and generics in .NET 2.0)
Copier&lt;ICustomer&gt;.Copy( customer ).To&lt;ICustomerView&gt;( view );</pre>
</div>
</p>
<p>Internally, I use my <a href="http://www.alteridem.net/2008/07/09/method-to-copy-data-between-objects-of-different-types/"><strong>CopyHelper</strong> class</a> from my last post. I extend that by creating a generic <strong>Copier</strong> class. I make the constructor private so that it can only be created as a part of the fluent interface, in this case the static copy method. Using the instance of the <strong>Copier</strong> class that was returned from that method, you can then copy <strong>To</strong> or <strong>From</strong> another class instance.</p>
<p>Here is the code.</p>
<div class="wlWriterSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:e4d4e61e-e527-494e-b96d-0d4b4f9ba5ea" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">
<pre name="code" class="c#">public sealed class Copier&lt;T1&gt; where T1 : class
{
    #region Private Members

    private readonly T1 _subject;

    #endregion

    #region Public Interface

    /// &lt;summary&gt;
    /// Begin the copying process.
    /// &lt;/summary&gt;
    /// &lt;param name="interface1"&gt;The object you are copying from or to&lt;/param&gt;
    /// &lt;returns&gt;An instance of the Copier class so that you can
    /// continue with the copy to/from in a fluent interface.&lt;/returns&gt;
    public static Copier&lt;T1&gt; Copy( T1 interface1 )
    {
        return new Copier&lt;T1&gt;( interface1 );
    }

    #endregion

    #region Construction

    /// &lt;summary&gt;
    /// Private constructor so that it can only be created as a part of a fluent interface.
    /// &lt;/summary&gt;
    /// &lt;param name="subject"&gt;&lt;/param&gt;
    private Copier( T1 subject )
    {
        _subject = subject;
    }

    #endregion

    #region Copier Methods

    /// &lt;summary&gt;
    /// Copies properties from the subject to the passed in object.
    /// &lt;/summary&gt;
    /// &lt;typeparam name="T2"&gt;The type of object you are copying into.&lt;/typeparam&gt;
    /// &lt;param name="to"&gt;The object to copy into.&lt;/param&gt;
    /// &lt;returns&gt;The modified object that you passed in.&lt;/returns&gt;
    public T2 To&lt;T2&gt;( T2 to ) where T2 : class
    {
        CopyHelper.Copy( typeof( T1 ), _subject, typeof( T2 ), to );
        return to;
    }

    /// &lt;summary&gt;
    /// Copies properties from the passed in object into the subject.
    /// &lt;/summary&gt;
    /// &lt;typeparam name="T2"&gt;The type of object you are copying from.&lt;/typeparam&gt;
    /// &lt;param name="from"&gt;The object to copy from.&lt;/param&gt;
    /// &lt;returns&gt;The modified subject that you originally passed in the Copy method.&lt;/returns&gt;
    public T1 From&lt;T2&gt;( T2 from ) where T2 : class
    {
        CopyHelper.Copy( typeof( T2 ), from, typeof( T1 ), _subject );
        return _subject;
    }

    #endregion
}</pre>
</div>
</p>
<p>I would like to constrain <strong>T1</strong> and <strong>T2</strong> to interfaces at compile time, but I am not sure if that can be done. If you have ideas, please post them in the comments. I thought of using reflection to check if <strong>T1</strong> and <strong>T2</strong> are interfaces at run time, but I am a big believer in favouring compile time errors over run time errors.</p>
<p>In my <a href="http://www.alteridem.net/2008/07/22/extending-copyhelper-using-extension-methods/">next post</a>, I am going to use C# 3.0 extension methods to further simplify copying allowing you to write code like this.</p>
<div class="wlWriterSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:acf0083b-b819-41ac-801c-41c0f977934e" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">
<pre name="code" class="c#:nogutter">// Copy the data from the customer to the view (using extension methods in C# 3.0)
customer.CopyTo&lt;ICustomerView&gt;( view );</pre>
</div>
<p>I am very interested in hearing your feedback on this, so be sure to post to the comments. Do you think this is a good idea? Do you have suggestions for improvements? Let me know.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.alteridem.net/2008/07/21/extending-copyhelper-using-generics/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Method to Copy Data Between Objects of Different Types</title>
		<link>http://www.alteridem.net/2008/07/09/method-to-copy-data-between-objects-of-different-types/</link>
		<comments>http://www.alteridem.net/2008/07/09/method-to-copy-data-between-objects-of-different-types/#comments</comments>
		<pubDate>Wed, 09 Jul 2008 16:05:00 +0000</pubDate>
		<dc:creator>Robert Prouse</dc:creator>
				<category><![CDATA[.NET 1.x]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Tips'n'Tricks]]></category>

		<guid isPermaLink="false">http://www.alteridem.net/2008/07/09/method-to-copy-data-between-objects-of-different-types/</guid>
		<description><![CDATA[One thing that I find tiresome when using the various Model/View patterns is the constant copying of data between the model and the view. Too often, I find myself writing code like this to copy data between an ICustomer and an ICustomerView; // Copy the data from the customer to the view view.Address = customer.Address; [...]]]></description>
			<content:encoded><![CDATA[<p>One thing that I find tiresome when using the various <a href="http://polymorphicpodcast.com/shows/mv-patterns/">Model/View patterns</a> is the constant copying of data between the model and the view. Too often, I find myself writing code like this to copy data between an <strong>ICustomer</strong> and an <strong>ICustomerView</strong>;</p>
<p><a name="listing1"></a></p>
<div class="wlWriterSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:2ee7dfba-ed37-4b8e-9881-03d732dd1547" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">
<pre name="code" class="c#:nogutter">// Copy the data from the customer to the view
view.Address = customer.Address;
view.Country = customer.Country;
view.FirstName = customer.FirstName;
view.LastName = customer.LastName;
view.PostalCode = customer.PostalCode;
view.Province = customer.Province;</pre>
</div>
<p>I would much rather write something like one of the following lines;</p>
<p><a name="listing2"></a></p>
<div class="wlWriterSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:d4534127-22e3-47de-9638-6bc33333d17b" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">
<pre name="code" class="c#">// Copy the data from the customer to the view (using reflection in .NET 1.x)
CopyHelper.Copy( typeof(ICustomer), customer, typeof(ICustomerView), view );

// Copy the data from the customer to the view (using reflection and generics in .NET 2.0)
Copier&lt;ICustomer&gt;.Copy( customer ).To&lt;ICustomerView&gt;( view );

// Copy the data from the customer to the view (using extension methods in C# 3.0)
customer.CopyTo&lt;ICustomerView&gt;( view );</pre>
</div>
<p>It got me to thinking that there must be a better way, so I began writing code that would do the grunt work for me. Too often,</p>
<p>Over the next few days, I will blog about my thought process in developing this method and take it through the various iterations that can be seen in lines 2, 5 &amp; 8 above.</p>
<p>Today, I will start with the .NET 1.x version. I will start with some design decisions;</p>
<ul>
<li>I want to be able to specify the types that I am copying between, not infer them using reflection. This way, I can use the interfaces, not the concrete classes when I am copying between the objects. </li>
<li>For now, I am going to assume that if both interfaces have a non-static get/set property with the same <strong>name</strong> and <strong>type</strong> I will copy between them. </li>
<li>I need to check that neither object is null and that I am not trying to copy an object over to itself. </li>
</ul>
<p>This was simple enough. I created a static helper class called <strong>CopyHelper</strong> with one static <strong>Copy</strong> method. I use <a href="http://msdn.microsoft.com/en-us/library/kyaxdd3x.aspx">Type.GetProperties</a> to get the non-static, public properties with getters and setters. If the name and type match, I use the <a href="http://msdn.microsoft.com/en-us/library/b05d59ty.aspx">GetValue</a> and <a href="http://msdn.microsoft.com/en-us/library/xb5dd1f1.aspx">SetValue</a> methods on the <a href="http://msdn.microsoft.com/en-us/library/system.reflection.propertyinfo.aspx">PropertyInfo</a> class to copy the value across from one object to the next. This is the result;</p>
<p><a name="listing3"></a></p>
<div class="wlWriterSmartContent" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:c3141718-afd0-45c0-af12-a896a062b949" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">
<pre name="code" class="c#">#region Copyright © Alteridem Consulting 2008
//
// All rights are reserved. Reproduction or transmission in whole or in part, in
// any form or by any means, electronic, mechanical or otherwise, is prohibited
// without the prior written consent of the copyright owner.
//
// Filename: CopyHelper.cs
// Date:     06/06/2008 11:18 AM
// Author:   Rob Prouse
//
#endregion

#region Using Directives

using System;
using System.Collections.Generic;
using System.Reflection;

#endregion

namespace Alteridem.ModelViewHelpers
{
    public static class CopyHelper
    {
        #region Private Members

        // We are interested in non-static, public properties with getters and setters
        private const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.SetProperty;

        #endregion

        /// &lt;summary&gt;
        /// Copies all public properties from one object to another.
        /// &lt;/summary&gt;
        /// &lt;param name="fromType"&gt;The type of the from object, preferably an interface. We could infer this using reflection, but this allows us to contrain the copy to an interface.&lt;/param&gt;
        /// &lt;param name="from"&gt;The object to copy from&lt;/param&gt;
        /// &lt;param name="toType"&gt;The type of the to object, preferably an interface. We could infer this using reflection, but this allows us to contrain the copy to an interface.&lt;/param&gt;
        /// &lt;param name="to"&gt;The object to copy to&lt;/param&gt;
        public static void Copy( Type fromType, object from, Type toType, object to )
        {
            if ( fromType == null )
                throw new ArgumentNullException( "fromType", "The type that you are copying from cannot be null" );

            if ( from == null )
                throw new ArgumentNullException( "from", "The object you are copying from cannot be null" );

            if ( toType == null )
                throw new ArgumentNullException( "toType", "The type that you are copying to cannot be null" );

            if ( to == null )
                throw new ArgumentNullException( "to", "The object you are copying to cannot be null" );

            // Don't copy if they are the same object
            if ( !ReferenceEquals( from, to ) )
            {
                // Get all of the public properties in the toType with getters and setters
                Dictionary&lt;string, PropertyInfo&gt; toProperties = new Dictionary&lt;string, PropertyInfo&gt;();
                PropertyInfo[] properties = toType.GetProperties( flags );
                foreach ( PropertyInfo property in properties )
                {
                    toProperties.Add( property.Name, property );
                }

                // Now get all of the public properties in the fromType with getters and setters
                properties = fromType.GetProperties( flags );
                foreach ( PropertyInfo fromProperty in properties )
                {
                    // If a property matches in name and type, copy across
                    if ( toProperties.ContainsKey( fromProperty.Name ) )
                    {
                        PropertyInfo toProperty = toProperties[fromProperty.Name];
                        if ( toProperty.PropertyType == fromProperty.PropertyType )
                        {
                            object value = fromProperty.GetValue( from, null );
                            toProperty.SetValue( to, value, null );
                        }
                    }
                }
            }
        }
    }
}</pre>
</div>
<p>Using this <a href="#listing3">class</a>, you can now write code like in <a href="#listing2">line 2 of the second listing</a> above. In my <a href="http://www.alteridem.net/2008/07/21/extending-copyhelper-using-generics/">next post</a>, I am going to extend this code using generics and give it a <a href="http://en.wikipedia.org/wiki/Fluent_interface">fluent interface</a> for better readability.</p>
<p>I am very interested in hearing your feedback on this, so be sure to post to the comments. Do you think this is a good idea? Do you have suggestions for improvements? Let me know.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.alteridem.net/2008/07/09/method-to-copy-data-between-objects-of-different-types/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Read Properties from an MSI File</title>
		<link>http://www.alteridem.net/2008/05/20/read-properties-from-an-msi-file/</link>
		<comments>http://www.alteridem.net/2008/05/20/read-properties-from-an-msi-file/#comments</comments>
		<pubDate>Tue, 20 May 2008 18:52:10 +0000</pubDate>
		<dc:creator>Robert Prouse</dc:creator>
				<category><![CDATA[.NET 2.0]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[MSI]]></category>

		<guid isPermaLink="false">http://www.alteridem.net/?p=47</guid>
		<description><![CDATA[Today I was working writing auto-updating for some software. I wanted to base it on the Product Version property in the installer MSI file, so I needed some code to read that from the file. It took a fair amount of searching and code tweaking, but I finally worked it all out. Add a reference [...]]]></description>
			<content:encoded><![CDATA[<p>Today I was working writing auto-updating for some software. I wanted to base it on the Product Version property in the installer MSI file, so I needed some code to read that from the file.</p>
<p>It took a fair amount of searching and code tweaking, but I finally worked it all out. </p>
<ol>
<li>Add a reference to the COM <strong>Microsoft Windows Installer Object Library</strong>.
<li>Add a <font face="Courier New"><font color="#0000ff">using</font> WindowsInstaller;</font>
<li>Add the following static method to your code (error checking removed for brevity.)</li>
</ol>
<pre name="code" class="c-sharp:nogutter">
public static string GetMsiProperty( string msiFile, string property )
{
   string retVal = string.Empty;

   // Create an Installer instance
   Type classType = Type.GetTypeFromProgID( “WindowsInstaller.Installer” );
   Object installerObj = Activator.CreateInstance( classType );
   Installer installer = installerObj as Installer;

   // Open the msi file for reading
   // 0 - Read, 1 - Read/Write
   Database database = installer.OpenDatabase( msiFile, 0 );

   // Fetch the requested property
   string sql = String.Format(
      “SELECT `Value` FROM `Property` WHERE Property=’{0}’”, property );
   View view = database.OpenView( sql );
   view.Execute( null );

   // Read in the fetched record
   Record record = view.Fetch();
   if ( record != null )
      retVal = record.get_StringData( 1 );

   return retVal;
}
</pre>
<p>If you want to look up the version, just pass in the name of the MSI file you want to inspect and &#8220;ProductVersion&#8221; for the property you want to return. For example;</p>
<pre name="code" class="c-sharp:nogutter">
string version = GetMsiProperty( msiFile, “ProductVersion” );
</pre>
<p>You can use this method to look up other properties in the installer. Some common ones you might want are <strong>ProductName, ProductCode, UpgradeCode, Manufacturer, ARPHELPLINK, ARPCOMMENTS, ARPCONTACT, ARPURLINFOABOUT</strong> and <strong>ARPURLUDATEINFO</strong>. For a full list of properties, see the <a href="http://msdn.microsoft.com/en-us/library/aa370905(VS.85).aspx">MSDN Reference</a>, but remember that most of the properties listed on that page are only for already installed applications and won&#8217;t be included in the installer.</p>
<p>If you find this code useful or the code is not self-explanatory, please leave a comment.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.alteridem.net/2008/05/20/read-properties-from-an-msi-file/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>log4net Slides and Example Code</title>
		<link>http://www.alteridem.net/2008/02/29/log4net-slides-and-example-code/</link>
		<comments>http://www.alteridem.net/2008/02/29/log4net-slides-and-example-code/#comments</comments>
		<pubDate>Fri, 29 Feb 2008 21:03:34 +0000</pubDate>
		<dc:creator>Robert Prouse</dc:creator>
				<category><![CDATA[Education]]></category>
		<category><![CDATA[Events]]></category>
		<category><![CDATA[log4net]]></category>

		<guid isPermaLink="false">http://www.alteridem.net/2008/02/29/log4net-slides-and-example-code/</guid>
		<description><![CDATA[I have been contacted people who cannot attend my Toronto Code Camp session on log4net tomorrow requesting a copy of my presentation and example code. I cannot find it posted on the Code Camp site, so here is a copy for anyone who is interested. The presentation is in PowerPoint 2007 and the example code [...]]]></description>
			<content:encoded><![CDATA[<p>I have been contacted people who cannot attend my <a href="http://www.torontocodecamp.net/">Toronto Code Camp</a> session on <a href="http://www.torontocodecamp.net/Sessions/tabid/55/CodecampId/1/SessionId/16/Default.aspx">log4net</a> tomorrow requesting a copy of my presentation and example code.  I cannot find it posted on the Code Camp site, so here is a copy for anyone who is interested.</p>
<p>The presentation is in PowerPoint 2007 and the example code is for Visual Studio 2008.  <b>update:</b> I have uploaded a PDF version of the presentation for those people without Office 2007.</p>
<ul>
<li><a href='http://www.alteridem.net/wp-content/uploads/2008/02/introduction-to-log4net_presentation.zip' title='Introduction to log4net Presentation'>Introduction to log4net Presentation</a></li>
<li><a href='http://www.alteridem.net/wp-content/uploads/2008/02/introductiontolog4net_code.zip' title='Introduction to log4net Example Code'>Introduction to log4net Example Code</a></li>
<li><a href='http://www.alteridem.net/wp-content/uploads/2008/03/introduction-to-log4net.pdf' title='Presentation in PDF Format'>Introduction to log4net Presentation in PDF Format</a></li>
</ul>
<p>If you are attending, I am looking forward to meeting you tomorrow at 9 AM.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.alteridem.net/2008/02/29/log4net-slides-and-example-code/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
