<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:pingback="http://madskills.com/public/xml/rss/module/pingback/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
  <channel>
    <title>IIrrelevant - HowTo</title>
    <link>http://www.milkcarton.com/blog/</link>
    <description>Irrelevant musings about software development</description>
    <language>en-us</language>
    <copyright>Dan Morphis</copyright>
    <lastBuildDate>Sat, 04 Apr 2009 05:37:33 GMT</lastBuildDate>
    <generator>newtelligence dasBlog 2.3.9074.18820</generator>
    <managingEditor>blog@milkcarton.com</managingEditor>
    <webMaster>blog@milkcarton.com</webMaster>
    <item>
      <trackback:ping>http://www.milkcarton.com/blog/Trackback.aspx?guid=bcd01da5-498e-410c-a687-acc721220457</trackback:ping>
      <pingback:server>http://www.milkcarton.com/blog/pingback.aspx</pingback:server>
      <pingback:target>http://www.milkcarton.com/blog/PermaLink,guid,bcd01da5-498e-410c-a687-acc721220457.aspx</pingback:target>
      <dc:creator>Dan Morphis</dc:creator>
      <wfw:comment>http://www.milkcarton.com/blog/CommentView,guid,bcd01da5-498e-410c-a687-acc721220457.aspx</wfw:comment>
      <wfw:commentRss>http://www.milkcarton.com/blog/SyndicationService.asmx/GetEntryCommentsRss?guid=bcd01da5-498e-410c-a687-acc721220457</wfw:commentRss>
      <slash:comments>3</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <h3>Background
</h3>
        <p>
For a project I'm doing, I have a task model for the various pieces. In the beginning,
I was manually creating a List&lt;ITask&gt;. As I kept adding tasks to run, I started
thinking about hacking some code together to rifle through my assembly and pull back
all the classes which implement ITask.
</p>
        <p>
Then I remember hearing about Managed Extensibility Framework (MEF). I did some searching,
found the <a href="http://www.codeplex.com/MEF/" target="_blank">MEF home page</a>,
and even read the <a href="http://mef.codeplex.com/Wiki/View.aspx?title=Overview&amp;referringTitle=Home" target="_blank">MEF
overview</a>. But none of that told me what I really wanted to know, what's the fastest
way to get started using MEF as a component loader?
</p>
        <p>
I did some more searching and found the <a href="http://dnrtv.com/" target="_blank">dnrTV</a> episode
"<a href="http://www.dnrtv.com/default.aspx?showNum=130" target="_blank">Glenn Block
on MEF, the Managed Extensibility Framework</a>" and after 20-30 minutes they finally
got down to how to create a plugin for your app.
</p>
        <p>
But what I really wanted, and I bet a lot of others, is a quick start guide for creating
a plugin.
</p>
        <h3>Solution
</h3>
        <p>
          <a href="http://mef.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=22313" target="_blank">Download
the latest version of MEF</a>, as of this writing its Preview 4. Grab the System.ComponentModel.Composition.dll
from the bin folder and stash it somewhere. Make a reference to said dll in your project.
</p>
        <p>
On your plugin class, add Export attribute:
</p>
        <div>
          <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none">[Export(<span style="color: #0000ff">typeof</span>(IPlugin))] <span style="color: #0000ff">public</span><span style="color: #0000ff">class</span> Foo
: IPlugin { ... }</pre>
        </div>
        <p>
In your plugin consumer, create a property to hold your plugins, and add the Import
attribute:
</p>
        <div>
          <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none">[Import(<span style="color: #0000ff">typeof</span>(IPlugin))] <span style="color: #0000ff">internal</span> IList&lt;IPlugin&gt;
_myPlugins { get; set; }</pre>
        </div>
        <p>
Now, tell MEF where to get the plugins at (line 2), and where you want MEF to fulfill
any plugins (line 5):
</p>
        <div>
          <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none">
            <p>
              <span style="color: #0000ff">private</span>
              <span style="color: #0000ff">void</span> LoadPlugins()
{ var catalog = <span style="color: #0000ff">new</span> AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = <span style="color: #0000ff">new</span> CompositionContainer(catalog);
var batch = <span style="color: #0000ff">new</span> CompositionBatch(); batch.AddPart(<span style="color: #0000ff">this</span>);
container.Compose(batch); } 
</p>
            <p>
 
</p>
          </pre>
        </div>
        <p>
I put my call to the LoadPlugins method in the constructor.
</p>
        <p>
Now, spin through your plugins and do the work:
</p>
        <div>
          <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none">Console.WriteLine(<span style="color: #006080">"Found
{0} plugins"</span>, _myPlugins.Count); <span style="color: #0000ff">foreach</span> (var
plugin <span style="color: #0000ff">in</span> _myPlugins) { Console.WriteLine(plugin.Name);
}</pre>
        </div>
        <p>
 
</p>
        <p>
          <a href="/blog/content/binary/5_Minute_MEF_Tutorial.zip" target="_blank">Download
the complete source</a> to this (really, only about 10 extra lines to glue things
together) and have fun!
</p>
        <a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fwww.milkcarton.com%2fblog%2f2009%2f04%2f04%2f5%2bMinute%2bTutorial%2bOn%2bManaged%2bExtensibility%2bFramework%2bMEF.aspx">
          <img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.milkcarton.com%2fblog%2f2009%2f04%2f04%2f5%2bMinute%2bTutorial%2bOn%2bManaged%2bExtensibility%2bFramework%2bMEF.aspx" border="0" alt="kick it on DotNetKicks.com" />
        </a>
        <img width="0" height="0" src="http://www.milkcarton.com/blog/aggbug.ashx?id=bcd01da5-498e-410c-a687-acc721220457" />
      </body>
      <title>5 Minute Tutorial on Managed Extensibility Framework (MEF)</title>
      <guid isPermaLink="false">http://www.milkcarton.com/blog/PermaLink,guid,bcd01da5-498e-410c-a687-acc721220457.aspx</guid>
      <link>http://www.milkcarton.com/blog/2009/04/04/5+Minute+Tutorial+On+Managed+Extensibility+Framework+MEF.aspx</link>
      <pubDate>Sat, 04 Apr 2009 05:37:33 GMT</pubDate>
      <description>&lt;h3&gt;Background
&lt;/h3&gt;
&lt;p&gt;
For a project I'm doing, I have a task model for the various pieces. In the beginning,
I was manually creating a List&amp;lt;ITask&amp;gt;. As I kept adding tasks to run, I started
thinking about hacking some code together to rifle through my assembly and pull back
all the classes which implement ITask.
&lt;/p&gt;
&lt;p&gt;
Then I remember hearing about Managed Extensibility Framework (MEF). I did some searching,
found the &lt;a href="http://www.codeplex.com/MEF/" target="_blank"&gt;MEF home page&lt;/a&gt;,
and even read the &lt;a href="http://mef.codeplex.com/Wiki/View.aspx?title=Overview&amp;amp;referringTitle=Home" target="_blank"&gt;MEF
overview&lt;/a&gt;. But none of that told me what I really wanted to know, what's the fastest
way to get started using MEF as a component loader?
&lt;/p&gt;
&lt;p&gt;
I did some more searching and found the &lt;a href="http://dnrtv.com/" target="_blank"&gt;dnrTV&lt;/a&gt; episode
"&lt;a href="http://www.dnrtv.com/default.aspx?showNum=130" target="_blank"&gt;Glenn Block
on MEF, the Managed Extensibility Framework&lt;/a&gt;" and after 20-30 minutes they finally
got down to how to create a plugin for your app.
&lt;/p&gt;
&lt;p&gt;
But what I really wanted, and I bet a lot of others, is a quick start guide for creating
a plugin.
&lt;/p&gt;
&lt;h3&gt;Solution
&lt;/h3&gt;
&lt;p&gt;
&lt;a href="http://mef.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=22313" target="_blank"&gt;Download
the latest version of MEF&lt;/a&gt;, as of this writing its Preview 4. Grab the System.ComponentModel.Composition.dll
from the bin folder and stash it somewhere. Make a reference to said dll in your project.
&lt;/p&gt;
&lt;p&gt;
On your plugin class, add Export attribute:
&lt;/p&gt;
&lt;div&gt;&lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;[Export(&lt;span style="color: #0000ff"&gt;typeof&lt;/span&gt;(IPlugin))] &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; Foo
: IPlugin { ... }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
In your plugin consumer, create a property to hold your plugins, and add the Import
attribute:
&lt;/p&gt;
&lt;div&gt;&lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;[Import(&lt;span style="color: #0000ff"&gt;typeof&lt;/span&gt;(IPlugin))] &lt;span style="color: #0000ff"&gt;internal&lt;/span&gt; IList&amp;lt;IPlugin&amp;gt;
_myPlugins { get; set; }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
Now, tell MEF where to get the plugins at (line 2), and where you want MEF to fulfill
any plugins (line 5):
&lt;/p&gt;
&lt;div&gt;&lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;
&lt;p&gt;
&lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; LoadPlugins()
{ var catalog = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; CompositionContainer(catalog);
var batch = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; CompositionBatch(); batch.AddPart(&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;);
container.Compose(batch); } 
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
I put my call to the LoadPlugins method in the constructor.
&lt;/p&gt;
&lt;p&gt;
Now, spin through your plugins and do the work:
&lt;/p&gt;
&lt;div&gt;&lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;Console.WriteLine(&lt;span style="color: #006080"&gt;"Found
{0} plugins"&lt;/span&gt;, _myPlugins.Count); &lt;span style="color: #0000ff"&gt;foreach&lt;/span&gt; (var
plugin &lt;span style="color: #0000ff"&gt;in&lt;/span&gt; _myPlugins) { Console.WriteLine(plugin.Name);
}&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
&lt;a href="/blog/content/binary/5_Minute_MEF_Tutorial.zip" target="_blank"&gt;Download
the complete source&lt;/a&gt; to this (really, only about 10 extra lines to glue things
together) and have fun!
&lt;/p&gt;
&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fwww.milkcarton.com%2fblog%2f2009%2f04%2f04%2f5%2bMinute%2bTutorial%2bOn%2bManaged%2bExtensibility%2bFramework%2bMEF.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.milkcarton.com%2fblog%2f2009%2f04%2f04%2f5%2bMinute%2bTutorial%2bOn%2bManaged%2bExtensibility%2bFramework%2bMEF.aspx" border="0" alt="kick it on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img width="0" height="0" src="http://www.milkcarton.com/blog/aggbug.ashx?id=bcd01da5-498e-410c-a687-acc721220457" /&gt;</description>
      <comments>http://www.milkcarton.com/blog/CommentView,guid,bcd01da5-498e-410c-a687-acc721220457.aspx</comments>
      <category>.NET</category>
      <category>C#</category>
      <category>HowTo</category>
      <category>MEF</category>
    </item>
    <item>
      <trackback:ping>http://www.milkcarton.com/blog/Trackback.aspx?guid=0f2afcce-d7c6-4d53-93b7-c3d9bf87ab2a</trackback:ping>
      <pingback:server>http://www.milkcarton.com/blog/pingback.aspx</pingback:server>
      <pingback:target>http://www.milkcarton.com/blog/PermaLink,guid,0f2afcce-d7c6-4d53-93b7-c3d9bf87ab2a.aspx</pingback:target>
      <dc:creator>Dan Morphis</dc:creator>
      <wfw:comment>http://www.milkcarton.com/blog/CommentView,guid,0f2afcce-d7c6-4d53-93b7-c3d9bf87ab2a.aspx</wfw:comment>
      <wfw:commentRss>http://www.milkcarton.com/blog/SyndicationService.asmx/GetEntryCommentsRss?guid=0f2afcce-d7c6-4d53-93b7-c3d9bf87ab2a</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
A long time, and many many moons ago I took wrote some code to interface our build
server with a <a href="http://www.epowerswitch.com/uk/p-4g_guard.htm" target="_blank" rel="nofollow">network
power switch</a> we had laying around the office. We used this to turn on and off
lava lamps to indicate the status of the build. Some might ask why we didn't use the
X10 support that is already in CCNET, and the answer mostly is cost, and the fact
that X10 wouldn't work in our environment.
</p>
        <p>
That was 2.5 years ago. Since then, our team has become more distributed. We have
one guy working in Ann Arbor, MI, and occasionally have others telecommuting. So not
everyone can see the status of the lamps. Also in the 2.5 years since that code was
written, a little thing called <a href="http://twitter.com/" target="_blank">Twitter</a> has
become very popular. I did some research, and found <a href="http://techblog.tomfanning.eu/2008/03/twitter-task-for-nant-sms-notification.html" target="_blank">Tom
Fannings nAnt Twitter task</a> and briefly considered using it.
</p>
        <p>
But in the end, I just couldn't resist adding my own <a href="http://www.codinghorror.com/blog/archives/000150.html" target="_blank">developer
gold plating</a> and thought it would be neat if we could also issue commands to the
build server via tweets. So with that feature in mind, I had to write it myself.
</p>
        <p>
To start out with, I used <a href="http://devblog.yedda.com/index.php/2007/05/16/twitter-c-library/" target="_blank">Yedda's
C# Twitter library</a>. The Yedda library is a pleasure to work with, it makes sending
a tweet as simple as
</p>
        <div>
          <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none">
            <span style="color: #0000ff">new</span> Twitter().UpdateAsXML(_username,
password, messsage);</pre>
        </div>
        <p>
One thing the Yedda library didn't have, was the ability to query for your Twitter
replies. A quick look through the source, and the <a href="http://apiwiki.twitter.com/" target="_blank">Twitter
API</a> docs and I realized this would be trivial. The details of how I did it aren't
important to this post, but if your curious, you can look at lines 567 - 627 of the
Yedda source included with this post.
</p>
        <p>
I'm not going to dive to much into how the whole project works, but here is a high
level. The software runs as a Windows Service, leverages the CCTrayLib assembly for
Cruise Control.NET to do all the heavy lifting. It polls the Cruise Control.NET server
every 5 seconds, and fires events when things happen. The two events we want are the
Polled and BuildOccurred events.
</p>
        <p>
These events allow us to intern kick off our own events based on the state of the
build. Based on the state, we grab the appropriate actions to run as defined in the
BuildActions.xml file. This maps a build state to a set of actions. In the case of
a "Building" action, we send a Twitter, with a message template of "{PROJECT} is building",
and turn ports 1 on, and 2 off on our ePower switch. Easy enough.
</p>
        <p>
But how do we take in commands? I pondered this for a minute than realized it would
be trivial to leverage the Twitter replies API for this. But what about security,
we can't have just anyone sending commands to our build server. This is where the
Twitter friends API comes in handy. In order to issue commands to our build server,
the account our build server uses has to have you as a friend, not just a follower.
</p>
        <p>
The first action I implemented was the force build command. The idea for the grammar
came from a <a href="http://twitter.com/orand/status/1319154695" target="_blank">joke
reply</a><a href="http://twitter.com/orand" target="_blank">@orand</a> sent our Twitter
bot. After that, I thought it might be nice to be able to get the list of projects,
get a projects status, and ask for help. So that leaves us with a total of 4 commands.
</p>
        <h3>A small bunny trail
</h3>
        <p>
When I first wrote the command parser, it looked something like this:
</p>
        <div>
          <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none">
            <span style="color: #0000ff">if</span> (msg.StartsWith(<span style="color: #006080">"force
build "</span>)) ProcessForceBuildCommand(msg, user); <span style="color: #0000ff">else</span><span style="color: #0000ff">if</span> (msg.StartsWith(<span style="color: #006080">"get
projects"</span>)) ProcessProjectListsCommand(msg, user); ...</pre>
        </div>
        <p>
I thought about it for a while, and thought there had to be an easier way. We are
using .NET 3.5 after all, with all its lamba, LINQ and new and improved delegate goodness.
I did some research and came upon the Action&lt;T&gt; (and Func&lt;T&gt;) delegate
type. And came up with this implementation for registering commands
</p>
        <div>
          <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none">var commands = <span style="color: #0000ff">new</span> Dictionary&lt;<span style="color: #0000ff">string</span>,
Action&lt;<span style="color: #0000ff">string</span>, <span style="color: #0000ff">string</span>&gt;&gt;
{ {<span style="color: #006080">"force build "</span>, ProcessForceBuildCommand},
{<span style="color: #006080">"get projects"</span>, ProcessProjectListsCommand},
{<span style="color: #006080">"get project status "</span>, ProcessProjectStatusCommand},
{<span style="color: #006080">"help"</span>, ProcessHelpCommand} };</pre>
        </div>
        <p>
Once we have all the commands registered, we can use some lambda and LINQ magic to
act upon the commands issued
</p>
        <div>
          <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none">var key = _commands.Keys.Where(msg.StartsWith).First();
<span style="color: #0000ff">if</span> (!<span style="color: #0000ff">string</span>.IsNullOrEmpty(key))
_commands[key].Invoke(msg, user); <span style="color: #0000ff">else</span> SendTweet(<span style="color: #0000ff">string</span>.Format(<span style="color: #006080">"@{0}
I'm sorry, I don't understand you. Maybe you should ask for help"</span>, user)); </pre>
        </div>
        <h3>Get to the point, I want to see the source code
</h3>
        <p>
The code overall is fairly well structured (if I do say so myself), although there
is one area where I did violate the separation of concerns rule, the TwitterManager
class knows more about Cruise Control.NET than it should. But, given that this is
a very simple internal project, and not for public consumption, I'm mostly OK with
that :)
</p>
        <p>
I've included the source to our entire build monitor, I hope you find it useful. 
We are using a very old version (1.1) of Cruise Control.NET in our environment. If
your using a more recent version, you will probably need to swap out the Cruise Control
CCTrayLib and Remote assemblies for something more recent, and invert some of the
commented out code in the SetupCruiseControl method of BuildServerMonitor.cs
</p>
        <p>
If you have any questions, or find/fix any bugs, please feel free to leave a comment,
or send me a tweet, my username on Twitter is <a href="http://twitter.com/akcoder" target="_blank">akcoder</a>.
</p>
        <p>
          <a href="http://files.milkcarton.com/Afhcan.BuildMonitor.zip" target="_blank">Download
the Afhcan.BuildMonitor</a>
        </p>
        <img width="0" height="0" src="http://www.milkcarton.com/blog/aggbug.ashx?id=0f2afcce-d7c6-4d53-93b7-c3d9bf87ab2a" />
      </body>
      <title>Does your Cruise Control.NET server Twitter?</title>
      <guid isPermaLink="false">http://www.milkcarton.com/blog/PermaLink,guid,0f2afcce-d7c6-4d53-93b7-c3d9bf87ab2a.aspx</guid>
      <link>http://www.milkcarton.com/blog/2009/03/16/Does+Your+Cruise+ControlNET+Server+Twitter.aspx</link>
      <pubDate>Mon, 16 Mar 2009 04:59:22 GMT</pubDate>
      <description>&lt;p&gt;
A long time, and many many moons ago I took wrote some code to interface our build
server with a &lt;a href="http://www.epowerswitch.com/uk/p-4g_guard.htm" target="_blank" rel="nofollow"&gt;network
power switch&lt;/a&gt; we had laying around the office. We used this to turn on and off
lava lamps to indicate the status of the build. Some might ask why we didn't use the
X10 support that is already in CCNET, and the answer mostly is cost, and the fact
that X10 wouldn't work in our environment.
&lt;/p&gt;
&lt;p&gt;
That was 2.5 years ago. Since then, our team has become more distributed. We have
one guy working in Ann Arbor, MI, and occasionally have others telecommuting. So not
everyone can see the status of the lamps. Also in the 2.5 years since that code was
written, a little thing called &lt;a href="http://twitter.com/" target="_blank"&gt;Twitter&lt;/a&gt; has
become very popular. I did some research, and found &lt;a href="http://techblog.tomfanning.eu/2008/03/twitter-task-for-nant-sms-notification.html" target="_blank"&gt;Tom
Fannings nAnt Twitter task&lt;/a&gt; and briefly considered using it.
&lt;/p&gt;
&lt;p&gt;
But in the end, I just couldn't resist adding my own &lt;a href="http://www.codinghorror.com/blog/archives/000150.html" target="_blank"&gt;developer
gold plating&lt;/a&gt; and thought it would be neat if we could also issue commands to the
build server via tweets. So with that feature in mind, I had to write it myself.
&lt;/p&gt;
&lt;p&gt;
To start out with, I used &lt;a href="http://devblog.yedda.com/index.php/2007/05/16/twitter-c-library/" target="_blank"&gt;Yedda's
C# Twitter library&lt;/a&gt;. The Yedda library is a pleasure to work with, it makes sending
a tweet as simple as
&lt;/p&gt;
&lt;div&gt;&lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Twitter().UpdateAsXML(_username,
password, messsage);&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
One thing the Yedda library didn't have, was the ability to query for your Twitter
replies. A quick look through the source, and the &lt;a href="http://apiwiki.twitter.com/" target="_blank"&gt;Twitter
API&lt;/a&gt; docs and I realized this would be trivial. The details of how I did it aren't
important to this post, but if your curious, you can look at lines 567 - 627 of the
Yedda source included with this post.
&lt;/p&gt;
&lt;p&gt;
I'm not going to dive to much into how the whole project works, but here is a high
level. The software runs as a Windows Service, leverages the CCTrayLib assembly for
Cruise Control.NET to do all the heavy lifting. It polls the Cruise Control.NET server
every 5 seconds, and fires events when things happen. The two events we want are the
Polled and BuildOccurred events.
&lt;/p&gt;
&lt;p&gt;
These events allow us to intern kick off our own events based on the state of the
build. Based on the state, we grab the appropriate actions to run as defined in the
BuildActions.xml file. This maps a build state to a set of actions. In the case of
a "Building" action, we send a Twitter, with a message template of "{PROJECT} is building",
and turn ports 1 on, and 2 off on our ePower switch. Easy enough.
&lt;/p&gt;
&lt;p&gt;
But how do we take in commands? I pondered this for a minute than realized it would
be trivial to leverage the Twitter replies API for this. But what about security,
we can't have just anyone sending commands to our build server. This is where the
Twitter friends API comes in handy. In order to issue commands to our build server,
the account our build server uses has to have you as a friend, not just a follower.
&lt;/p&gt;
&lt;p&gt;
The first action I implemented was the force build command. The idea for the grammar
came from a &lt;a href="http://twitter.com/orand/status/1319154695" target="_blank"&gt;joke
reply&lt;/a&gt; &lt;a href="http://twitter.com/orand" target="_blank"&gt;@orand&lt;/a&gt; sent our Twitter
bot. After that, I thought it might be nice to be able to get the list of projects,
get a projects status, and ask for help. So that leaves us with a total of 4 commands.
&lt;/p&gt;
&lt;h3&gt;A small bunny trail
&lt;/h3&gt;
&lt;p&gt;
When I first wrote the command parser, it looked something like this:
&lt;/p&gt;
&lt;div&gt;&lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (msg.StartsWith(&lt;span style="color: #006080"&gt;"force
build "&lt;/span&gt;)) ProcessForceBuildCommand(msg, user); &lt;span style="color: #0000ff"&gt;else&lt;/span&gt; &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (msg.StartsWith(&lt;span style="color: #006080"&gt;"get
projects"&lt;/span&gt;)) ProcessProjectListsCommand(msg, user); ...&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
I thought about it for a while, and thought there had to be an easier way. We are
using .NET 3.5 after all, with all its lamba, LINQ and new and improved delegate goodness.
I did some research and came upon the Action&amp;lt;T&amp;gt; (and Func&amp;lt;T&amp;gt;) delegate
type. And came up with this implementation for registering commands
&lt;/p&gt;
&lt;div&gt;&lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;var commands = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Dictionary&amp;lt;&lt;span style="color: #0000ff"&gt;string&lt;/span&gt;,
Action&amp;lt;&lt;span style="color: #0000ff"&gt;string&lt;/span&gt;, &lt;span style="color: #0000ff"&gt;string&lt;/span&gt;&amp;gt;&amp;gt;
{ {&lt;span style="color: #006080"&gt;"force build "&lt;/span&gt;, ProcessForceBuildCommand},
{&lt;span style="color: #006080"&gt;"get projects"&lt;/span&gt;, ProcessProjectListsCommand},
{&lt;span style="color: #006080"&gt;"get project status "&lt;/span&gt;, ProcessProjectStatusCommand},
{&lt;span style="color: #006080"&gt;"help"&lt;/span&gt;, ProcessHelpCommand} };&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
Once we have all the commands registered, we can use some lambda and LINQ magic to
act upon the commands issued
&lt;/p&gt;
&lt;div&gt;&lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;var key = _commands.Keys.Where(msg.StartsWith).First();
&lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (!&lt;span style="color: #0000ff"&gt;string&lt;/span&gt;.IsNullOrEmpty(key))
_commands[key].Invoke(msg, user); &lt;span style="color: #0000ff"&gt;else&lt;/span&gt; SendTweet(&lt;span style="color: #0000ff"&gt;string&lt;/span&gt;.Format(&lt;span style="color: #006080"&gt;"@{0}
I'm sorry, I don't understand you. Maybe you should ask for help"&lt;/span&gt;, user)); &lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Get to the point, I want to see the source code
&lt;/h3&gt;
&lt;p&gt;
The code overall is fairly well structured (if I do say so myself), although there
is one area where I did violate the separation of concerns rule, the TwitterManager
class knows more about Cruise Control.NET than it should. But, given that this is
a very simple internal project, and not for public consumption, I'm mostly OK with
that :)
&lt;/p&gt;
&lt;p&gt;
I've included the source to our entire build monitor, I hope you find it useful.&amp;nbsp;
We are using a very old version (1.1) of Cruise Control.NET in our environment. If
your using a more recent version, you will probably need to swap out the Cruise Control
CCTrayLib and Remote assemblies for something more recent, and invert some of the
commented out code in the SetupCruiseControl method of BuildServerMonitor.cs
&lt;/p&gt;
&lt;p&gt;
If you have any questions, or find/fix any bugs, please feel free to leave a comment,
or send me a tweet, my username on Twitter is &lt;a href="http://twitter.com/akcoder" target="_blank"&gt;akcoder&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://files.milkcarton.com/Afhcan.BuildMonitor.zip" target="_blank"&gt;Download
the Afhcan.BuildMonitor&lt;/a&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.milkcarton.com/blog/aggbug.ashx?id=0f2afcce-d7c6-4d53-93b7-c3d9bf87ab2a" /&gt;</description>
      <comments>http://www.milkcarton.com/blog/CommentView,guid,0f2afcce-d7c6-4d53-93b7-c3d9bf87ab2a.aspx</comments>
      <category>.NET</category>
      <category>C#</category>
      <category>HowTo</category>
    </item>
    <item>
      <trackback:ping>http://www.milkcarton.com/blog/Trackback.aspx?guid=8396d43e-d21d-44fd-b279-b9e8940a3304</trackback:ping>
      <pingback:server>http://www.milkcarton.com/blog/pingback.aspx</pingback:server>
      <pingback:target>http://www.milkcarton.com/blog/PermaLink,guid,8396d43e-d21d-44fd-b279-b9e8940a3304.aspx</pingback:target>
      <dc:creator>Dan Morphis</dc:creator>
      <wfw:comment>http://www.milkcarton.com/blog/CommentView,guid,8396d43e-d21d-44fd-b279-b9e8940a3304.aspx</wfw:comment>
      <wfw:commentRss>http://www.milkcarton.com/blog/SyndicationService.asmx/GetEntryCommentsRss?guid=8396d43e-d21d-44fd-b279-b9e8940a3304</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
One of our applications has a Windows service in it. To make debugging and running
this service easy,we have a winform in the service which can be activated by passing
a command line switch. Simple enough. Our service does all its logging with log4net.
I wanted to be able to put the output of the logging on our development form, but
how?
</p>
        <p>
After looking at various things, I realized what I needed to do was create a log4net
appender, and add the appender to the log4net logger and at regular intervals, grab
the contents of the logger.
</p>
        <h3>Solution
</h3>
        <p>
I created the below appender which uses a StringBuilder as the backing store. It takes
in one bool param in the contructor which allows you to specify if you want the log
to be built up in reverse.  This is useful if you want to display the most recent
event at the top.
</p>
        <div>
          <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none">
            <span style="color: #0000ff">public</span>
            <span style="color: #0000ff">class</span> StringBuilderAppender
: log4net.Appender.AppenderSkeleton { <span style="color: #0000ff">private</span> System.Text.StringBuilder
_builder = <span style="color: #0000ff">new</span> System.Text.StringBuilder(); <span style="color: #0000ff">private</span><span style="color: #0000ff">readonly</span><span style="color: #0000ff">bool</span> _invert; <span style="color: #0000ff">public</span> StringBuilderAppender(<span style="color: #0000ff">bool</span> invert)
{ _invert = invert; } <span style="color: #0000ff">public</span><span style="color: #0000ff">string</span> Text
{ get { <span style="color: #0000ff">return</span> _builder.ToString(); } } <span style="color: #0000ff">protected</span><span style="color: #0000ff">override</span><span style="color: #0000ff">void</span> Append(log4net.Core.LoggingEvent
loggingEvent) { var msg = loggingEvent.RenderedMessage; <span style="color: #0000ff">if</span> (_invert)
_builder = <span style="color: #0000ff">new</span> System.Text.StringBuilder().AppendLine(msg).Append(_builder); <span style="color: #0000ff">else</span> _builder.AppendLine(msg);
} }</pre>
        </div>
        <p>
Now we need to add our new appender to the logger. I found this helper method someone
wrote.
</p>
        <div>
          <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none">
            <span style="color: #0000ff">public</span>
            <span style="color: #0000ff">static</span>
            <span style="color: #0000ff">void</span> AddAppender(<span style="color: #0000ff">string</span> loggerName,
IAppender appender) { log4net.ILog log = log4net.LogManager.GetLogger(loggerName);
log4net.Repository.Hierarchy.Logger l = (log4net.Repository.Hierarchy.Logger)log.Logger;
l.AddAppender(appender); }</pre>
        </div>
        <p>
Finally, lets put it all together:
</p>
        <div>
          <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none">StringBuilderAppender appender = <span style="color: #0000ff">new</span> StringBuilderAppender(<span style="color: #0000ff">true</span>);
AddAppender(<span style="color: #006080">"MyLogger"</span>, appender); <span style="color: #0000ff">while</span>(<span style="color: #0000ff">true</span>)
{ System.Threading.Thread.Sleep(5000); someControl.Text = appender.Text; }</pre>
        </div>
        <img width="0" height="0" src="http://www.milkcarton.com/blog/aggbug.ashx?id=8396d43e-d21d-44fd-b279-b9e8940a3304" />
      </body>
      <title>Building a log4net appender</title>
      <guid isPermaLink="false">http://www.milkcarton.com/blog/PermaLink,guid,8396d43e-d21d-44fd-b279-b9e8940a3304.aspx</guid>
      <link>http://www.milkcarton.com/blog/2009/02/21/Building+A+Log4net+Appender.aspx</link>
      <pubDate>Sat, 21 Feb 2009 07:18:15 GMT</pubDate>
      <description>&lt;p&gt;
One of our applications has a Windows service in it. To make debugging and running
this service easy,we have a winform in the service which can be activated by passing
a command line switch. Simple enough. Our service does all its logging with log4net.
I wanted to be able to put the output of the logging on our development form, but
how?
&lt;/p&gt;
&lt;p&gt;
After looking at various things, I realized what I needed to do was create a log4net
appender, and add the appender to the log4net logger and at regular intervals, grab
the contents of the logger.
&lt;/p&gt;
&lt;h3&gt;Solution
&lt;/h3&gt;
&lt;p&gt;
I created the below appender which uses a StringBuilder as the backing store. It takes
in one bool param in the contructor which allows you to specify if you want the log
to be built up in reverse.&amp;nbsp; This is useful if you want to display the most recent
event at the top.
&lt;/p&gt;
&lt;div&gt;&lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; StringBuilderAppender
: log4net.Appender.AppenderSkeleton { &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; System.Text.StringBuilder
_builder = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; System.Text.StringBuilder(); &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;readonly&lt;/span&gt; &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; _invert; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; StringBuilderAppender(&lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; invert)
{ _invert = invert; } &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; Text
{ get { &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; _builder.ToString(); } } &lt;span style="color: #0000ff"&gt;protected&lt;/span&gt; &lt;span style="color: #0000ff"&gt;override&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Append(log4net.Core.LoggingEvent
loggingEvent) { var msg = loggingEvent.RenderedMessage; &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (_invert)
_builder = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; System.Text.StringBuilder().AppendLine(msg).Append(_builder); &lt;span style="color: #0000ff"&gt;else&lt;/span&gt; _builder.AppendLine(msg);
} }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
Now we need to add our new appender to the logger. I found this helper method someone
wrote.
&lt;/p&gt;
&lt;div&gt;&lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; AddAppender(&lt;span style="color: #0000ff"&gt;string&lt;/span&gt; loggerName,
IAppender appender) { log4net.ILog log = log4net.LogManager.GetLogger(loggerName);
log4net.Repository.Hierarchy.Logger l = (log4net.Repository.Hierarchy.Logger)log.Logger;
l.AddAppender(appender); }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
Finally, lets put it all together:
&lt;/p&gt;
&lt;div&gt;&lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;StringBuilderAppender appender = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; StringBuilderAppender(&lt;span style="color: #0000ff"&gt;true&lt;/span&gt;);
AddAppender(&lt;span style="color: #006080"&gt;"MyLogger"&lt;/span&gt;, appender); &lt;span style="color: #0000ff"&gt;while&lt;/span&gt;(&lt;span style="color: #0000ff"&gt;true&lt;/span&gt;)
{ System.Threading.Thread.Sleep(5000); someControl.Text = appender.Text; }&lt;/pre&gt;
&lt;/div&gt;
&lt;img width="0" height="0" src="http://www.milkcarton.com/blog/aggbug.ashx?id=8396d43e-d21d-44fd-b279-b9e8940a3304" /&gt;</description>
      <comments>http://www.milkcarton.com/blog/CommentView,guid,8396d43e-d21d-44fd-b279-b9e8940a3304.aspx</comments>
      <category>.NET</category>
      <category>HowTo</category>
      <category>Logging</category>
    </item>
    <item>
      <trackback:ping>http://www.milkcarton.com/blog/Trackback.aspx?guid=dc6fd10c-b67e-4771-a7dc-9ee2444d02e5</trackback:ping>
      <pingback:server>http://www.milkcarton.com/blog/pingback.aspx</pingback:server>
      <pingback:target>http://www.milkcarton.com/blog/PermaLink,guid,dc6fd10c-b67e-4771-a7dc-9ee2444d02e5.aspx</pingback:target>
      <dc:creator>Dan Morphis</dc:creator>
      <wfw:comment>http://www.milkcarton.com/blog/CommentView,guid,dc6fd10c-b67e-4771-a7dc-9ee2444d02e5.aspx</wfw:comment>
      <wfw:commentRss>http://www.milkcarton.com/blog/SyndicationService.asmx/GetEntryCommentsRss?guid=dc6fd10c-b67e-4771-a7dc-9ee2444d02e5</wfw:commentRss>
      <slash:comments>4</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
At our organization, we have to globalize our software. Making sure you've gotten
all the strings globalized can be a real pain. You have to create a new language resource
that looks nothing like your native language, then set the Thread.CurrentThread.CurrentUICulture
to the culture of the new language resource you created.  Such a pain.
</p>
        <p>
While pondering that this afternoon, I came upon something better. The .NET CultureManger
looks for the most specific resource file, then works its way back to the least specific.
For example, given the following resource files:
</p>
        <ul>
          <li>
i18n 
</li>
          <li>
i18n.en 
</li>
          <li>
i18n.en-US</li>
        </ul>
        <p>
If your current culture was en-GB, the CultureManger would use the resource file for
i18n.en, since there is no i18n.en-GB. But, if your current culture was da-DK, it
would use i18n.
</p>
        <p>
In our software, we have i18n, and i18n.da-DK resource files, plus i18n.fr-FR which
is a special, internal resource file. What's so special about the fr-FR resource file
you ask? The fr-FR resource file is really the i18n resource file which as been transformed
to replace all the localized text with dashes.
</p>
        <p>
Why did we do this? Because with all the English text replaced with dashes, it makes
it very easy to see which text in the application hasn't been globalized. The down
side to this, is we have to change the CurrentUICulture (and CurrentCulture) to fr-FR
in order to test this.
</p>
        <h3>Solution
</h3>
        <p>
The solution is actually quite simple, rename the fr-FR resource file to i18n.en-US
(or what ever the ISO code for your culture is).  Now when your testing, the
CultureManager will pick the most specific resource file, and use that. But, don't
forget to remove en-US folder from the final build folder before you deploy your application,
lest users get your debug language resource.
</p>
        <p>
        </p>
        <a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fwww.milkcarton.com%2fblog%2f2009%2f02%2f10%2fA%2bBetter%2bEasier%2bWay%2bTo%2bMake%2bSure%2bYouve%2bGlobalized%2bEverything.aspx">
          <img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.milkcarton.com%2fblog%2f2009%2f02%2f10%2fA%2bBetter%2bEasier%2bWay%2bTo%2bMake%2bSure%2bYouve%2bGlobalized%2bEverything.aspx" border="0" alt="kick it on DotNetKicks.com" />
        </a>
        <img width="0" height="0" src="http://www.milkcarton.com/blog/aggbug.ashx?id=dc6fd10c-b67e-4771-a7dc-9ee2444d02e5" />
      </body>
      <title>A better, easier way to make sure you've globalized everything</title>
      <guid isPermaLink="false">http://www.milkcarton.com/blog/PermaLink,guid,dc6fd10c-b67e-4771-a7dc-9ee2444d02e5.aspx</guid>
      <link>http://www.milkcarton.com/blog/2009/02/10/A+Better+Easier+Way+To+Make+Sure+Youve+Globalized+Everything.aspx</link>
      <pubDate>Tue, 10 Feb 2009 23:11:48 GMT</pubDate>
      <description>&lt;p&gt;
At our organization, we have to globalize our software. Making sure you've gotten
all the strings globalized can be a real pain. You have to create a new language resource
that looks nothing like your native language, then set the Thread.CurrentThread.CurrentUICulture
to the culture of the new language resource you created.&amp;nbsp; Such a pain.
&lt;/p&gt;
&lt;p&gt;
While pondering that this afternoon, I came upon something better. The .NET CultureManger
looks for the most specific resource file, then works its way back to the least specific.
For example, given the following resource files:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
i18n 
&lt;li&gt;
i18n.en 
&lt;li&gt;
i18n.en-US&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
If your current culture was en-GB, the CultureManger would use the resource file for
i18n.en, since there is no i18n.en-GB. But, if your current culture was da-DK, it
would use i18n.
&lt;/p&gt;
&lt;p&gt;
In our software, we have i18n, and i18n.da-DK resource files, plus i18n.fr-FR which
is a special, internal resource file. What's so special about the fr-FR resource file
you ask? The fr-FR resource file is really the i18n resource file which as been transformed
to replace all the localized text with dashes.
&lt;/p&gt;
&lt;p&gt;
Why did we do this? Because with all the English text replaced with dashes, it makes
it very easy to see which text in the application hasn't been globalized. The down
side to this, is we have to change the CurrentUICulture (and CurrentCulture) to fr-FR
in order to test this.
&lt;/p&gt;
&lt;h3&gt;Solution
&lt;/h3&gt;
&lt;p&gt;
The solution is actually quite simple, rename the fr-FR resource file to i18n.en-US
(or what ever the ISO code for your culture is).&amp;nbsp; Now when your testing, the
CultureManager will pick the most specific resource file, and use that. But, don't
forget to remove en-US folder from the final build folder before you deploy your application,
lest users get your debug language resource.
&lt;/p&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fwww.milkcarton.com%2fblog%2f2009%2f02%2f10%2fA%2bBetter%2bEasier%2bWay%2bTo%2bMake%2bSure%2bYouve%2bGlobalized%2bEverything.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.milkcarton.com%2fblog%2f2009%2f02%2f10%2fA%2bBetter%2bEasier%2bWay%2bTo%2bMake%2bSure%2bYouve%2bGlobalized%2bEverything.aspx" border="0" alt="kick it on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img width="0" height="0" src="http://www.milkcarton.com/blog/aggbug.ashx?id=dc6fd10c-b67e-4771-a7dc-9ee2444d02e5" /&gt;</description>
      <comments>http://www.milkcarton.com/blog/CommentView,guid,dc6fd10c-b67e-4771-a7dc-9ee2444d02e5.aspx</comments>
      <category>.NET</category>
      <category>Globalization</category>
      <category>HowTo</category>
    </item>
    <item>
      <trackback:ping>http://www.milkcarton.com/blog/Trackback.aspx?guid=74895ca2-de31-4c92-aa9c-d5da390a7700</trackback:ping>
      <pingback:server>http://www.milkcarton.com/blog/pingback.aspx</pingback:server>
      <pingback:target>http://www.milkcarton.com/blog/PermaLink,guid,74895ca2-de31-4c92-aa9c-d5da390a7700.aspx</pingback:target>
      <dc:creator>Dan Morphis</dc:creator>
      <wfw:comment>http://www.milkcarton.com/blog/CommentView,guid,74895ca2-de31-4c92-aa9c-d5da390a7700.aspx</wfw:comment>
      <wfw:commentRss>http://www.milkcarton.com/blog/SyndicationService.asmx/GetEntryCommentsRss?guid=74895ca2-de31-4c92-aa9c-d5da390a7700</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
As our production database gets more and more data in it, we noticed that things were
slowing down. I ran SQL Profiler trying to figure out if we needed to ad more indexes,
better arrange the data, or anything we could do to improve the performance. After
about an hour of running queries, creating indexes, profiling, and looking at execution
plans; I had gotten barely anything for performance gains.
</p>
        <p>
I then decided to take a different track and wondered if our indexes needed to be
rebuilt.  A quick Google later, and I came across an article on <a title="Tips for Rebuilding Indexes" href="http://www.sql-server-performance.com/tips/rebuilding_indexes_p1.aspx" target="_blank">Tips
for Rebuilding Indexes</a> over at <a href="http://www.sql-server-performance.com/" target="_blank">SQL
Server Performance</a>.
</p>
        <p>
The gist of the article is your indexes get fragmented and need to be rebuilt. I ran
the script present in the article and noticed a substantial improvement in performance. 
I failed to capture metrics to quantify the performance improvements, but the users
definitely noticed.
</p>
        <p>
Without further ado, here is the script from the article:
</p>
        <p>
If your using MS SQL Server 2000:
</p>
        <div>
          <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none">
            <span style="color: #0000ff">USE</span> DatabaseName
--Enter the name <span style="color: #0000ff">of</span> the <span style="color: #0000ff">database</span> you
want <span style="color: #0000ff">to</span> reindex <span style="color: #0000ff">DECLARE</span> @TableName <span style="color: #0000ff">varchar</span>(255) <span style="color: #0000ff">DECLARE</span> TableCursor <span style="color: #0000ff">CURSOR</span><span style="color: #0000ff">FOR</span><span style="color: #0000ff">SELECT</span> table_name <span style="color: #0000ff">FROM</span> information_schema.tables <span style="color: #0000ff">WHERE</span> table_type
= <span style="color: #006080">'base table'</span><span style="color: #0000ff">OPEN</span> TableCursor <span style="color: #0000ff">FETCH</span><span style="color: #0000ff">NEXT</span><span style="color: #0000ff">FROM</span> TableCursor <span style="color: #0000ff">INTO</span> @TableName <span style="color: #0000ff">WHILE</span><span style="color: #cc6633">@@FETCH_STATUS</span> =
0 <span style="color: #0000ff">BEGIN</span><span style="color: #0000ff">DBCC</span> DBREINDEX(@TableName,<span style="color: #006080">'
'</span>,90) <span style="color: #0000ff">FETCH</span><span style="color: #0000ff">NEXT</span><span style="color: #0000ff">FROM</span> TableCursor <span style="color: #0000ff">INTO</span> @TableName <span style="color: #0000ff">END</span><span style="color: #0000ff">CLOSE</span> TableCursor <span style="color: #0000ff">DEALLOCATE</span> TableCursor</pre>
        </div>
        <div> 
</div>
        <div>If your using MS SQL Server 2005:
</div>
        <div>
          <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none">
            <span style="color: #0000ff">USE</span> DatabaseName
--Enter the name <span style="color: #0000ff">of</span> the <span style="color: #0000ff">database</span> you
want <span style="color: #0000ff">to</span> reindex <span style="color: #0000ff">DECLARE</span> @TableName <span style="color: #0000ff">varchar</span>(255) <span style="color: #0000ff">DECLARE</span> TableCursor <span style="color: #0000ff">CURSOR</span><span style="color: #0000ff">FOR</span><span style="color: #0000ff">SELECT</span> table_name <span style="color: #0000ff">FROM</span> information_schema.tables <span style="color: #0000ff">WHERE</span> table_type
= <span style="color: #006080">'base table'</span><span style="color: #0000ff">OPEN</span> TableCursor <span style="color: #0000ff">FETCH</span><span style="color: #0000ff">NEXT</span><span style="color: #0000ff">FROM</span> TableCursor <span style="color: #0000ff">INTO</span> @TableName <span style="color: #0000ff">WHILE</span><span style="color: #cc6633">@@FETCH_STATUS</span> =
0 <span style="color: #0000ff">BEGIN</span><span style="color: #0000ff">ALTER</span><span style="color: #0000ff">INDEX</span><span style="color: #0000ff">ON</span><span style="color: #0000ff">schema</span>.<span style="color: #0000ff">table</span> REBUILD/REORGANIZE <span style="color: #0000ff">FETCH</span><span style="color: #0000ff">NEXT</span><span style="color: #0000ff">FROM</span> TableCursor <span style="color: #0000ff">INTO</span> @TableName <span style="color: #0000ff">END</span><span style="color: #0000ff">CLOSE</span> TableCursor <span style="color: #0000ff">DEALLOCATE</span> TableCursor</pre>
        </div>
        <img width="0" height="0" src="http://www.milkcarton.com/blog/aggbug.ashx?id=74895ca2-de31-4c92-aa9c-d5da390a7700" />
      </body>
      <title>Have you re-indexed your database lately?</title>
      <guid isPermaLink="false">http://www.milkcarton.com/blog/PermaLink,guid,74895ca2-de31-4c92-aa9c-d5da390a7700.aspx</guid>
      <link>http://www.milkcarton.com/blog/2009/01/29/Have+You+Reindexed+Your+Database+Lately.aspx</link>
      <pubDate>Thu, 29 Jan 2009 19:04:54 GMT</pubDate>
      <description>&lt;p&gt;
As our production database gets more and more data in it, we noticed that things were
slowing down. I ran SQL Profiler trying to figure out if we needed to ad more indexes,
better arrange the data, or anything we could do to improve the performance. After
about an hour of running queries, creating indexes, profiling, and looking at execution
plans; I had gotten barely anything for performance gains.
&lt;/p&gt;
&lt;p&gt;
I then decided to take a different track and wondered if our indexes needed to be
rebuilt.&amp;nbsp; A quick Google later, and I came across an article on &lt;a title="Tips for Rebuilding Indexes" href="http://www.sql-server-performance.com/tips/rebuilding_indexes_p1.aspx" target="_blank"&gt;Tips
for Rebuilding Indexes&lt;/a&gt; over at &lt;a href="http://www.sql-server-performance.com/" target="_blank"&gt;SQL
Server Performance&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
The gist of the article is your indexes get fragmented and need to be rebuilt. I ran
the script present in the article and noticed a substantial improvement in performance.&amp;nbsp;
I failed to capture metrics to quantify the performance improvements, but the users
definitely noticed.
&lt;/p&gt;
&lt;p&gt;
Without further ado, here is the script from the article:
&lt;/p&gt;
&lt;p&gt;
If your using MS SQL Server 2000:
&lt;/p&gt;
&lt;div&gt;&lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;USE&lt;/span&gt; DatabaseName
--Enter the name &lt;span style="color: #0000ff"&gt;of&lt;/span&gt; the &lt;span style="color: #0000ff"&gt;database&lt;/span&gt; you
want &lt;span style="color: #0000ff"&gt;to&lt;/span&gt; reindex &lt;span style="color: #0000ff"&gt;DECLARE&lt;/span&gt; @TableName &lt;span style="color: #0000ff"&gt;varchar&lt;/span&gt;(255) &lt;span style="color: #0000ff"&gt;DECLARE&lt;/span&gt; TableCursor &lt;span style="color: #0000ff"&gt;CURSOR&lt;/span&gt; &lt;span style="color: #0000ff"&gt;FOR&lt;/span&gt; &lt;span style="color: #0000ff"&gt;SELECT&lt;/span&gt; table_name &lt;span style="color: #0000ff"&gt;FROM&lt;/span&gt; information_schema.tables &lt;span style="color: #0000ff"&gt;WHERE&lt;/span&gt; table_type
= &lt;span style="color: #006080"&gt;'base table'&lt;/span&gt; &lt;span style="color: #0000ff"&gt;OPEN&lt;/span&gt; TableCursor &lt;span style="color: #0000ff"&gt;FETCH&lt;/span&gt; &lt;span style="color: #0000ff"&gt;NEXT&lt;/span&gt; &lt;span style="color: #0000ff"&gt;FROM&lt;/span&gt; TableCursor &lt;span style="color: #0000ff"&gt;INTO&lt;/span&gt; @TableName &lt;span style="color: #0000ff"&gt;WHILE&lt;/span&gt; &lt;span style="color: #cc6633"&gt;@@FETCH_STATUS&lt;/span&gt; =
0 &lt;span style="color: #0000ff"&gt;BEGIN&lt;/span&gt; &lt;span style="color: #0000ff"&gt;DBCC&lt;/span&gt; DBREINDEX(@TableName,&lt;span style="color: #006080"&gt;'
'&lt;/span&gt;,90) &lt;span style="color: #0000ff"&gt;FETCH&lt;/span&gt; &lt;span style="color: #0000ff"&gt;NEXT&lt;/span&gt; &lt;span style="color: #0000ff"&gt;FROM&lt;/span&gt; TableCursor &lt;span style="color: #0000ff"&gt;INTO&lt;/span&gt; @TableName &lt;span style="color: #0000ff"&gt;END&lt;/span&gt; &lt;span style="color: #0000ff"&gt;CLOSE&lt;/span&gt; TableCursor &lt;span style="color: #0000ff"&gt;DEALLOCATE&lt;/span&gt; TableCursor&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;
&lt;/div&gt;
&lt;div&gt;If your using MS SQL Server 2005:
&lt;/div&gt;
&lt;div&gt;&lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;USE&lt;/span&gt; DatabaseName
--Enter the name &lt;span style="color: #0000ff"&gt;of&lt;/span&gt; the &lt;span style="color: #0000ff"&gt;database&lt;/span&gt; you
want &lt;span style="color: #0000ff"&gt;to&lt;/span&gt; reindex &lt;span style="color: #0000ff"&gt;DECLARE&lt;/span&gt; @TableName &lt;span style="color: #0000ff"&gt;varchar&lt;/span&gt;(255) &lt;span style="color: #0000ff"&gt;DECLARE&lt;/span&gt; TableCursor &lt;span style="color: #0000ff"&gt;CURSOR&lt;/span&gt; &lt;span style="color: #0000ff"&gt;FOR&lt;/span&gt; &lt;span style="color: #0000ff"&gt;SELECT&lt;/span&gt; table_name &lt;span style="color: #0000ff"&gt;FROM&lt;/span&gt; information_schema.tables &lt;span style="color: #0000ff"&gt;WHERE&lt;/span&gt; table_type
= &lt;span style="color: #006080"&gt;'base table'&lt;/span&gt; &lt;span style="color: #0000ff"&gt;OPEN&lt;/span&gt; TableCursor &lt;span style="color: #0000ff"&gt;FETCH&lt;/span&gt; &lt;span style="color: #0000ff"&gt;NEXT&lt;/span&gt; &lt;span style="color: #0000ff"&gt;FROM&lt;/span&gt; TableCursor &lt;span style="color: #0000ff"&gt;INTO&lt;/span&gt; @TableName &lt;span style="color: #0000ff"&gt;WHILE&lt;/span&gt; &lt;span style="color: #cc6633"&gt;@@FETCH_STATUS&lt;/span&gt; =
0 &lt;span style="color: #0000ff"&gt;BEGIN&lt;/span&gt; &lt;span style="color: #0000ff"&gt;ALTER&lt;/span&gt; &lt;span style="color: #0000ff"&gt;INDEX&lt;/span&gt; &lt;span style="color: #0000ff"&gt;ON&lt;/span&gt; &lt;span style="color: #0000ff"&gt;schema&lt;/span&gt;.&lt;span style="color: #0000ff"&gt;table&lt;/span&gt; REBUILD/REORGANIZE &lt;span style="color: #0000ff"&gt;FETCH&lt;/span&gt; &lt;span style="color: #0000ff"&gt;NEXT&lt;/span&gt; &lt;span style="color: #0000ff"&gt;FROM&lt;/span&gt; TableCursor &lt;span style="color: #0000ff"&gt;INTO&lt;/span&gt; @TableName &lt;span style="color: #0000ff"&gt;END&lt;/span&gt; &lt;span style="color: #0000ff"&gt;CLOSE&lt;/span&gt; TableCursor &lt;span style="color: #0000ff"&gt;DEALLOCATE&lt;/span&gt; TableCursor&lt;/pre&gt;
&lt;/div&gt;
&lt;img width="0" height="0" src="http://www.milkcarton.com/blog/aggbug.ashx?id=74895ca2-de31-4c92-aa9c-d5da390a7700" /&gt;</description>
      <comments>http://www.milkcarton.com/blog/CommentView,guid,74895ca2-de31-4c92-aa9c-d5da390a7700.aspx</comments>
      <category>HowTo</category>
      <category>SQL</category>
    </item>
    <item>
      <trackback:ping>http://www.milkcarton.com/blog/Trackback.aspx?guid=11135e07-6332-4c64-a883-61810d48b80c</trackback:ping>
      <pingback:server>http://www.milkcarton.com/blog/pingback.aspx</pingback:server>
      <pingback:target>http://www.milkcarton.com/blog/PermaLink,guid,11135e07-6332-4c64-a883-61810d48b80c.aspx</pingback:target>
      <dc:creator>Dan Morphis</dc:creator>
      <wfw:comment>http://www.milkcarton.com/blog/CommentView,guid,11135e07-6332-4c64-a883-61810d48b80c.aspx</wfw:comment>
      <wfw:commentRss>http://www.milkcarton.com/blog/SyndicationService.asmx/GetEntryCommentsRss?guid=11135e07-6332-4c64-a883-61810d48b80c</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <h3>Background
</h3>
        <p>
Call me a control freak, but I like to see all the shared volumes on my Mac. I could
open Terminal and cd to the Volumes folder, or I could use Finder and Go -&gt; Go
to Folder to see everything that OS X has mounted in my Volumes folder. But thats
kind of a pain.
</p>
        <h3>Solution
</h3>
        <p>
After a little bit of digging around, I found out about SetFile. SetFile is a command
line utility that allows you to set the file attributes on files in an HFS+ directory.
After figuring out the parameters for it, I came up with this little ditty to make
the Volumes folder show up under "Macintosh HD." Run this in Terminal:
</p>
        <blockquote>sudo SetFile -a v /Volumes</blockquote>
        <p>
With this command, you are setting the visibility attribute on the Volumes folder
to visible. To reverse the process, change -a v to -a <strong>V</strong>. Now open
up "Macintosh HD" and you should now see all the volumes mounted on your Mac!
</p>
        <img src="http://www.milkcarton.com/blog/content/binary/VolumesScreenShot.png" height="111" width="192" border="1" hspace="4" vspace="4" alt="Screenshot" title="Screenshot" />
        <img width="0" height="0" src="http://www.milkcarton.com/blog/aggbug.ashx?id=11135e07-6332-4c64-a883-61810d48b80c" />
      </body>
      <title>How to make the OS X Volumes folder visible</title>
      <guid isPermaLink="false">http://www.milkcarton.com/blog/PermaLink,guid,11135e07-6332-4c64-a883-61810d48b80c.aspx</guid>
      <link>http://www.milkcarton.com/blog/2009/01/25/How+To+Make+The+OS+X+Volumes+Folder+Visible.aspx</link>
      <pubDate>Sun, 25 Jan 2009 05:59:00 GMT</pubDate>
      <description>&lt;h3&gt;Background
&lt;/h3&gt;
&lt;p&gt;
Call me a control freak, but I like to see all the shared volumes on my Mac. I could
open Terminal and cd to the Volumes folder, or I could use Finder and Go -&amp;gt; Go
to Folder to see everything that OS X has mounted in my Volumes folder. But thats
kind of a pain.
&lt;/p&gt;
&lt;h3&gt;Solution
&lt;/h3&gt;
&lt;p&gt;
After a little bit of digging around, I found out about SetFile. SetFile is a command
line utility that allows you to set the file attributes on files in an HFS+ directory.
After figuring out the parameters for it, I came up with this little ditty to make
the Volumes folder show up under "Macintosh HD." Run this in Terminal:
&lt;/p&gt;
&lt;blockquote&gt;sudo SetFile -a v /Volumes&lt;/blockquote&gt; 
&lt;p&gt;
With this command, you are setting the visibility attribute on the Volumes folder
to visible. To reverse the process, change -a v to -a &lt;strong&gt;V&lt;/strong&gt;. Now open
up "Macintosh HD" and you should now see all the volumes mounted on your Mac!
&lt;/p&gt;
&lt;img src="http://www.milkcarton.com/blog/content/binary/VolumesScreenShot.png" height="111" width="192" border="1" hspace="4" vspace="4" alt="Screenshot" title="Screenshot" /&gt; &lt;img width="0" height="0" src="http://www.milkcarton.com/blog/aggbug.ashx?id=11135e07-6332-4c64-a883-61810d48b80c" /&gt;</description>
      <comments>http://www.milkcarton.com/blog/CommentView,guid,11135e07-6332-4c64-a883-61810d48b80c.aspx</comments>
      <category>HowTo</category>
      <category>Mac OS X</category>
    </item>
    <item>
      <trackback:ping>http://www.milkcarton.com/blog/Trackback.aspx?guid=9ca26879-f4aa-4bd9-ae5a-c083bb91cf3b</trackback:ping>
      <pingback:server>http://www.milkcarton.com/blog/pingback.aspx</pingback:server>
      <pingback:target>http://www.milkcarton.com/blog/PermaLink,guid,9ca26879-f4aa-4bd9-ae5a-c083bb91cf3b.aspx</pingback:target>
      <dc:creator>Dan Morphis</dc:creator>
      <wfw:comment>http://www.milkcarton.com/blog/CommentView,guid,9ca26879-f4aa-4bd9-ae5a-c083bb91cf3b.aspx</wfw:comment>
      <wfw:commentRss>http://www.milkcarton.com/blog/SyndicationService.asmx/GetEntryCommentsRss?guid=9ca26879-f4aa-4bd9-ae5a-c083bb91cf3b</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <h3>Background
</h3>
        <p>
When I originally got Mac Mini, Mac OS X 10.5.1 was out. I did a quick google search
and found simple steps to follow to get Time Machine to backup to a network volume
on my Linux server. Everything worked great!
</p>
        <p>
When Mac OS X 10.5.2 was released a few weeks later, Time Machine would no longer
backup to my file server. I did a lot of googling and found I wasn't the only one
with the problem, but couldn't find any solutions. I've given it the old collage try
a few times since then trying to get it working again, as recently as mid December
2008, but to no avail.
</p>
        <p>
I don't know what possessed me to try and get it working again this time, but I did.
And I won! It wasn't an easy battle, nor was it an epic battle. But it was a battle
none the less.
</p>
        <h3>Problem
</h3>
        <p>
I found the various postings on the net about how to everything working, including <a href="http://hupio.wordpress.com/2008/04/27/osx-timemachine-and-sambawindows-share/">Hupio's
OSX Timemachine and Samba/Windows share</a>. But nothing really worked. I kept getting
the error message "the backup disk image could not be mounted."
</p>
        <p>
I almost gave up again, but decided to google the error message. And came across a
few more sites, but they didn't have anything of interest. I don't know why, but I
tried to create my sparse bundle on the network share itself, instead of on the Mini
and moving it to the network share. That got me the error message "hdiutil: create
failed - Operation not supported".
</p>
        <p>
Googling that error message led me to <a href="http://www.viraj.org/b2evolution/blogs/index.php/2008/05/26/">Viraj's
post about Time machine + AFP + Ubuntu - Samba.</a> Viraj got everything working by
installing the AFP service on his Linux (Ubuntu) server. He linked to <a href="http://blog.damontimm.com/how-to-install-netatalk-afp-on-ubuntu-with-encrypted-authentication/">How
to: Install Netatalk (AFP) on Ubuntu with Encrypted Authentication</a> which was perfect
because I happen to be running an Ubuntu server.
</p>
        <h3>Solution
</h3>
        <p>
If your using a Linux file server like I am, and want to backup your Mac using Time
Machine to your file server, follow these steps:
</p>
        <p>
1. <a href="http://blog.damontimm.com/how-to-install-netatalk-afp-on-ubuntu-with-encrypted-authentication/">Install
AFP on your Linux server</a></p>
        <p>
2. Figure out where you are going to store the backups on your file server. I stored
mine in /media/backup/TimeMachine. You will need to edit your /etc/netatalk/AppleVolumes.default
file and point it to the directory: 
</p>
        <blockquote> sudo echo "/media/backup/TimeMachine \"Time Machines\"" &gt;&gt;
/etc/netatalk/AppleVolumes.default </blockquote>
        <p>
3. Restart netatalk 
</p>
        <blockquote> sudo /etc/init.d/netatalk restart </blockquote>
        <p>
4. Mount your "Time Machines" volume. Finder -&gt; Go -&gt; Connect to Server and enter
afp://IPADDRESS/Time Machines
</p>
        <p>
5. <a href="http://hupio.wordpress.com/2008/04/27/osx-timemachine-and-sambawindows-share/">Create
a sparse bundle</a>. If your OS volume is case-sensitive like mine, run this in terminal: 
</p>
        <blockquote> hdiutil create -library SPUD -size 50g -fs "Case-sensitive Journaled
HFS+" -type SPARSEBUNDLE -volname "TimeMachine for YOURNAME" "YOURMACSNAME_MACADDRESS.sparsebundle" </blockquote> this
will create a 50 GB sparse bundle for Time Machine. If your OS volume is <strong>not</strong> case-sensitive
(the default) use this command: <blockquote> hdiutil create -library SPUD -size 50g
-fs "Journaled HFS+" -type SPARSEBUNDLE -volname "TimeMachine for YOURNAME" "YOURMACSNAME_MACADDRESS.sparsebundle" </blockquote><p>
I'm not going to go into the details about the command line, the link above goes into
greater detail. You will need to read the article so you can plug the correct values
in.
</p><p>
6. Move your newly created sparsebundle to your "Time Machines" share: 
</p><blockquote> mv mini_MACADDRESS.sparsebundle /Volumes/Time\ Machines/ </blockquote><p>
7. Configure your Mac to allow backing up to a network share: 
</p><blockquote> defaults write com.apple.systempreferences TMShowUnsupportedNetworkVolumes
1 </blockquote><p>
8. Finally, open Time Machine, click "Change Disk" and point to your "Time Machines"
volume. In 2 minutes, Time Machine will start to backup your data to your Linux network
file system!
</p><p>
A quick note about the conventions used above 
</p><blockquote>all commands blockquote are supposed to be run in Terminal. All commands
that start with a sudo (items 2 and 3) are supposed to be run on your Linux server </blockquote><img width="0" height="0" src="http://www.milkcarton.com/blog/aggbug.ashx?id=9ca26879-f4aa-4bd9-ae5a-c083bb91cf3b" /></body>
      <title>Getting Time Machine to backup to a network volume</title>
      <guid isPermaLink="false">http://www.milkcarton.com/blog/PermaLink,guid,9ca26879-f4aa-4bd9-ae5a-c083bb91cf3b.aspx</guid>
      <link>http://www.milkcarton.com/blog/2009/01/24/Getting+Time+Machine+To+Backup+To+A+Network+Volume.aspx</link>
      <pubDate>Sat, 24 Jan 2009 07:42:58 GMT</pubDate>
      <description>&lt;h3&gt;Background
&lt;/h3&gt;
&lt;p&gt;
When I originally got Mac Mini, Mac OS X 10.5.1 was out. I did a quick google search
and found simple steps to follow to get Time Machine to backup to a network volume
on my Linux server. Everything worked great!
&lt;/p&gt;
&lt;p&gt;
When Mac OS X 10.5.2 was released a few weeks later, Time Machine would no longer
backup to my file server. I did a lot of googling and found I wasn't the only one
with the problem, but couldn't find any solutions. I've given it the old collage try
a few times since then trying to get it working again, as recently as mid December
2008, but to no avail.
&lt;/p&gt;
&lt;p&gt;
I don't know what possessed me to try and get it working again this time, but I did.
And I won! It wasn't an easy battle, nor was it an epic battle. But it was a battle
none the less.
&lt;/p&gt;
&lt;h3&gt;Problem
&lt;/h3&gt;
&lt;p&gt;
I found the various postings on the net about how to everything working, including &lt;a href="http://hupio.wordpress.com/2008/04/27/osx-timemachine-and-sambawindows-share/"&gt;Hupio's
OSX Timemachine and Samba/Windows share&lt;/a&gt;. But nothing really worked. I kept getting
the error message "the backup disk image could not be mounted."
&lt;/p&gt;
&lt;p&gt;
I almost gave up again, but decided to google the error message. And came across a
few more sites, but they didn't have anything of interest. I don't know why, but I
tried to create my sparse bundle on the network share itself, instead of on the Mini
and moving it to the network share. That got me the error message "hdiutil: create
failed - Operation not supported".
&lt;/p&gt;
&lt;p&gt;
Googling that error message led me to &lt;a href="http://www.viraj.org/b2evolution/blogs/index.php/2008/05/26/"&gt;Viraj's
post about Time machine + AFP + Ubuntu - Samba.&lt;/a&gt; Viraj got everything working by
installing the AFP service on his Linux (Ubuntu) server. He linked to &lt;a href="http://blog.damontimm.com/how-to-install-netatalk-afp-on-ubuntu-with-encrypted-authentication/"&gt;How
to: Install Netatalk (AFP) on Ubuntu with Encrypted Authentication&lt;/a&gt; which was perfect
because I happen to be running an Ubuntu server.
&lt;/p&gt;
&lt;h3&gt;Solution
&lt;/h3&gt;
&lt;p&gt;
If your using a Linux file server like I am, and want to backup your Mac using Time
Machine to your file server, follow these steps:
&lt;/p&gt;
&lt;p&gt;
1. &lt;a href="http://blog.damontimm.com/how-to-install-netatalk-afp-on-ubuntu-with-encrypted-authentication/"&gt;Install
AFP on your Linux server&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
2. Figure out where you are going to store the backups on your file server. I stored
mine in /media/backup/TimeMachine. You will need to edit your /etc/netatalk/AppleVolumes.default
file and point it to the directory: &lt;blockquote&gt; sudo echo "/media/backup/TimeMachine
\"Time Machines\"" &amp;gt;&amp;gt; /etc/netatalk/AppleVolumes.default &lt;/blockquote&gt;&gt;
&lt;p&gt;
3. Restart netatalk &lt;blockquote&gt; sudo /etc/init.d/netatalk restart &lt;/blockquote&gt;&gt;
&lt;p&gt;
4. Mount your "Time Machines" volume. Finder -&gt; Go -&gt; Connect to Server and enter
afp://IPADDRESS/Time Machines
&lt;/p&gt;
&lt;p&gt;
5. &lt;a href="http://hupio.wordpress.com/2008/04/27/osx-timemachine-and-sambawindows-share/"&gt;Create
a sparse bundle&lt;/a&gt;. If your OS volume is case-sensitive like mine, run this in terminal: &lt;blockquote&gt; hdiutil
create -library SPUD -size 50g -fs "Case-sensitive Journaled HFS+" -type SPARSEBUNDLE
-volname "TimeMachine for YOURNAME" "YOURMACSNAME_MACADDRESS.sparsebundle" &lt;/blockquote&gt; this
will create a 50 GB sparse bundle for Time Machine. If your OS volume is &lt;strong&gt;not&lt;/strong&gt; case-sensitive
(the default) use this command: &lt;blockquote&gt; hdiutil create -library SPUD -size 50g
-fs "Journaled HFS+" -type SPARSEBUNDLE -volname "TimeMachine for YOURNAME" "YOURMACSNAME_MACADDRESS.sparsebundle" &lt;/blockquote&gt;&gt;
&lt;p&gt;
I'm not going to go into the details about the command line, the link above goes into
greater detail. You will need to read the article so you can plug the correct values
in.
&lt;/p&gt;
&lt;p&gt;
6. Move your newly created sparsebundle to your "Time Machines" share: &lt;blockquote&gt; mv
mini_MACADDRESS.sparsebundle /Volumes/Time\ Machines/ &lt;/blockquote&gt;&gt;
&lt;p&gt;
7. Configure your Mac to allow backing up to a network share: &lt;blockquote&gt; defaults
write com.apple.systempreferences TMShowUnsupportedNetworkVolumes 1 &lt;/blockquote&gt;&gt;
&lt;p&gt;
8. Finally, open Time Machine, click "Change Disk" and point to your "Time Machines"
volume. In 2 minutes, Time Machine will start to backup your data to your Linux network
file system!
&lt;/p&gt;
&lt;p&gt;
A quick note about the conventions used above &lt;blockquote&gt;all commands blockquote
are supposed to be run in Terminal. All commands that start with a sudo (items 2 and
3) are supposed to be run on your Linux server &lt;/blockquote&gt;&gt;
&lt;img width="0" height="0" src="http://www.milkcarton.com/blog/aggbug.ashx?id=9ca26879-f4aa-4bd9-ae5a-c083bb91cf3b" /&gt;</description>
      <comments>http://www.milkcarton.com/blog/CommentView,guid,9ca26879-f4aa-4bd9-ae5a-c083bb91cf3b.aspx</comments>
      <category>HowTo</category>
      <category>Mac OS X</category>
    </item>
    <item>
      <trackback:ping>http://www.milkcarton.com/blog/Trackback.aspx?guid=2082acf3-3fe7-44c9-aa47-1b075c19a82b</trackback:ping>
      <pingback:server>http://www.milkcarton.com/blog/pingback.aspx</pingback:server>
      <pingback:target>http://www.milkcarton.com/blog/PermaLink,guid,2082acf3-3fe7-44c9-aa47-1b075c19a82b.aspx</pingback:target>
      <dc:creator>Dan Morphis</dc:creator>
      <wfw:comment>http://www.milkcarton.com/blog/CommentView,guid,2082acf3-3fe7-44c9-aa47-1b075c19a82b.aspx</wfw:comment>
      <wfw:commentRss>http://www.milkcarton.com/blog/SyndicationService.asmx/GetEntryCommentsRss?guid=2082acf3-3fe7-44c9-aa47-1b075c19a82b</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
After much time, trial and error, I was finally able to get Visual Studio's remote
debugging features to work.  In my travels around the Internet, no one seems
to have compiled all the steps to make the process work successfully and seemlessly
into one page, this is my attempt.
</p>
        <h3>Setting up
</h3>
        <p>
Complete all the steps listed on the <a href="http://msdn.microsoft.com/en-us/library/bt727f1t.aspx">How
to: Set Up Remote Debugging</a> article on MSDN. 
</p>
        <h3>Permissions
</h3>
        <p>
Don't try and fight the cross-domain permissions battle, its just not worth it. 
If the machine your trying debug is not on a domain, then don't run VS from a machine
thats on the domain. Make sure the same user with the same password exists on<strong> both</strong> machines.
</p>
        <h3>PDB's
</h3>
        <p>
Put the PDB files on the remote machine (target) in the same folder as your app.
</p>
        <h3>Source Code
</h3>
        <p>
In our environment, our build server produces an installer, and it also produces the
PDB's we used in the above step. If we want Visual Studio to automatically pick up
the right source files when we are debugging, you need to store the source code on
your host machine in the same location as the machine that built your PDB's.
</p>
        <p>
For example, in our world, on our build server the code lives on d:\code\projectname\code\
so on your host machine, you would store our source code on d:\code\projectname\code
</p>
        <h3>Results
</h3>
        <p>
If you have followed all these steps, and if the stars align just right, you should
now be able to step through your source code when remote debugging.
</p>
        <h3>Questions/Comments
</h3>
        <p>
Questions, comments or just can't make it work for some reason? Leave a comment and
fill out your actual email address nd I'll try to address it!
</p>
        <p>
 
</p>
        <p>
          <a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fwww.milkcarton.com%2fblog%2f2009%2f01%2f06%2fHow%2bTo%2bGet%2bRemote%2bDebugging%2bTo%2bWork%2bSuccessfully.aspx">
            <img alt="kick it on DotNetKicks.com" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.milkcarton.com%2fblog%2f2009%2f01%2f06%2fHow%2bTo%2bGet%2bRemote%2bDebugging%2bTo%2bWork%2bSuccessfully.aspx" border="0" />
          </a>
        </p>
        <img width="0" height="0" src="http://www.milkcarton.com/blog/aggbug.ashx?id=2082acf3-3fe7-44c9-aa47-1b075c19a82b" />
      </body>
      <title>How to get remote debugging to work successfully</title>
      <guid isPermaLink="false">http://www.milkcarton.com/blog/PermaLink,guid,2082acf3-3fe7-44c9-aa47-1b075c19a82b.aspx</guid>
      <link>http://www.milkcarton.com/blog/2009/01/06/How+To+Get+Remote+Debugging+To+Work+Successfully.aspx</link>
      <pubDate>Tue, 06 Jan 2009 22:20:47 GMT</pubDate>
      <description>&lt;p&gt;
After much time, trial and error, I was finally able to get Visual Studio's remote
debugging features to work.&amp;nbsp; In my travels around the Internet, no one seems
to have compiled all the steps to make the process work successfully and seemlessly
into one page, this is my attempt.
&lt;/p&gt;
&lt;h3&gt;Setting up
&lt;/h3&gt;
&lt;p&gt;
Complete all the steps listed on the &lt;a href="http://msdn.microsoft.com/en-us/library/bt727f1t.aspx"&gt;How
to: Set Up Remote Debugging&lt;/a&gt; article on MSDN. 
&lt;/p&gt;
&lt;h3&gt;Permissions
&lt;/h3&gt;
&lt;p&gt;
Don't try and fight the cross-domain permissions battle, its just not worth it.&amp;nbsp;
If the machine your trying debug is not on a domain, then don't run VS from a machine
thats on the domain. Make sure the same user with the same password exists on&lt;strong&gt; both&lt;/strong&gt; machines.
&lt;/p&gt;
&lt;h3&gt;PDB's
&lt;/h3&gt;
&lt;p&gt;
Put the PDB files on the remote machine (target) in the same folder as your app.
&lt;/p&gt;
&lt;h3&gt;Source Code
&lt;/h3&gt;
&lt;p&gt;
In our environment, our build server produces an installer, and it also produces the
PDB's we used in the above step. If we want Visual Studio to automatically pick up
the right source files when we are debugging, you need to store the source code on
your host machine in the same location as the machine that built your PDB's.
&lt;/p&gt;
&lt;p&gt;
For example, in our world, on our build server the code lives on d:\code\projectname\code\
so on your host machine, you would store our source code on d:\code\projectname\code
&lt;/p&gt;
&lt;h3&gt;Results
&lt;/h3&gt;
&lt;p&gt;
If you have followed all these steps, and if the stars align just right, you should
now be able to step through your source code when remote debugging.
&lt;/p&gt;
&lt;h3&gt;Questions/Comments
&lt;/h3&gt;
&lt;p&gt;
Questions, comments or just can't make it work for some reason? Leave a comment and
fill out your actual email address nd I'll try to address it!
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fwww.milkcarton.com%2fblog%2f2009%2f01%2f06%2fHow%2bTo%2bGet%2bRemote%2bDebugging%2bTo%2bWork%2bSuccessfully.aspx"&gt;&lt;img alt="kick it on DotNetKicks.com" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.milkcarton.com%2fblog%2f2009%2f01%2f06%2fHow%2bTo%2bGet%2bRemote%2bDebugging%2bTo%2bWork%2bSuccessfully.aspx" border="0"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.milkcarton.com/blog/aggbug.ashx?id=2082acf3-3fe7-44c9-aa47-1b075c19a82b" /&gt;</description>
      <comments>http://www.milkcarton.com/blog/CommentView,guid,2082acf3-3fe7-44c9-aa47-1b075c19a82b.aspx</comments>
      <category>Debugging</category>
      <category>HowTo</category>
      <category>VisualStudio</category>
    </item>
  </channel>
</rss>